Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added src/dummy.js
Empty file.
186 changes: 89 additions & 97 deletions src/posts/summary.js
Original file line number Diff line number Diff line change
@@ -1,107 +1,99 @@

'use strict';

var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const validator = require('validator');
const _ = require('lodash');

const topics = require('../topics');
const user = require('../user');
const plugins = require('../plugins');
const categories = require('../categories');
const utils = require('../utils');

module.exports = function (Posts) {
Posts.getPostSummaryByPids = async function (pids, uid, options) {
if (!Array.isArray(pids) || !pids.length) {
return [];
}

options.stripTags = options.hasOwnProperty('stripTags') ? options.stripTags : false;
options.parse = options.hasOwnProperty('parse') ? options.parse : true;
options.extraFields = options.hasOwnProperty('extraFields') ? options.extraFields : [];

const fields = ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted', 'upvotes', 'downvotes', 'replies', 'handle'].concat(options.extraFields);

let posts = await Posts.getPostsFields(pids, fields);
posts = posts.filter(Boolean);
posts = await user.blocks.filter(uid, posts);

const uids = _.uniq(posts.map(p => p && p.uid));
const tids = _.uniq(posts.map(p => p && p.tid));

const [users, topicsAndCategories] = await Promise.all([
user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status']),
getTopicAndCategories(tids),
]);

const uidToUser = toObject('uid', users);
const tidToTopic = toObject('tid', topicsAndCategories.topics);
const cidToCategory = toObject('cid', topicsAndCategories.categories);

posts.forEach((post) => {
// If the post author isn't represented in the retrieved users' data,
// then it means they were deleted, assume guest.
if (!uidToUser.hasOwnProperty(post.uid)) {
post.uid = 0;
}
post.user = uidToUser[post.uid];
Posts.overrideGuestHandle(post, post.handle);
post.handle = undefined;
post.topic = tidToTopic[post.tid];
post.category = post.topic && cidToCategory[post.topic.cid];
post.isMainPost = post.topic && post.pid === post.topic.mainPid;
post.deleted = post.deleted === 1;
post.timestampISO = utils.toISOString(post.timestamp);
});

posts = posts.filter(post => tidToTopic[post.tid]);

posts = await parsePosts(posts, options);
const result = await plugins.hooks.fire('filter:post.getPostSummaryByPids', { posts: posts, uid: uid });
return result.posts;
};

async function parsePosts(posts, options) {
return await Promise.all(posts.map(async (post) => {
if (!post.content || !options.parse) {
post.content = post.content ? validator.escape(String(post.content)) : post.content;
return post;
}
post = await Posts.parsePost(post);
if (options.stripTags) {
post.content = stripTags(post.content);
}
return post;
}));
}

async function getTopicAndCategories(tids) {
const topicsData = await topics.getTopicsFields(tids, [
'uid', 'tid', 'title', 'cid', 'tags', 'slug',
'deleted', 'scheduled', 'postcount', 'mainPid', 'teaserPid',
]);

const cids = _.uniq(topicsData.map(topic => topic && topic.cid));
const categoriesData = await categories.getCategoriesFields(cids, [
'cid', 'name', 'icon', 'slug', 'parentCid',
'bgColor', 'color', 'backgroundImage', 'imageClass',
]);

return { topics: topicsData, categories: categoriesData };
}

function toObject(key, data) {
const obj = {};
for (let i = 0; i < data.length; i += 1) {
obj[data[i][key]] = data[i];
}
return obj;
}

function stripTags(content) {
if (content) {
return utils.stripHTMLTags(content, utils.stripTags);
}
return content;
}
Posts.getPostSummaryByPids = function (pids, uid, options) {
return __awaiter(this, void 0, void 0, function* () {
if (!Array.isArray(pids) || !pids.length) {
return [];
}
options.stripTags = options.hasOwnProperty('stripTags') ? options.stripTags : false;
options.parse = options.hasOwnProperty('parse') ? options.parse : true;
options.extraFields = options.hasOwnProperty('extraFields') ? options.extraFields : [];
const fields = ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted', 'upvotes', 'downvotes', 'replies', 'handle'].concat(options.extraFields);
let posts = yield Posts.getPostsFields(pids, fields);
posts = posts.filter(Boolean);
posts = yield user.blocks.filter(uid, posts);
const uids = _.uniq(posts.map(p => p && p.uid));
const tids = _.uniq(posts.map(p => p && p.tid));
const [users, topicsAndCategories] = yield Promise.all([
user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status']),
getTopicAndCategories(tids),
]);
const uidToUser = toObject('uid', users);
const tidToTopic = toObject('tid', topicsAndCategories.topics);
const cidToCategory = toObject('cid', topicsAndCategories.categories);
posts.forEach((post) => {
if (!uidToUser.hasOwnProperty(post.uid)) {
post.uid = 0;
}
post.user = uidToUser[post.uid];
Posts.overrideGuestHandle(post, post.handle);
post.handle = undefined;
post.topic = tidToTopic[post.tid];
post.category = post.topic && cidToCategory[post.topic.cid];
post.isMainPost = post.topic && post.pid === post.topic.mainPid;
post.deleted = post.deleted === 1;
post.timestampISO = utils.toISOString(post.timestamp);
});
posts = posts.filter(post => tidToTopic[post.tid]);
posts = yield parsePosts(posts, options);
const result = yield plugins.hooks.fire('filter:post.getPostSummaryByPids', { posts: posts, uid: uid });
return result.posts;
});
};
function parsePosts(posts, options) {
return __awaiter(this, void 0, void 0, function* () {
return yield Promise.all(posts.map((post) => __awaiter(this, void 0, void 0, function* () {
if (!post.content || !options.parse) {
post.content = post.content ? validator.escape(String(post.content)) : post.content;
return post;
}
post = yield Posts.parsePost(post);
if (options.stripTags) {
post.content = stripTags(post.content);
}
return post;
})));
});
}
function getTopicAndCategories(tids) {
return __awaiter(this, void 0, void 0, function* () {
const topicsData = yield topics.getTopicsFields(tids, [
'uid', 'tid', 'title', 'cid', 'tags', 'slug', 'deleted', 'scheduled', 'postcount', 'mainPid', 'teaserPid',
]);
const cids = _.uniq(topicsData.map(topic => topic && topic.cid));
const categoriesData = yield categories.getCategoriesFields(cids, [
'cid', 'name', 'icon', 'slug', 'parentCid', 'bgColor', 'color', 'backgroundImage', 'imageClass',
]);
return { topics: topicsData, categories: categoriesData };
});
}
function toObject(key, data) {
const obj = {};
data.forEach(item => {
obj[item[key]] = item;
});
return obj;
}
function stripTags(content) {
if (content) {
return utils.stripHTMLTags(content, utils.stripTags);
}
return content;
}
};
174 changes: 174 additions & 0 deletions src/posts/summary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/* This is done because the other files are using modules.export and not export default */

