UNPKG

@rnaga/wp-node

Version:

👉 **[View Full Documentation at rnaga.github.io/wp-node →](https://rnaga.github.io/wp-node/)**

432 lines (431 loc) • 20.1 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.CommentCrud = void 0; const common_1 = require("../common"); const diff_1 = require("../common/diff"); const components_1 = require("../core/components"); const options_1 = require("../core/options"); const comment_util_1 = require("../core/utils/comment.util"); const post_util_1 = require("../core/utils/post.util"); const query_util_1 = require("../core/utils/query.util"); const component_1 = require("../decorators/component"); const transactions_1 = require("../transactions"); const val = __importStar(require("../validators")); const crud_1 = require("./crud"); const error_1 = require("./error"); let CommentCrud = class CommentCrud extends crud_1.Crud { commentUtil; postUtil; constructor(components, commentUtil, postUtil) { super(components); this.commentUtil = commentUtil; this.postUtil = postUtil; } async canReadPostComment(post, password) { if (!post.props) { return false; } const { user } = await this.getUser(); const postType = this.postUtil.getTypeObject(post.props?.post_type); if (!postType) { return false; } return (this.checkPasswordProtectedPost(post.props, password ?? "") || (await this.canReadPost(post.props)) || (await user.can("edit_post", post.props?.ID))); } async canRead(comment) { const { user } = await this.getUser(); if (!user.props) { return false; } if (comment.props?.comment_post_ID) { const post = await this.postUtil.get(comment.props.comment_post_ID); if (post.props && (await this.canReadPost(post.props)) && `${comment.props.comment_approved}` == "1") { return true; } } if (!comment.props?.comment_post_ID && !(await user.can("moderate_comments"))) { return false; } if (comment.props?.user_id && user.props.ID === comment.props.user_id) { return true; } return (comment.props?.comment_ID && (await user.can("edit_comment", comment.props.comment_ID))); } async canEdit(commentId) { if (!(await this.commentUtil.get(commentId)).props) { return false; } const { user } = await this.getUser(); const role = await user.role(); if (role.is("anonymous")) { return false; } return ((await user.can("moderate_comments")) || (await user.can("edit_comment", commentId))); } async formReturnData(comment, context, options) { const { limitChildren = 99 } = options ?? {}; const props = comment.props; const parentComment = await comment.parent(); const user = await comment.user(); const post = await comment.post(); const children = await comment.children(limitChildren); const countChildren = await comment.countChildren(); if ("edit" === context) { return { ...props, post_comment_count: post?.comment_count, post_title: post?.post_title, post_type: post?.post_type, post_guid: post?.guid, user_display_name: user?.display_name, parent_comment_author: parentComment?.comment_author, parent_user_display_name: parentComment?.display_name, parent_user_id: parentComment?.user_id, children, count_children: countChildren, metas: await comment.meta.props(), }; } else { return { comment_ID: props.comment_ID, comment_post_ID: props.comment_post_ID, comment_author: props.comment_author, comment_author_email: props.comment_author_email, comment_date: props.comment_date, comment_date_gmt: props.comment_date_gmt, comment_content: props.comment_content, comment_parent: props.comment_parent, comment_approved: props.comment_approved, comment_type: props.comment_type, post_comment_count: post?.comment_count, post_title: post?.post_title, post_type: post?.post_type, post_guid: post?.guid, post_author: post?.post_author, post_status: post?.post_status, parent_comment_author: parentComment?.comment_author, parent_user_id: parentComment?.user_id, parent_user_display_name: parentComment?.display_name, user_id: props.user_id, user_display_name: user?.display_name, children, count_children: countChildren, }; } } async getAsUpsert(commentId) { const comment = (await this.get(commentId, { context: "edit" })).data; return this.returnValue(val.trx.commentUpsert.parse(comment)); } async get(commentId, options) { const { password = "", context = "view" } = options ?? {}; const comment = await this.commentUtil.get(commentId); if (!comment.props) { throw new error_1.CrudError(error_1.StatusMessage.NOT_FOUND, "Comment not found"); } const { user } = await this.getUser(); if (context == "edit" && !(await user.can("moderate_comments"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit comments"); } if (!(await this.canRead(comment))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to read comments"); } const post = await this.postUtil.get(comment.props.comment_post_ID); if (post.props && !(await this.canReadPostComment(post, password))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to read the post for this comment"); } return this.returnValue((await this.formReturnData(comment, context))); } async create(data, options) { const { remoteIp } = options ?? {}; const { user, userId } = await this.getUser(); const role = await user.role(); if (!role.is("anonymous") || !user.props) { const options = this.components.get(options_1.Options); if (await options.get("comment_registration")) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you must be logged in to comment"); } } // Limit who can set comment `author`, `author_ip` or `status` to anything other than the default. if (data.user_id && userId !== data.user_id && (await user.can("moderate_comments"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit this comment"); } if (data.comment_author_IP && !(await user.can("moderate_comments")) && (!remoteIp || data.comment_author_IP !== remoteIp)) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit this comment"); } if (data.comment_approved && !(await user.can("moderate_comments"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit this comment"); } if (!data.comment_post_ID) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to create this comment without a post"); } const post = await this.postUtil.get(data.comment_post_ID); if (!post.props || "draft" === post.props.post_status || "trash" === post.props.post_status || !this.canReadPostComment(post) || !this.commentUtil.isOpen(post)) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to create a comment on this post"); } if (data.comment_ID) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, "Cannot create existing comment"); } // Do not allow comments to be created with a non-default type. if (data.comment_type && data.comment_type?.length > 0 && "comment" !== data.comment_type) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, "Cannot create a comment with that type"); } if (!data.comment_content || data.comment_content == "") { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, "Invalid comment content"); } // Set author data if the user's logged in. if (user.props && 0 >= [ "user_id", "comment_author", "comment_author_email", "comment_author_url", ].filter((k) => data[k] && (data[k]?.toString() ?? "").length > 0) .length) { data = { ...data, user_id: user.props.ID, comment_author: user.props.display_name, comment_author_email: user.props.user_email, comment_author_url: user.props.user_url, }; } // Honor the discussion setting that requires a name and email address of the comment author. const optionsCore = this.components.get(options_1.Options); if ((!data.comment_author_email || 0 >= data.comment_author_email.length) && (!data.comment_author || 0 >= data.comment_author.length) && (await optionsCore.get("require_name_email"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Creating a comment requires valid author name and email values."); } data.comment_approved = await this.commentUtil.getStatus(data); const commentTrx = this.components.get(transactions_1.CommentTrx); return this.returnValue(await commentTrx.upsert(data)); } async update(commentId, data) { if (!(await this.canEdit(commentId))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit this comment"); } data.comment_ID = commentId; const currentComment = (await this.getAsUpsert(commentId)).data; if (currentComment.comment_type !== data.comment_type) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to change the comment type"); } const diffData = (0, diff_1.diffObject)(data, currentComment); const postId = diffData.comment_post_ID ?? currentComment.comment_post_ID; if (postId && postId > 0) { const post = await this.postUtil.get(postId); if (!post.props) { throw new error_1.CrudError(error_1.StatusMessage.BAD_REQUEST, "Invalid post ID"); } } const commentTrx = this.components.get(transactions_1.CommentTrx); return this.returnValue(await commentTrx.upsert(data)); } async delete(commentId, force = false) { if (!(await this.canEdit(commentId))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to delete this comment"); } const commentTrx = this.components.get(transactions_1.CommentTrx); return this.returnValue(await commentTrx.remove(commentId, force)); } async list(args, options) { const { context = "view", password, limitChildren } = options ?? {}; const { user } = await this.getUser(); const queryUtil = this.components.get(query_util_1.QueryUtil); const parsedArgs = val.crud.commentListParams.parse(args ?? {}); if (parsedArgs.post) { for (const postId of parsedArgs.post) { const post = await this.postUtil.get(postId); if (!post.props || !(await this.canReadPost(post.props)) || !(await this.canReadPostComment(post, password))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to read the post for this comment"); } } } if (context == "edit" && !(await user.can("moderate_comments"))) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Sorry, you are not allowed to edit comments"); } if (!(await user.can("edit_posts"))) { const protectedParams = [ "author", "author_exclude", "author_email", "type", "status", ]; for (const param of protectedParams) { switch (param) { case "status": if ("approve" !== parsedArgs[param]) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Query parameter not permitted"); } break; case "type": if ("comment" !== parsedArgs[param]) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, "Query parameter not permitted"); } break; default: if (parsedArgs[param]) { throw new error_1.CrudError(error_1.StatusMessage.UNAUTHORIZED, `Query parameter not permitted - ${param} ${parsedArgs[param]}`); } break; } } } const buildQuery = (query) => { const { column } = query.alias; const offset = (parsedArgs.page - 1) * parsedArgs.per_page; const limit = parsedArgs.per_page; query.builder .offset(offset) .limit(limit) .groupBy(column("comments", "comment_ID")); if (parsedArgs.orderby) { query.builder.orderBy(column("comments", parsedArgs.orderby), parsedArgs.order); } if (parsedArgs.search) { query.andWhere((query) => { const searchColumns = [ "author", "author_email", "author_url", "author_IP", "content", ]; for (const searchColumn of searchColumns) { parsedArgs.search && query.or.whereLike(searchColumn, parsedArgs.search); } }); } for (const key of Object.keys(parsedArgs)) { const value = parsedArgs[key]; if (!value) continue; switch (key) { case "after": case "before": query.where("date", common_1.formatting.dateMySQL(value), key == "after" ? ">=" : "<="); break; case "author": query.whereIn("user_id", value); break; case "author_exclude": query.andWhereNot((query) => query.whereIn("user_id", value)); break; case "author_email": query.where("author_email", value); break; case "include": query.whereIn("ID", value); break; case "exclude": query.andWhereNot((query) => query.whereIn("ID", value)); break; case "parent": query.whereIn("parent", value); break; case "parent_exclude": query.andWhereNot((query) => query.whereIn("parent", value)); break; case "post": query.whereIn("post_ID", value); break; case "type": query.where("type", value); break; case "status": query.whereIn("approved", value == "approve" ? ["1", "approve"] : [`${value}`]); break; } } }; const comments = (await queryUtil.comments((query) => { buildQuery(query); })) ?? []; const counts = await queryUtil.comments((query) => { buildQuery(query); query.count("comments", "comment_ID"); }, val.query.resultCount); const data = []; for (const comment of this.commentUtil.toComments(comments)) { data.push(await this.formReturnData(comment, context, { limitChildren })); } const pagination = this.pagination({ page: parsedArgs.page, limit: parsedArgs.per_page, count: counts?.count ?? 0, }); return this.returnValue(data, { pagination }); } }; exports.CommentCrud = CommentCrud; exports.CommentCrud = CommentCrud = __decorate([ (0, component_1.component)(), __metadata("design:paramtypes", [components_1.Components, comment_util_1.CommentUtil, post_util_1.PostUtil]) ], CommentCrud);