/** * AKSRT User Ban — 前台“禁封/解封用户”弹窗 * * 1:1 复刻参考主题 Zibll 前台禁封能力: * - zib_get_edit_user_ban_link → [data-aksrt-ban] 委托入口(三点菜单内) * - zib_get_set_user_ban_modal → REST /user-ban/modal 拉取弹窗内容 * - zib_ajax_set_user_ban → REST /user-ban/set 提交 * - zib_get_ban_info_modal → [data-aksrt-ban-info](查看禁封状态) * - zib_get_user_ban_appeal_modal → [data-aksrt-ban-appeal](禁封申诉) * - zib_get_ban_log_modal → [data-aksrt-ban-log](禁封记录) * - zib_user_ban_appeal(wp-ajax) → admin-ajax aksrt_ban_appeal_submit 提交 * * 弹窗经 AksrtModalRuntime.openRemote 打开(与参考主题 refresh_modal 每次刷新 * 同语义):桌面 600px 宽(参考主题未传 data-class → Bootstrap .modal-dialog * 默认宽 600px)/ min-height 360px(data-height=360 → .modal-content 初始高度), * 移动端底部抽屉(mobileBottom)。提交成功语义与参考主题 zib_ajax_set_user_ban * 返回值一致:禁封 → reload:true(整页刷新),解封 → hide_modal:true(仅关闭弹窗)。 * * @package AKSRT */ (function () { 'use strict'; var API_ROOT = (function () { var d = window.aksrtData || {}; var raw = d.apiUrl || d.restUrl || d.rest_url || '/index.php?rest_route=/aksrt/v1'; return String(raw).replace(/\/$/, ''); })(); var NONCE = (window.aksrtData && window.aksrtData.restNonce) || ''; var AJAX_URL = (window.aksrtData && window.aksrtData.ajaxUrl) || (window._win && window._win.ajax_url) || '/wp-admin/admin-ajax.php'; var MODAL_HEIGHT = 360; // 参考主题 data_class='modal-mini' + data-height=240:400px 宽、初始高 240。 var MINI_MODAL_HEIGHT = 240; function ajaxUrl() { return AJAX_URL; } function openRemoteModal(options) { if (!window.AksrtModalRuntime || typeof window.AksrtModalRuntime.openRemote !== 'function') { notyf('禁封组件加载失败,请刷新页面后重试', 'danger'); return; } window.AksrtModalRuntime.openRemote(options); } function openBanModal(userId) { openRemoteModal({ url: API_ROOT + '/user-ban/modal', method: 'GET', query: { user_id: userId }, cache: false, height: MODAL_HEIGHT, mobileBottom: true, kind: 'user-ban', className: 'aksrt-user-ban-modal', onContent: function (html, session) { if (session && session.shell) bindBanModal(session.shell); }, }); } // 查看禁封状态弹窗(参考主题 zib_get_ban_info_modal / user_ban_info_modal)。 function openInfoModal(userId) { openRemoteModal({ url: API_ROOT + '/user-ban/info-modal', method: 'GET', query: { user_id: userId }, cache: false, height: MINI_MODAL_HEIGHT, mobileBottom: true, kind: 'user-ban-info', className: 'aksrt-user-ban-info-modal', }); } // 禁封申诉弹窗(参考主题 zib_get_user_ban_appeal_modal;表单走 admin-ajax)。 function openAppealModal(userId) { openRemoteModal({ url: API_ROOT + '/user-ban/appeal-modal', method: 'GET', query: { user_id: userId }, cache: false, height: MINI_MODAL_HEIGHT, mobileBottom: true, kind: 'user-ban-appeal', className: 'aksrt-user-ban-appeal-modal', onContent: function (html, session) { if (session && session.shell) bindAppealForm(session.shell); }, }); } // 禁封记录弹窗(参考主题 zib_get_ban_log_modal)。 function openLogModal(userId) { openRemoteModal({ url: API_ROOT + '/user-ban/log-modal', method: 'GET', query: { user_id: userId }, cache: false, height: MINI_MODAL_HEIGHT, mobileBottom: true, kind: 'user-ban-log', className: 'aksrt-user-ban-log-modal', }); } function bindAppealForm(shell) { var form = shell.querySelector('[data-aksrt-appeal-form]'); if (!form || form.dataset.aksrtAppealBound === '1') return; form.dataset.aksrtAppealBound = '1'; // autoHeight 仅对显式声明 autoHeight/maxHeight 的文本框生效: // 申诉说明框(1:1 参考主题)为 rows=3 自然高度 + 容器滚动, // 无 maxHeight 时执行 grow 会被压成 0px。 Array.prototype.forEach.call( form.querySelectorAll('textarea[autoHeight], textarea[maxHeight]'), bindAutoHeight ); form.addEventListener('submit', function (event) { event.preventDefault(); submitAppeal(form); }); } function submitAppeal(form) { var btn = form.querySelector('[data-aksrt-appeal-submit]'); if (btn) btn.disabled = true; notyf('正在提交申诉…', 'load', 3000, 'aksrt-appeal-submit'); var fd = new FormData(form); if (!fd.get('action')) fd.set('action', 'aksrt_ban_appeal_submit'); fetch(ajaxUrl(), { method: 'POST', body: fd, credentials: 'same-origin' }) .then(function (res) { return res.json(); }) .then(function (data) { if (data && !data.error) { notyf(data.msg || '申诉已提交', 'success'); if (data.reload) { window.setTimeout(function () { window.location.reload(); }, 400); } else if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.close === 'function') { window.AksrtModalRuntime.close('submit'); } } else { if (btn) btn.disabled = false; notyf((data && data.msg) || '提交失败', 'danger'); } }) .catch(function () { if (btn) btn.disabled = false; notyf('网络错误,请重试', 'danger'); }) .then(function () { var toast = document.getElementById('aksrt-appeal-submit'); if (toast) toast.remove(); }); } function collectPayload(shell, userId, unban) { var payload = { user_id: userId, ban: unban ? 0 : 1 }; if (unban) { var descInput = shell.querySelector('[data-aksrt-ban-form] [name="desc"]'); payload.desc = (descInput && descInput.value || '').trim(); return payload; } var form = shell.querySelector('[data-aksrt-ban-form]'); if (!form) return payload; var banInput = form.querySelector('input[name="ban"]:checked'); payload.ban = banInput ? parseInt(banInput.value, 10) : 1; var timeInput = form.querySelector('input[name="time_day"]:checked'); payload.time_day = timeInput ? parseInt(timeInput.value, 10) : 7; var reasonInput = form.querySelector('input[name="reason"]:checked'); payload.reason = reasonInput ? reasonInput.value : ''; var otherInput = form.querySelector('[name="reason_other"]'); payload.reason_other = (otherInput && otherInput.value || '').trim(); var desc = form.querySelector('[name="desc"]'); payload.desc = (desc && desc.value || '').trim(); payload.delete_content = !!(form.querySelector('[name="delete_content"]') || {}).checked; payload.no_appeal = !!(form.querySelector('[name="no_appeal"]') || {}).checked; var noAppealDesc = form.querySelector('[name="no_appeal_desc"]'); payload.no_appeal_desc = (noAppealDesc && noAppealDesc.value || '').trim(); return payload; } function submitBan(shell, userId, unban) { var payload = collectPayload(shell, userId, unban); if (!unban) { var reason = payload.reason === 'other' || !payload.reason ? payload.reason_other : payload.reason; if (!reason) { notyf('请选择或输入禁封原因', 'danger'); return; } if (payload.no_appeal && !payload.no_appeal_desc) { notyf('请输入禁止用户申诉的说明或原因', 'danger'); return; } } var submitBtn = shell.querySelector(unban ? '[data-aksrt-unban-submit]' : '[data-aksrt-ban-submit]'); if (submitBtn) submitBtn.disabled = true; notyf(unban ? '正在解封…' : '正在提交禁封…', 'load', 3000, 'aksrt-ban-submit'); window .fetch(API_ROOT + '/user-ban/set', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': NONCE }, body: JSON.stringify(payload), }) .then(function (res) { return res.json(); }) .then(function (result) { if (result && result.success) { notyf(result.message || '操作成功', 'success'); // 参考主题 zib_ajax_set_user_ban 返回值语义: // 禁封 → reload:true(整页刷新,入口按钮状态随页面重渲染); // 解封 → hide_modal:true(仅关闭弹窗)。 if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.close === 'function') { window.AksrtModalRuntime.close('submit'); } if (!unban) window.location.reload(); } else { if (submitBtn) submitBtn.disabled = false; notyf((result && result.message) || '操作失败', 'danger'); } }) .catch(function () { if (submitBtn) submitBtn.disabled = false; notyf('网络错误,请重试', 'danger'); }) .then(function () { var toast = document.getElementById('aksrt-ban-submit'); if (toast) toast.remove(); }); } function bindAutoHeight(textarea) { // 1:1 复刻参考主题 textarea[autoHeight] 行为(input/focus 触发): // 高度 = scrollHeight + 上下边框;超过 maxHeight 属性则锁定 maxHeight 并 // 打开自身滚动条(参考主题 main.js autoHeight 同语义)。 if (!textarea || textarea.dataset.aksrtAutoHeight === '1') return; textarea.dataset.aksrtAutoHeight = '1'; var grow = function () { var style = getComputedStyle(textarea); var border = (parseInt(style.getPropertyValue('border-top-width'), 10) || 0) + (parseInt(style.getPropertyValue('border-bottom-width'), 10) || 0); var max = parseInt(textarea.getAttribute('maxHeight'), 10) || 0; textarea.style.height = 'auto'; if (max > textarea.scrollHeight) { textarea.style.height = textarea.scrollHeight + border + 'px'; textarea.style.overflow = 'hidden'; } else { textarea.style.height = max + 'px'; textarea.style.overflow = 'auto'; } }; textarea.addEventListener('input', grow); textarea.addEventListener('focus', grow); if (textarea.offsetHeight > 0) grow(); } function syncBanForm(shell) { var form = shell.querySelector('[data-aksrt-ban-form]'); if (!form || form.getAttribute('data-aksrt-ban-mode') !== 'ban') return; var banInput = form.querySelector('input[name="ban"]:checked'); var ban = banInput ? banInput.value : '1'; Array.prototype.forEach.call(form.querySelectorAll('[data-aksrt-ban-desc]'), function (el) { el.style.display = el.getAttribute('data-aksrt-ban-desc') === ban ? '' : 'none'; }); var reasonInput = form.querySelector('input[name="reason"]:checked'); var otherBox = form.querySelector('[data-aksrt-ban-reason-other]'); if (otherBox) otherBox.style.display = reasonInput && reasonInput.value === 'other' ? '' : 'none'; var noAppealBox = form.querySelector('[data-aksrt-ban-noappeal-desc]'); var noAppealInput = form.querySelector('[name="no_appeal"]'); if (noAppealBox) noAppealBox.style.display = noAppealInput && noAppealInput.checked ? '' : 'none'; } function bindBanModal(shell) { Array.prototype.forEach.call(shell.querySelectorAll('textarea.form-control'), bindAutoHeight); syncBanForm(shell); } /** * 收起封面三点菜单面板(参考主题 author_header_drop_lists 菜单项点击后 * 不自动收起,需手动关闭;评论区与文章作者下拉由各自脚本点击项后自动收起)。 */ function closeCoverMenu(trigger) { var menu = trigger && trigger.closest ? trigger.closest('[data-aksrt-menu]') : null; if (!menu) return; var panel = menu.querySelector('.aksrt-cover-menu__panel'); if (panel) panel.hidden = true; var menuTrigger = menu.querySelector('.aksrt-cover-menu__trigger'); if (menuTrigger) menuTrigger.setAttribute('aria-expanded', 'false'); } function initBanBindings() { if (document.documentElement.dataset.aksrtBanInitialized === '1') return; document.documentElement.dataset.aksrtBanInitialized = '1'; document.addEventListener('click', function (event) { if (!event.target || !event.target.closest) return; // 禁封 / 解封入口(封面菜单按钮 / 评论区菜单项 / 文章作者菜单项)。 var banBtn = event.target.closest('[data-aksrt-ban]'); if (banBtn) { event.preventDefault(); var userId = parseInt(banBtn.getAttribute('data-aksrt-ban'), 10); if (userId > 0) { closeCoverMenu(banBtn); openBanModal(userId); } return; } // 查看禁封状态入口(用户中心元信息 / 评论拒绝态 / 禁封信息弹窗内申诉按钮)。 var infoBtn = event.target.closest('[data-aksrt-ban-info]'); if (infoBtn) { event.preventDefault(); var infoUser = parseInt(infoBtn.getAttribute('data-aksrt-ban-info'), 10); if (infoUser > 0) openInfoModal(infoUser); return; } // 申诉入口(禁封信息弹窗 / 登录后禁封页)。 var appealBtn = event.target.closest('[data-aksrt-ban-appeal]'); if (appealBtn) { event.preventDefault(); var appealUser = parseInt(appealBtn.getAttribute('data-aksrt-ban-appeal'), 10); if (appealUser > 0) openAppealModal(appealUser); return; } // 禁封记录入口(禁封/解封弹窗内的记录链接,仅管理员)。 var logBtn = event.target.closest('[data-aksrt-ban-log]'); if (logBtn) { event.preventDefault(); var logUser = parseInt(logBtn.getAttribute('data-aksrt-ban-log'), 10); if (logUser > 0) openLogModal(logUser); return; } // 弹窗内交互(事件委托到 document,弹窗壳由 runtime portal 到 body)。 var active = window.AksrtModalRuntime && window.AksrtModalRuntime.getActive ? window.AksrtModalRuntime.getActive() : null; if (!active || !active.contains(event.target)) return; var shell = active; var form = shell.querySelector('[data-aksrt-ban-form]'); var modalUser = form ? parseInt(form.getAttribute('data-aksrt-ban-user'), 10) : 0; var submitBtn = event.target.closest('[data-aksrt-ban-submit]'); if (submitBtn) { event.preventDefault(); submitBtn.disabled = true; submitBan(shell, modalUser, false); return; } var unbanBtn = event.target.closest('[data-aksrt-unban-submit]'); if (unbanBtn) { event.preventDefault(); unbanBtn.disabled = true; submitBan(shell, modalUser, true); return; } }); document.addEventListener('change', function (event) { var active = window.AksrtModalRuntime && window.AksrtModalRuntime.getActive ? window.AksrtModalRuntime.getActive() : null; if (!active || !event.target || !active.contains(event.target)) return; syncBanForm(active); }); // 弹窗内容注入后由 onContent 绑定;bs.modal 展示契约下对 user-ban 弹窗做同步。 jQuery(document).on('loaded.bs.modal show.bs.modal', function (event) { var shell = event.target && event.target.classList && event.target.classList.contains('aksrt-user-ban-modal') ? event.target : null; if (shell) syncBanForm(shell); }); } jQuery(document) .off('auto_fun.aksrt-ban') .on('auto_fun.aksrt-ban', initBanBindings); })();