/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable no-multi-spaces */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */

const validator = require('validator');
const _ = require('lodash');
const topics = require('../topics');
const user = require('../user');
const plugins = require('../plugins');
const categories = require('../categories');
const utils = require('../utils');

interface Post {
pid: number;
tid: number;
content: string;
uid: number;
timestamp: number;
deleted: number | boolean;
upvotes: number;
downvotes: number;
replies: number;
handle?: string | undefined;
user?: User;
topic?: Topic;
category?: Category;
isMainPost?: boolean;
timestampISO?: string;
}

interface User {
uid: number;
username: string;
userslug: string;
picture: string;
status: string;
}

interface Topic {
tid: number;
uid: number;
title: string;
cid: number;
tags: string[];
slug: string;
deleted: number;
scheduled: boolean;
postcount: number;
mainPid: number;
teaserPid: number;
}

interface Category {
cid: number;
name: string;
icon: string;
slug: string;
parentCid?: number;
bgColor?: string;
color?: string;
backgroundImage?: string;
imageClass?: string;
}

interface ParseOptions {
parse: boolean;
stripTags?: boolean;
extraFields: string[];
}

interface Posts {
getPostsFields(pids: number[], fields: string[]): Promise<Post[]>;
overrideGuestHandle(post: Post, handle?: string): void;
parsePost(post: Post): Promise<Post>;
getPostSummaryByPids(pids: number[], uid: number, options: ParseOptions): Promise<Post[]>;
}

