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-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));
|
|
|
|
}
|
|
|
|
|
2020-01-04 11:04:14 +00:00
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2020-01-04 11:04:14 +00:00
|
|
|
/**
|
|
|
|
* 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-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
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|