1
0
mirror of https://gitlab.com/comunic/comunicapiv2 synced 2024-11-22 13:29:22 +00:00

Start implementation of post system

This commit is contained in:
Pierre HUBERT 2020-01-03 10:17:34 +01:00
parent 899ec40bd7
commit 87daf8acbe
4 changed files with 387 additions and 0 deletions

View File

@ -0,0 +1,68 @@
import { RequestHandler } from "../entities/RequestHandler";
import { UserHelper } from "../helpers/UserHelper";
import { PostsHelper } from "../helpers/PostsHelper";
import { Post, PostVisibilityLevel } from "../entities/Post";
/**
* Posts controller
*
* @author Pierre HUBERT
*/
const VISIBILITY_LEVELS_API = {};
VISIBILITY_LEVELS_API[PostVisibilityLevel.VISIBILITY_PUBLIC] = "public";
VISIBILITY_LEVELS_API[PostVisibilityLevel.VISIBILITY_FRIENDS] = "friends";
VISIBILITY_LEVELS_API[PostVisibilityLevel.VISIBILITY_USER] = "private";
VISIBILITY_LEVELS_API[PostVisibilityLevel.VISIBILITY_GROUP_MEMBERS] = "members";
export class PostsController {
/**
* Get the posts of a user
*
* @param h Request handler
*/
public static async GetListUser(h: RequestHandler) {
const userID = await h.postUserId("userID");
const startFrom = h.postInt("startFrom", 0);
if(!await UserHelper.CanSeeUserPage(h.optionnalUserID, userID))
h.error(401, "You are not allowed to access this user posts !");
const posts = await PostsHelper.GetUserPosts(h.optionnalUserID, userID, startFrom);
let list = [];
for (const p of posts) {
list.push(await this.PostToAPI(p));
}
h.send(list);
}
/**
* Turn a post object into an API entry
*
* @param p The post
*/
public static async PostToAPI(p: Post) : Promise<any> {
let data : any = {
ID: p.id,
useriD: p.userID,
user_page_id: p.userPageID,
group_id: p.groupID,
post_time: p.timeCreate,
content: p.hasContent ? p.content : null,
visibility_level: VISIBILITY_LEVELS_API[p.visibilityLevel],
kind: p.kind,
// File specific
file_size: !p.hasFile ? null : p.file.size,
file_type: !p.hasFile ? null : p.file.type,
file_path: !p.hasFile ? null : p.file.path,
file_url: !p.hasFile ? null : p.file.url
};
return data;
}
}

View File

@ -11,6 +11,7 @@ import { WebAppControllers } from "./WebAppController";
import { CallsController } from "./CallsController"; import { CallsController } from "./CallsController";
import { FriendsController } from "./FriendsController"; import { FriendsController } from "./FriendsController";
import { MoviesController } from "./MoviesController"; import { MoviesController } from "./MoviesController";
import { PostsController } from "./PostsController";
/** /**
* Controllers routes * Controllers routes
@ -182,6 +183,11 @@ export const Routes : Route[] = [
// Posts controller
{path: "/posts/get_user", cb: (h) => PostsController.GetListUser(h), needLogin: false},
// Notifications controller // Notifications controller
{path: "/notifications/count_unread", cb: (h) => NotificationsController.CountUnread(h)}, {path: "/notifications/count_unread", cb: (h) => NotificationsController.CountUnread(h)},

151
src/entities/Post.ts Normal file
View File

@ -0,0 +1,151 @@
import { pathUserData } from "../utils/UserDataUtils";
/**
* Post entity
*
* @author Pierre HUBERT
*/
export enum PostVisibilityLevel {
//Posts that can be seen by anyone
VISIBILITY_PUBLIC = 1,
//Posts that can be seen by the friends of the user
VISIBILITY_FRIENDS = 2,
//Posts that can be seen by the user only
VISIBILITY_USER = 3,
//Posts that can be seen by the members of a group (same as friends)
VISIBILITY_GROUP_MEMBERS = 50,
}
export enum PostAccessLevel {
//When a user can't access to a post
NO_ACCESS = 0,
//When a user can see a post and perform basic actions such as liking
BASIC_ACCESS = 1,
//When a user has intermediate access to the post (delete post)
INTERMEDIATE_ACCESS = 2,
//When a user has a full access to the post
FULL_ACCESS = 3,
}
export enum PostKind {
POST_KIND_TEXT = "text",
POST_KIND_IMAGE = "image",
POST_KIND_WEBLINK = "weblink",
POST_KIND_PDF = "pdf",
POST_KIND_MOVIE = "movie",
POST_KIND_COUNTDOWN = "countdown",
POST_KIND_SURVEY = "survey",
POST_KIND_YOUTUBE = "youtube",
}
export enum PostPageKind {
PAGE_KIND_USER = "user",
PAGE_KIND_GROUP = "group",
}
export interface PostFileBuilder {
path: string,
size: number,
type: string,
}
export class PostFile implements PostFileBuilder {
path: string;
size: number;
type: string;
public constructor(info: PostFileBuilder) {
for (const key in info) {
if (info.hasOwnProperty(key))
this[key] = info[key];
}
}
get url() : string {
return pathUserData(this.path)
}
}
export interface PostLinkBuilder {
url: string,
title: string,
description: string,
image: string
}
export class PostLink implements PostLinkBuilder {
url: string;
title: string;
description: string;
image: string;
public constructor(info: PostLinkBuilder) {
for (const key in info) {
if (info.hasOwnProperty(key))
this[key] = info[key];
}
}
}
export interface PostBuilder {
id: number,
userID: number,
timeCreate: number,
kindPage: PostPageKind,
pageID: number,
content: string,
visibilityLevel: PostVisibilityLevel,
kind: PostKind,
file ?: PostFile,
movieID ?: number,
timeEnd ?: number,
link ?: PostLink,
}
export class Post implements PostBuilder {
id: number;
userID: number;
timeCreate: number;
kindPage: PostPageKind;
pageID: number;
content: string;
visibilityLevel: PostVisibilityLevel;
kind: PostKind;
file?: PostFile;
movieID?: number;
timeEnd?: number;
link?: PostLink;
public constructor(info: PostBuilder) {
for (const key in info) {
if (info.hasOwnProperty(key))
this[key] = info[key];
}
}
get userPageID() : number {
return this.kindPage == PostPageKind.PAGE_KIND_USER ? this.pageID : 0
}
get groupID() : number {
return this.kindPage == PostPageKind.PAGE_KIND_GROUP ? this.pageID : 0
}
get hasContent() : boolean {
return this.content != null && this.content && this.content.length > 0;
}
get hasFile() : boolean {
return this.file != null && this.file != undefined;
}
}

162
src/helpers/PostsHelper.ts Normal file
View File

@ -0,0 +1,162 @@
import { PostKind, PostVisibilityLevel, Post, PostPageKind, PostFile, PostLink } from "../entities/Post";
import { FriendsHelper } from "./FriendsHelper";
import { DatabaseHelper } from "./DatabaseHelper";
/**
* Posts helper
*
* @author Pierre HUBERT
*/
/**
* Table name
*/
const TABLE_NAME = "texte";
/**
* Database mapping
*/
const PostDBTypes : Record<string, PostKind> = {
"texte": PostKind.POST_KIND_IMAGE,
"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<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));
}
/**
* 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
}),
});
}
}