/* eslint-disable no-multi-spaces */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
module.exports = function (Posts:Posts) {
function toObject<Item>(key: keyof Item, data: Item[]): Record<string | number, Item> {
const obj: Record<string | number, Item> = {};
data.forEach((item: Item) => {
const itemKey = item[key];
if (typeof itemKey === 'string' || typeof itemKey === 'number') obj[itemKey] = item;
});
return obj;
}

function stripTags(content: string): string {
if (content && utils) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
return utils.stripHTMLTags(content, utils.stripTags);
}
return content;
}

async function getTopicAndCategories(tids: number[]): Promise<{ topics: Topic[], categories: Category[] }> {
const topicsData: Topic[] = await topics.getTopicsFields(tids, [
'uid', 'tid', 'title', 'cid', 'tags', 'slug', 'deleted', 'scheduled', 'postcount', 'mainPid', 'teaserPid',
]);

const cids = _.uniq(topicsData.map(topic => topic && topic.cid));
const categoriesData: Category[] = await categories.getCategoriesFields(cids, [
'cid', 'name', 'icon', 'slug', 'parentCid', 'bgColor', 'color', 'backgroundImage', 'imageClass',
]);

return { topics: topicsData, categories: categoriesData };
}

Posts.getPostSummaryByPids = async function (pids: number[], uid: number, options: ParseOptions): Promise<Post[]> {
if (!Array.isArray(pids) || !pids.length) {
return [];
}

options.stripTags = options.hasOwnProperty('stripTags') ? options.stripTags : false;
options.parse = options.hasOwnProperty('parse') ? options.parse : true;
options.extraFields = options.hasOwnProperty('extraFields') ? options.extraFields : [];

const fields = ['pid', 'tid', 'content', 'uid', 'timestamp', 'deleted', 'upvotes', 'downvotes', 'replies', 'handle'].concat(options.extraFields);

let posts: Post[] = await Posts.getPostsFields(pids, fields);
posts = posts.filter(Boolean);
posts = await user.blocks.filter(uid, posts);

const uids = _.uniq(posts.map(p => p && p.uid));
const tids = _.uniq(posts.map(p => p && p.tid));

const [users, topicsAndCategories] = await Promise.all([
user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status']),
getTopicAndCategories(tids),
]);

const uidToUser = toObject<User>('uid', users);
const tidToTopic = toObject<Topic>('tid', topicsAndCategories.topics);
const cidToCategory = toObject<Category>('cid', topicsAndCategories.categories);

posts.forEach((post:Post) => {
if (!uidToUser.hasOwnProperty(post.uid)) {
post.uid = 0;
}
post.user = uidToUser[post.uid];
Posts.overrideGuestHandle(post, post.handle);
post.handle = undefined;
post.topic = tidToTopic[post.tid];
post.category = post.topic && cidToCategory[post.topic.cid];
post.isMainPost = post.topic && post.pid === post.topic.mainPid;
post.deleted = post.deleted === 1;
post.timestampISO = utils.toISOString(post.timestamp);
});

posts = posts.filter(post => tidToTopic[post.tid]);

posts = await parsePosts(posts, options);
const result = await plugins.hooks.fire('filter:post.getPostSummaryByPids', { posts: posts, uid: uid });
return result.posts;
};

async function parsePosts(posts: Post[], options: ParseOptions): Promise<Post[]> {
return await Promise.all(posts.map(async (post) => {
if (!post.content || !options.parse) {
post.content = post.content ? validator.escape(String(post.content)) : post.content;
return post;
}
post = await Posts.parsePost(post);
if (options.stripTags) {
post.content = stripTags(post.content);
}
return post;
}));
}
};