UNPKG

@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
"use strict"; 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);