@rnaga/wp-node
Version:
👉 **[View Full Documentation at rnaga.github.io/wp-node →](https://rnaga.github.io/wp-node/)**
313 lines (312 loc) • 13 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommentUtil = void 0;
const common_1 = require("../../common");
const config_1 = require("../../config");
const component_1 = require("../../decorators/component");
const val = __importStar(require("../../validators"));
const comment_1 = require("../comment");
const components_1 = require("../components");
const options_1 = require("../options");
const post_util_1 = require("./post.util");
const query_util_1 = require("./query.util");
const user_util_1 = require("./user.util");
let CommentUtil = class CommentUtil {
components;
config;
constructor(components, config) {
this.components = components;
this.config = config;
}
async get(id) {
return await this.components.asyncGet(comment_1.Comment, [id]);
}
toComment(comment) {
return this.components.get(comment_1.Comment, [comment.comment_ID, comment]);
}
toComments(comments) {
return comments.map((comment) => this.toComment(comment));
}
async getDefaultStatus(postType, commentType) {
postType = postType ?? "post";
commentType = commentType ?? "comment";
const options = this.components.get(options_1.Options);
const postTypeObjects = this.components
.get(post_util_1.PostUtil)
.getTypeObject(postType);
let supports, option, status;
switch (commentType) {
case "pingback":
case "trackback":
supports = "trackbacks";
option = "ping";
break;
default:
supports = "comments";
option = "comment";
break;
}
// Set the status.
if ("page" === postType) {
status = "closed";
}
else if (postTypeObjects && postTypeObjects.supports.includes(supports)) {
const defaultStatus = await options.get(`default_${option}_status`);
status =
defaultStatus === "open" || defaultStatus === "closed"
? defaultStatus
: "closed";
}
else {
status = "closed";
}
return status;
}
// wp_get_comment_status
async getStatusAsString(commentId) {
const comment = await this.get(commentId);
if (!comment.props) {
return "";
}
const approved = comment.props.comment_approved;
if (!approved) {
return "";
}
if ("1" === approved) {
return "approved";
}
if ("0" === approved) {
return "unapproved";
}
if ("spam" == approved || "trash" == approved) {
return approved;
}
return "";
}
// comments_open
isOpen(post) {
return post.props && "open" == post.props.comment_status;
}
// wp_check_comment_disallowed_list
async containsNGWord(args) {
const { comment_author: author = "", comment_author_email: email = "", comment_author_url: url = "", comment_content: content = "", comment_author_IP: authorIp = "", comment_agent: userAgent = "", } = args;
const options = this.components.get(options_1.Options);
const moderationKeys = await options.get("disallowed_keys");
if (!moderationKeys || 0 >= moderationKeys.length) {
return false;
}
// Ensure HTML tags are not being used to bypass the list of disallowed characters and words.
const commentWithoutHtml = common_1.formatting.stripTags(content);
const escapeRegExp = (key) => {
// Escape special characters for use in a regular expression
return key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
for (const key of moderationKeys.split("\n").map((key) => key.trim())) {
// Skip empty lines.
if (0 >= key.length) {
continue;
}
// Do some escaping magic so that '#' chars in the spam words don't break things
const pattern = new RegExp(escapeRegExp(key), "iu");
if (pattern.test(author) ||
pattern.test(email) ||
pattern.test(url) ||
pattern.test(content) ||
pattern.test(commentWithoutHtml) ||
pattern.test(authorIp) ||
pattern.test(userAgent)) {
return true;
}
}
return false;
}
// check_comment
async isValid(args) {
const { comment_author: author = "", comment_author_email: email = "", comment_author_url: url = "", comment_content: content = "", comment_author_IP: authorIp = "", comment_agent: userAgent = "", comment_type: commentType = "comment", } = args;
const options = this.components.get(options_1.Options);
// If manual moderation is enabled, skip all checks and return false.
if (1 === (await options.get("comment_moderation"))) {
return false;
}
// Check for the number of external links if a max allowed number is set.
const maxLinks = await options.get("comment_max_links");
if (maxLinks) {
const regex = /<a [^>]*href/gi;
const numLinks = content.match(regex);
if (numLinks && numLinks.length >= maxLinks) {
return false;
}
}
const moderationKeys = (await options.get("moderation_keys"))?.trim();
// If moderation 'keys' (keywords) are set, process them.
if (moderationKeys) {
const escapeRegExp = (key) => {
// Escape special characters for use in a regular expression
return key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
for (const key of moderationKeys.split("\n").map((key) => key.trim())) {
// Skip empty lines.
if (0 >= key.length) {
continue;
}
// - Escape the word to be used in the regex
// - Create the regex pattern
const pattern = new RegExp(escapeRegExp(key), "iu");
for (const field of [
author,
email,
url,
content,
authorIp,
userAgent,
]) {
if (pattern.test(field)) {
return false;
}
}
}
/*
* Check if the option to approve comments by previously-approved authors is enabled.
*
* If it is enabled, check whether the comment author has a previously-approved comment,
* as well as whether there are any moderation keywords (if set) present in the author
* email address. If both checks pass, return true. Otherwise, return false.
*/
if (1 === (await options.get("comment_previously_approved"))) {
if (commentType === "trackback" ||
commentType === "pingback" ||
0 >= author.length ||
0 >= email.length) {
return false;
}
const queryUtil = this.components.get(query_util_1.QueryUtil);
const user = await queryUtil.users((query) => {
query.where("user_email", common_1.formatting.unslash(email)).builder.first();
}, val.database.wpUsers);
let okToComment = false;
if (user && user.ID) {
okToComment = (await queryUtil.comments((query) => {
query.where("user_id", user.ID).where("approved", "1");
}))
? true
: false;
}
else {
okToComment = (await queryUtil.comments((query) => {
query
.where("author", author)
.where("author_email", email)
.where("approved", "1");
}))
? true
: false;
}
return (okToComment && (!moderationKeys || !email.includes(moderationKeys)));
}
}
return true;
}
// wp_allow_comment
async getStatus(comment) {
const parsed = val.database.wpComments.safeParse({
comment_ID: 0,
...comment,
});
if (!parsed.success) {
return "0";
}
const data = parsed.data;
/*
* Simple duplicate check.
* expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
*/
const queryUtil = this.components.get(query_util_1.QueryUtil);
const duplicate = await queryUtil.comments((query) => {
query
.where("post_ID", common_1.formatting.unslash(data.comment_post_ID))
.where("parent", common_1.formatting.unslash(data.comment_parent));
if (data.comment_author) {
query.where("author", common_1.formatting.unslash(data.comment_author));
}
if (data.comment_author_email) {
query.where("author_email", common_1.formatting.unslash(data.comment_author_email));
}
query.builder.first();
}, val.database.wpComments);
if (duplicate) {
throw new Error("comment_duplicate");
}
let user = undefined;
let post = undefined;
if (data.user_id && data.comment_post_ID) {
const userUtil = this.components.get(user_util_1.UserUtil);
user = await userUtil.get(data.user_id);
post = await queryUtil.posts((query) => {
query.where("ID", data.comment_post_ID).builder.first();
}, val.database.wpPosts);
}
if (user &&
user.props &&
(data.user_id == post?.post_author ||
(await user.can("moderate_comments")))) {
return "1";
}
let approved = "0";
if (await this.isValid(comment)) {
approved = "1";
}
if (await this.containsNGWord(comment)) {
approved = this.config.config.constants.EMPTY_TRASH_DAYS
? "trash"
: "spam";
}
return approved;
}
};
exports.CommentUtil = CommentUtil;
exports.CommentUtil = CommentUtil = __decorate([
(0, component_1.component)(),
__metadata("design:paramtypes", [components_1.Components, config_1.Config])
], CommentUtil);