UNPKG

@rnaga/wp-node

Version:

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

536 lines (535 loc) • 21.9 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); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UserTrx = void 0; const common_1 = require("../common/"); const config_1 = require("../config"); const components_1 = require("../core/components"); const current_1 = require("../core/current"); const logger_1 = require("../core/logger"); const options_1 = require("../core/options"); const blog_util_1 = require("../core/utils/blog.util"); const query_util_1 = require("../core/utils/query.util"); const roles_util_1 = require("../core/utils/roles.util"); const user_util_1 = require("../core/utils/user.util"); const validator_1 = require("../core/validator"); const vars_1 = require("../core/vars"); const database_1 = __importDefault(require("../database")); const component_1 = require("../decorators/component"); const val = __importStar(require("../validators")); const blog_trx_1 = require("./blog.trx"); const link_trx_1 = require("./link.trx"); const meta_trx_1 = require("./meta.trx"); const post_trx_1 = require("./post.trx"); const trx_1 = require("./trx"); let UserTrx = class UserTrx extends trx_1.Trx { database; logger; components; config; validator; vars; constructor(database, logger, components, config, validator, vars) { super(components); this.database = database; this.logger = logger; this.components = components; this.config = config; this.validator = validator; this.vars = vars; } // wpmu_create_user // async upsert(input, options) { const { attachRole = true, removeRole = false } = options ?? {}; const userUtil = this.components.get(user_util_1.UserUtil); let update = false; let userBefore = undefined; if (input.ID) { update = true; userBefore = await userUtil.get(input.ID); const roleBefore = await userBefore.role(); if (!userBefore.props) { throw Error(`User not found - ${input.ID}`); } // Combine input with existing record input = { ...userBefore.props, ...(await userBefore.meta.props()), role: Array.from(roleBefore.names), ...input, }; } const queryUtil = this.components.get(query_util_1.QueryUtil); const parsedInput = val.trx.userUpsert.parse(input); const data = { ID: parsedInput.ID ?? 0, user_url: parsedInput.user_url ?? "", user_registered: parsedInput.user_registered ?? common_1.formatting.dateMySQL(), user_activation_key: parsedInput.user_activation_key, first_name: parsedInput.first_name, user_pass: parsedInput?.user_pass ?? "", }; let metaInput = { nickname: parsedInput.nickname, comment_shortcuts: parsedInput.comment_shortcuts, first_name: parsedInput.first_name, last_name: parsedInput.last_name, description: parsedInput.description, rich_editing: parsedInput.rich_editing, syntax_highlighting: parsedInput.syntax_highlighting, admin_color: parsedInput.admin_color.replace(/[^a-z0-9 _.\-@]/gi, ""), use_ssl: parsedInput.use_ssl, show_admin_bar_front: parsedInput.show_admin_bar_front, locale: parsedInput.locale, }; if (!update) { data.user_login = val.trx.userLogin.parse(parsedInput.user_login); data.user_pass = (0, common_1.hashPassword)(parsedInput.user_pass); } data.user_login = common_1.formatting.username(parsedInput.user_login); if (0 >= data.user_login.length || 60 < data.user_login.length) { throw new Error(`user_loign is too short or long - ${data.user_login}`); } const userLogin = parsedInput.user_login; if (0 <= metaInput.nickname.length) { metaInput.nickname = userLogin; } if (!update && (await queryUtil.users((query) => query.where("user_login", userLogin)))) { throw new Error(`username already exists - ${userLogin}`); } /* * If a nicename is provided, remove unsafe user characters before using it. * Otherwise build a nicename from the user_login. */ const userNicename = 0 < parsedInput.user_nicename.length ? common_1.formatting.username(parsedInput.user_nicename, true) : userLogin.substring(0, 50); data.user_nicename = await userUtil.getUniqueNicename(userNicename, userLogin); /* * If there is no update, just check for `email_exists`. If there is an update, * check if current email and new email are the same, and check `email_exists` * accordingly. */ if ((!update || (userBefore && parsedInput.user_email !== userBefore.props?.user_email)) && (await queryUtil.users((query) => query.where("user_email", parsedInput.user_email)))) { throw new Error(`Email is already used - ${parsedInput.user_email} ${userBefore ? userBefore.props?.user_email : ""}`); } data.user_email = parsedInput.user_email; if (100 < data.user_url.length) { throw new Error(`user url is too long - ${data.user_url}`); } if (parsedInput.spam && !this.config.isMultiSite()) { throw new Error("Marking a user as spam is only supported on Multisite."); } if (this.config.isMultiSite()) { data.spam = parsedInput.spam ?? 0; } metaInput.nickname = parsedInput.nickname ?? userLogin; data.display_name = parsedInput.display_name; if (0 === data.display_name.length) { data.display_name = `${metaInput.first_name} ${metaInput.last_name}`.trim(); } if (0 === data.display_name.length) { data.display_name = userLogin; } let dataUpsert = {}; try { dataUpsert = this.validator.execAny(update ? val.trx.userUpdate : val.trx.userInsert, Object.entries(data) .map(([key, value]) => ({ [key]: common_1.formatting.unslash(value), })) .reduce((obj, item) => ({ ...obj, ...item }), {})); } catch (e) { this.logger.warn(`parse error: ${e}`, { data }); throw e; } if (!dataUpsert) { throw new Error(`Invalid post data - ${JSON.stringify(data)}`); } if (this.config.isMultiSite()) { dataUpsert.spam = data.spam; } const trx = await this.database.transaction; try { if (update) { if (userBefore && (data.user_email !== userBefore.props?.user_email || data.user_pass !== userBefore.props.user_pass)) { dataUpsert.user_activation_key = ""; } await trx .table(this.tables.get("users")) .where("ID", data.ID) .update(dataUpsert); } else { await trx .insert(dataUpsert) .into(this.tables.get("users")) .then((v) => { data.ID = v[0]; }); } } catch (e) { await trx.rollback(); throw new Error(`Failed to insert user - ${e}`); } await trx.commit(); const user = await userUtil.get(data.ID); if (!user.props) { throw new Error(`User not found - ${data.ID}`); } const userId = user.props.ID; metaInput = { ...metaInput, ...parsedInput.meta_input }; const metaTrx = this.components.get(meta_trx_1.MetaTrx); for (const [key, value] of Object.entries(metaInput)) { if (!value) { continue; } await metaTrx.upsert("user", userId, key, value, { serialize: typeof value == "object" || Array.isArray(value), }); } if (update && removeRole) { await this.removeRole(userId); } else if (attachRole) { await this.upsertRole(userId, parsedInput.role); } return userId; } async upsertRole(userId, roleNameOrNames) { const optionsComponent = this.components.get(options_1.Options); let roleNames = {}; if (!roleNameOrNames) { roleNameOrNames = (await optionsComponent.get("default_role")) ?? "subscriber"; } if ("string" === typeof roleNameOrNames) { roleNames = { [roleNameOrNames]: true }; } else if (Array.isArray(roleNameOrNames)) { for (const roleName of roleNameOrNames) { roleNames = { ...roleNames, [roleName]: true }; } } const metaTrx = this.components.get(meta_trx_1.MetaTrx); const result = await metaTrx.upsert("user", userId, `${this.tables.prefix}capabilities`, roleNames, { serialize: true, }); return result; } async syncSuperAdmin(userId, options) { const userUtil = this.components.get(user_util_1.UserUtil); const { remove = false, blogId } = options ?? {}; let { siteId } = options ?? {}; if (blogId) { const blogUtil = this.components.get(blog_util_1.BlogUtil); const blog = await blogUtil.get(blogId); if (!blog.props?.site_id) { throw new Error("Invalid Blog"); } siteId = blog.props?.site_id; } if (!siteId) { const current = this.components.get(current_1.Current); siteId = current.siteId; } const user = await userUtil.get(userId); if (!user.props?.user_login) { throw new Error("User not found"); } const userLogin = user.props.user_login; const metaTrx = this.components.get(meta_trx_1.MetaTrx); const rolesUtil = this.components.get(roles_util_1.RolesUtil); const superAdmins = await rolesUtil.getSuperAdmins({ siteId, }); const newSuperAdmins = new Set(); if (remove) { // Skip if there's only one super admin in site. // i.e. make sure there's at least one super admin. superAdmins.length > 1 && superAdmins.map((superAdmin) => superAdmin !== userLogin && newSuperAdmins.add(superAdmin)); } else { superAdmins.map((superAdmin) => newSuperAdmins.add(superAdmin)); newSuperAdmins.add(userLogin); } newSuperAdmins.size > 0 && newSuperAdmins.size !== superAdmins.length && (await metaTrx.upsert("site", siteId, "site_admins", Array.from(newSuperAdmins), { serialize: true, })); } // wp_delete_user async remove(userId, reassign) { const userUtil = this.components.get(user_util_1.UserUtil); const queryUtil = this.components.get(query_util_1.QueryUtil); const user = await userUtil.get(userId); if (!user.props) { throw new Error(`User not found - ${userId}`); } let reassignUser = undefined; if (reassign) { reassignUser = await userUtil.get(reassign); if (!reassignUser.props) { throw new Error(`User not found - ${reassign}`); } } const postTypeObject = this.config.config.posts.types; if (!reassign) { const postTypesToDelete = []; for (const [type, typeObject] of Object.entries(postTypeObject)) { if (typeObject.deleteWithUser || typeObject.supports.includes("author")) { postTypesToDelete.push(type); } } const postsToDelete = (await queryUtil.posts((query) => { query .where("post_author", userId) .whereIn("post_type", postTypesToDelete); })) ?? []; const postTrx = this.components.get(post_trx_1.PostTrx); for (const post of postsToDelete) { await postTrx.remove(post.ID); } const linksToDelete = (await queryUtil.common("links", (query) => { query.where("link_owner", userId); })) ?? []; const linkTrx = this.components.get(link_trx_1.LinkTrx); for (const link of linksToDelete) { await linkTrx.remove(link.link_id); } } else { const postTrx = this.components.get(post_trx_1.PostTrx); const linkTrx = this.components.get(link_trx_1.LinkTrx); // Re-assign posts and links await postTrx.changeAuthor(userId, reassign); await linkTrx.changeUser(userId, reassign); } // FINALLY, delete user. if (this.config.isMultiSite()) { const blogTrx = this.components.get(blog_trx_1.BlogTrx); const current = this.components.get(current_1.Current); await blogTrx.removeUser(current.blogId, userId); } else { await this.removeMeta(user); const trx = await this.database.transaction; try { await trx.table(this.tables.get("users")).where("ID", userId).del(); } catch (e) { await trx.rollback(); throw new Error("Failed to delete user"); } await trx.commit(); } return true; } // Remove user from entire blogs async removeFromAllBlogs(userId, reassignList // <blogId, userId to reassign> ) { if (!this.config.isMultiSite()) { throw new Error("Multisite mode is disabled"); } const userUtil = this.components.get(user_util_1.UserUtil); const sites = await userUtil.getSites(userId); const CONTEXT = this.vars.CONTEXT; for (const site of sites.sites ?? []) { for (const blog of site.blogs ?? []) { const blogId = blog.blog_id; const siteId = blog.site_id; const clonedContext = await CONTEXT.clone(); await clonedContext.current.switchSite(siteId, blogId); const reassign = reassignList ? reassignList[blogId] : undefined; await clonedContext.utils.trx.user.remove(userId, reassign); } } // Remove user meta await this.removeMeta(userId); // Remove user wp_users const trx = await this.database.transaction; try { await trx.table(this.tables.get("users")).where("ID", userId).del(); } catch (e) { await trx.rollback(); throw new Error("Failed to delete user"); } await trx.commit(); return true; } async removeMeta(userIdOrUser) { const userUtil = this.components.get(user_util_1.UserUtil); const queryUtil = this.components.get(query_util_1.QueryUtil); const user = typeof userIdOrUser === "number" ? await userUtil.get(userIdOrUser) : userIdOrUser; if (!user.props) { throw new Error("User not found"); } const userId = user.props.ID; const metaToDelete = (await queryUtil.meta("user", (query) => { query.withIds([userId]); })) ?? []; const metaTrx = this.components.get(meta_trx_1.MetaTrx); for (const meta of metaToDelete) { if (typeof meta.meta_key !== "string") { continue; } await metaTrx.remove("user", { key: meta.meta_key, objectId: userId, }); } } async removeRole(userId, options) { const metaTrx = this.components.get(meta_trx_1.MetaTrx); const { removeSuperAdmin = true } = options ?? {}; // Remove from super admins list if (removeSuperAdmin) { await this.syncSuperAdmin(userId, { remove: true, }); } return await metaTrx.remove("user", { objectId: userId, key: `${this.tables.prefix}capabilities`, }); } // register_new_user async registerNew(userLogin, email) { userLogin = common_1.formatting.username(userLogin); if (0 >= userLogin.length) { throw new Error(`Please enter a username.`); } const userUtil = this.components.get(user_util_1.UserUtil); let user = await userUtil.get(userLogin); if (user.props) { throw new Error(`This username is already registered. - ${userLogin}`); } // Check the email address. if (0 >= email.length) { throw new Error(`Please type your email address.`); } if (!this.validator.fieldSafe("users", "user_email", email)) { throw new Error(`The email address is not correct. - ${email}`); } user = await userUtil.get(email); if (user.props) { throw new Error(`This email address is already registered. - ${email}`); } const userIdOrError = await this.upsert({ user_login: userLogin, user_email: email, }).catch((e) => e); if (typeof userIdOrError !== "number") { throw new Error(userIdOrError); } const userId = userIdOrError; const metaTrx = this.components.get(meta_trx_1.MetaTrx); await metaTrx.upsert("user", userId, "default_password_nag", true); // Set up the password change nag. return userId; } // get_password_reset_key async resetActivationKey(user) { const userUtil = this.components.get(user_util_1.UserUtil); if (!user.props || !userUtil.isPasswordResetAllowed(user)) { throw new Error("Password reset is not allowed for this user"); } // Generate something random for a password reset key. const resetKey = (0, common_1.generatePassword)(20, false); const hash = (0, common_1.hashPassword)(resetKey); const activationKey = `${Math.floor(Date.now() / 1000)}:${hash}`; try { await this.upsert({ ID: user.props.ID, user_activation_key: activationKey, }); } catch (e) { throw new Error(`Failed to save activation key - ${e}`); } return resetKey; } async revokeActivationKey(user) { if (!user.props) { throw new Error("Invalid User"); } const userId = user.props?.ID; try { await this.upsert({ ID: userId, user_activation_key: "", }); } catch (e) { throw new Error(`Failed to revoke activation key - ${e}`); } } }; exports.UserTrx = UserTrx; exports.UserTrx = UserTrx = __decorate([ (0, component_1.transactions)(), __metadata("design:paramtypes", [database_1.default, logger_1.Logger, components_1.Components, config_1.Config, validator_1.Validator, vars_1.Vars]) ], UserTrx);