Merge pull request 'fix(users): keep management inside center' (#5) from worktree/fix-user-view into main

Reviewed-on: #5
This commit is contained in:
2026-08-12 13:01:56 +00:00
44 changed files with 194 additions and 2584 deletions
@@ -36,16 +36,6 @@ export const extensionsSection = {
canonicalDomain: 'extensions',
section: 'push',
}),
schemaPage({
key: 'extensions-docs-mode',
name: 'ExtensionsDocsMode',
label: '文档模式',
route: '/extensions/docs-mode',
title: '文档模式',
desc: '文档专题内容模式与页面增强。',
canonicalDomain: 'extensions',
section: 'docs-mode',
}),
customPage({
key: 'extensions-security',
name: 'ExtensionsSecurity',
-129
View File
@@ -2,43 +2,6 @@
<div class="aksrt-page-container">
<PageHeader title="文档中心" desc="主题使用文档、开发指南、迁移说明和更新日志。" />
<!-- Docs Mode Status -->
<n-card class="aksrt-docs__section" size="small">
<div class="aksrt-docs__mode-status">
<div class="aksrt-docs__mode-info">
<FaIcon :icon="docsModeEnabled ? 'fas fa-book-open' : 'fas fa-book'" :size="18" :color="docsModeEnabled ? '#52c41a' : '#8a909c'" />
<div>
<strong>{{ docsModeEnabled ? '文档模式已启用' : '文档模式未启用' }}</strong>
<p>{{ docsModeEnabled ? '文档导航页面和文档站式侧边栏已激活' : '启用后将激活文档导航页面模板和文档站式侧边栏' }}</p>
</div>
</div>
<n-space>
<router-link to="/extensions/docs-mode" class="aksrt-docs__mode-link">
<FaIcon icon="fas fa-cog" :size="12" />
{{ docsModeEnabled ? '管理文档模式' : '启用文档模式' }}
</router-link>
</n-space>
</div>
</n-card>
<!-- Doc Pages Overview -->
<n-card title="文档页面" size="small" class="aksrt-docs__section">
<n-alert v-if="docPageCount === 0" type="info" size="small">
尚未创建文档页面。在 WordPress 后台新建页面时,选择"文档导航页"或"专题聚合页"模板。
</n-alert>
<n-grid v-else :cols="3" :x-gap="12" :y-gap="12">
<n-grid-item v-for="stat in docPageStats" :key="stat.key">
<div class="aksrt-docs__stat">
<FaIcon :icon="stat.icon" :size="16" :color="stat.color" />
<div>
<strong>{{ stat.value }}</strong>
<p>{{ stat.label }}</p>
</div>
</div>
</n-grid-item>
</n-grid>
</n-card>
<!-- Quick Actions -->
<n-card title="常用操作" size="small" class="aksrt-docs__section">
<n-grid :cols="2" :x-gap="12" :y-gap="12">
@@ -130,13 +93,6 @@ import api from '@/api/index.js'
const themeVersion = ref(window.aksrtAdminData?.themeVersion || '3.4.0-dev')
const dbSchemaVersion = ref('—')
const docsModeEnabled = ref(false)
const docPageCount = ref(0)
const docPageStats = ref([
{ key: 'total', icon: 'fas fa-file-alt', color: '#1890ff', value: 0, label: '文档页面' },
{ key: 'nav', icon: 'fas fa-sitemap', color: '#722ed1', value: 0, label: '导航页' },
{ key: 'special', icon: 'fas fa-layer-group', color: '#fa8c16', value: 0, label: '专题页' },
])
const docs = ref([
{ key: 'admin-ia-map', title: '后台 IA 蓝图', description: '完整的后台信息架构映射,12 个顶级分区及子页面清单。', icon: 'fas fa-sitemap', color: '#1890ff', file: 'admin-ia-map.md' },
@@ -148,28 +104,6 @@ const docs = ref([
])
onMounted(async () => {
try {
const res = await api.getSchemaPage('extensions', 'docs-mode')
if (res?.data) {
docsModeEnabled.value = !!res.data.docs_mode_enabled
}
} catch {
// Schema endpoint may not exist yet
}
// Fetch doc page stats via REST if available
try {
const statsRes = await api.request('/docs/stats')
if (statsRes?.data) {
docPageCount.value = statsRes.data.total || 0
docPageStats.value[0].value = statsRes.data.total || 0
docPageStats.value[1].value = statsRes.data.nav_count || 0
docPageStats.value[2].value = statsRes.data.special_count || 0
}
} catch {
// Endpoint may not exist yet
}
// Fetch schema version from health
try {
const healthRes = await api.getHealthCheck()
@@ -189,69 +123,6 @@ function openDoc(doc) {
</script>
<style scoped>
.aksrt-docs__mode-status {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.aksrt-docs__mode-info {
display: flex;
align-items: center;
gap: 12px;
}
.aksrt-docs__mode-info strong {
display: block;
font-size: 14px;
color: #1a1a2e;
}
.aksrt-docs__mode-info p {
margin: 2px 0 0;
font-size: 12px;
color: #8a909c;
}
.aksrt-docs__mode-link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border-radius: 6px;
background: #f5f7fa;
color: #1890ff;
text-decoration: none;
font-size: 13px;
transition: background 0.2s;
}
.aksrt-docs__mode-link:hover {
background: #e6f4ff;
}
.aksrt-docs__stat {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
border-radius: 6px;
background: #fafafa;
}
.aksrt-docs__stat strong {
display: block;
font-size: 20px;
color: #1a1a2e;
}
.aksrt-docs__stat p {
margin: 0;
font-size: 12px;
color: #8a909c;
}
.aksrt-docs__grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
@@ -1,311 +0,0 @@
<template>
<div class="aksrt-page-container">
<PageHeader title="文档模式" desc="启用文档专题内容模式,配置文档导航页面和文档站式体验。" />
<n-card class="aksrt-docs-mode__card" title="文档模式" size="small">
<n-form label-placement="left" label-width="160">
<n-form-item label="启用文档模式">
<n-switch v-model:value="formData.docs_mode_enabled" />
<n-text depth="3" style="margin-left: 12px; font-size: 12px;">
开启后将激活文档导航页面模板和文档站式侧边栏
</n-text>
</n-form-item>
<n-form-item v-if="formData.docs_mode_enabled" label="文档首页模板">
<n-select
v-model:value="formData.docs_mode_default_template"
:options="templateOptions"
placeholder="选择文档首页模板"
style="max-width: 360px;"
/>
</n-form-item>
<n-form-item v-if="formData.docs_mode_enabled" label="文档分类">
<n-select
v-model:value="formData.docs_mode_cats"
:options="categoryOptions"
:loading="categoriesLoading"
multiple
clearable
filterable
placeholder="选择用于文档模式的分类"
style="max-width: 520px;"
/>
</n-form-item>
<n-form-item v-if="formData.docs_mode_enabled" label="首页排除">
<n-switch v-model:value="formData.docs_mode_exclude" />
<n-text depth="3" style="margin-left: 12px; font-size: 12px;">
开启后,所选文档分类的内容不会出现在首页常规文章流中
</n-text>
</n-form-item>
<n-form-item v-if="formData.docs_mode_enabled" label="显示目录树">
<n-switch v-model:value="formData.docs_mode_toc_enabled" />
<n-text depth="3" style="margin-left: 12px; font-size: 12px;">
在文档页面侧边栏显示章节目录树
</n-text>
</n-form-item>
<n-form-item v-if="formData.docs_mode_enabled" label="显示章节导航">
<n-switch v-model:value="formData.docs_mode_chapter_nav_enabled" />
<n-text depth="3" style="margin-left: 12px; font-size: 12px;">
在文档底部显示上一章/下一章导航
</n-text>
</n-form-item>
<n-form-item v-if="formData.docs_mode_enabled" label="默认排序">
<n-radio-group v-model:value="formData.docs_mode_default_order">
<n-radio value="menu_order">按菜单顺序</n-radio>
<n-radio value="date">按发布时间</n-radio>
<n-radio value="title">按标题排序</n-radio>
</n-radio-group>
</n-form-item>
</n-form>
<template #action>
<n-space justify="end">
<n-button @click="handleReset">重置</n-button>
<n-button type="primary" :loading="saving" @click="handleSave">保存设置</n-button>
</n-space>
</template>
</n-card>
<n-card title="文档页面统计" size="small" class="aksrt-docs-mode__card">
<n-grid :cols="3" :x-gap="12" :y-gap="12">
<n-grid-item>
<div class="aksrt-docs-mode__stat">
<strong>{{ docStats.total || 0 }}</strong>
<p>文档页面总数</p>
</div>
</n-grid-item>
<n-grid-item>
<div class="aksrt-docs-mode__stat">
<strong>{{ docStats.nav_count || 0 }}</strong>
<p>文档导航页</p>
</div>
</n-grid-item>
<n-grid-item>
<div class="aksrt-docs-mode__stat">
<strong>{{ docStats.special_count || 0 }}</strong>
<p>专题聚合页</p>
</div>
</n-grid-item>
</n-grid>
</n-card>
<n-card title="可用页面模板" size="small" class="aksrt-docs-mode__card">
<n-alert type="info" size="small" style="margin-bottom: 12px;">
以下页面模板可用于创建文档类页面。在 WordPress 后台新建页面时,从"页面属性 → 模板"中选择对应模板。
</n-alert>
<n-list bordered size="small">
<n-list-item v-for="tpl in availableTemplates" :key="tpl.file">
<n-thing :title="tpl.name" :description="tpl.description">
<template #avatar>
<FaIcon :icon="tpl.icon" :size="18" color="#1890ff" />
</template>
<template #header-extra>
<n-tag size="small" type="info">{{ tpl.file }}</n-tag>
</template>
</n-thing>
</n-list-item>
</n-list>
</n-card>
<n-card title="快速入口" size="small" class="aksrt-docs-mode__card">
<n-space>
<router-link to="/page-display/special" class="aksrt-docs-mode__link">
<FaIcon icon="fas fa-layer-group" :size="14" />
专题页面配置
</router-link>
</n-space>
</n-card>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useMessage } from 'naive-ui'
import PageHeader from '@/components/PageHeader.vue'
import FaIcon from '@/components/form/FaIcon.vue'
import api from '@/api/index.js'
const message = useMessage()
const saving = ref(false)
const docStats = ref({ total: 0, nav_count: 0, special_count: 0 })
const categoriesLoading = ref(false)
const categoryOptions = ref([])
const defaults = {
docs_mode_enabled: false,
docs_mode_default_template: 'documentnav',
docs_mode_toc_enabled: true,
docs_mode_chapter_nav_enabled: true,
docs_mode_default_order: 'menu_order',
docs_mode_cats: [],
docs_mode_exclude: true,
}
const formData = ref({ ...defaults })
const templateOptions = [
{ label: '文档导航页 (page-documentnav)', value: 'documentnav' },
{ label: '专题聚合页 (page-special)', value: 'special' },
]
const availableTemplates = [
{
file: 'page-documentnav.php',
name: '文档导航页',
description: '站式文档导航模板,支持侧边栏目录树、章节高亮和快速搜索。',
icon: 'fas fa-book',
},
{
file: 'page-special.php',
name: '专题聚合页',
description: '专题内容聚合模板,支持封面、摘要、多列布局和子页面矩阵。',
icon: 'fas fa-layer-group',
},
{
file: 'page-download.php',
name: '下载中心页',
description: '资源下载中心模板,支持资源类型筛选、权限说明和交易承接。',
icon: 'fas fa-download',
},
{
file: 'page-categories.php',
name: '分类导航页',
description: '全站分类导航模板,展示分类树和文章统计。',
icon: 'fas fa-folder-open',
},
{
file: 'page-friends.php',
name: '友情链接页',
description: '友情链接展示模板。',
icon: 'fas fa-link',
},
]
onMounted(async () => {
await Promise.all([loadSettings(), loadDocStats(), loadCategories()])
})
async function loadSettings() {
try {
const res = await api.getSchemaPage('extensions', 'docs-mode')
if (res?.data) {
formData.value = {
...defaults,
...res.data,
docs_mode_cats: normalizeCategoryIds(res.data.docs_mode_cats),
}
}
} catch {
// Schema endpoint may not exist yet; use defaults
}
}
async function loadDocStats() {
try {
const statsRes = await api.request('/docs/stats')
if (statsRes?.data) {
docStats.value = statsRes.data
}
} catch {
// Endpoint may not exist yet
}
}
async function loadCategories() {
categoriesLoading.value = true
try {
const res = await api.getCategories()
const items = res?.data || res || []
categoryOptions.value = (Array.isArray(items) ? items : []).map((item) => ({
label: item.name + (item.count ? ` (${item.count})` : ''),
value: Number(item.term_id),
}))
} catch {
categoryOptions.value = []
} finally {
categoriesLoading.value = false
}
}
function normalizeCategoryIds(value) {
if (Array.isArray(value)) {
return value.map((item) => Number(item)).filter((item) => Number.isFinite(item) && item > 0)
}
if (typeof value === 'string' && value.trim()) {
return value
.split(',')
.map((item) => Number(item.trim()))
.filter((item) => Number.isFinite(item) && item > 0)
}
return []
}
async function handleSave() {
saving.value = true
try {
await api.saveSchemaPage('extensions', 'docs-mode', {
...formData.value,
docs_mode_cats: normalizeCategoryIds(formData.value.docs_mode_cats),
})
message.success('文档模式设置已保存')
} catch (err) {
message.error('保存失败: ' + (err?.message || '未知错误'))
} finally {
saving.value = false
}
}
function handleReset() {
formData.value = { ...defaults }
message.info('已重置为默认值')
}
</script>
<style scoped>
.aksrt-docs-mode__card {
margin-bottom: 16px;
}
.aksrt-docs-mode__stat {
text-align: center;
padding: 12px;
border-radius: 6px;
background: #fafafa;
}
.aksrt-docs-mode__stat strong {
display: block;
font-size: 24px;
color: #1a1a2e;
}
.aksrt-docs-mode__stat p {
margin: 4px 0 0;
font-size: 12px;
color: #8a909c;
}
.aksrt-docs-mode__link {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border-radius: 6px;
background: #f5f7fa;
color: #1890ff;
text-decoration: none;
font-size: 13px;
transition: background 0.2s;
}
.aksrt-docs-mode__link:hover {
background: #e6f4ff;
}
</style>
@@ -84,7 +84,6 @@ const updateInfo = ref({
const guides = [
{ key: 'rewrite', title: '伪静态配置', desc: '检查固定链接与重写规则', url: '/wp-admin/options-permalink.php' },
{ key: 'docs-mode', title: '文档模式', desc: '配置文档分类与专题模板', url: '#/extensions/docs-mode' },
{ key: 'oauth', title: '第三方登录', desc: '检查 GitHub / Gitee / QQ / Google 登录', url: '#/users/oauth' },
{ key: 'backup', title: '设置快照', desc: '创建与恢复站点配置快照', url: '#/tools/backup' },
{ key: 'health', title: '健康检查', desc: '查看 PHP / WP / 数据表状态', url: '#/tools/health' },
@@ -1,125 +0,0 @@
export const docsModeSchema = {
key: 'extensions.docs-mode',
title: '文档模式',
icon: 'fas fa-book-open',
description: '启用文档专题内容模式,配置文档导航页面和文档站式体验。',
defaults: {
docs_mode_enabled: false,
docs_mode_default_template: 'documentnav',
docs_mode_toc_enabled: true,
docs_mode_chapter_nav_enabled: true,
docs_mode_default_order: 'menu_order',
docs_mode_cats: [],
docs_mode_exclude: true,
docs_mode_toc_depth: 3,
docs_mode_chapter_title: '',
docs_mode_style: 'default',
docs_mode_icon: true,
docs_mode_count: true,
},
groups: [
{
key: 'basic',
title: '基础配置',
icon: 'fas fa-cog',
fields: [
{ key: 'docs_mode_enabled', label: '启用文档模式', type: 'switch', default: false, help: '开启后将激活文档导航页面模板和文档站式侧边栏' },
{
key: 'docs_mode_default_template',
label: '文档首页模板',
type: 'select',
default: 'documentnav',
options: [
{ label: '文档导航页', value: 'documentnav' },
{ label: '专题聚合页', value: 'special' },
],
dependsOn: { key: 'docs_mode_enabled', value: true },
},
],
},
{
key: 'navigation',
title: '导航设置',
icon: 'fas fa-compass',
fields: [
{ key: 'docs_mode_toc_enabled', label: '显示目录树', type: 'switch', default: true, dependsOn: { key: 'docs_mode_enabled', value: true }, help: '在文档页面侧边栏显示章节目录树' },
{ key: 'docs_mode_chapter_nav_enabled', label: '显示章节导航', type: 'switch', default: true, dependsOn: { key: 'docs_mode_enabled', value: true }, help: '在文档底部显示上一章/下一章导航' },
{
key: 'docs_mode_default_order',
label: '默认排序',
type: 'radio',
default: 'menu_order',
options: [
{ label: '按菜单顺序', value: 'menu_order' },
{ label: '按发布时间', value: 'date' },
{ label: '按标题排序', value: 'title' },
],
dependsOn: { key: 'docs_mode_enabled', value: true },
},
{
key: 'docs_mode_toc_depth',
label: '目录深度',
type: 'number',
default: 3,
min: 1,
max: 6,
dependsOn: { key: 'docs_mode_enabled', value: true },
},
{
key: 'docs_mode_chapter_title',
label: '章节标题',
type: 'input',
default: '',
dependsOn: { key: 'docs_mode_enabled', value: true },
},
],
},
{
key: 'filter',
title: '筛选设置',
icon: 'fas fa-filter',
fields: [
{
key: 'docs_mode_cats',
label: '文档分类',
type: 'taxonomy_select',
taxonomy: 'category',
multiple: true,
clearable: true,
default: [],
placeholder: '选择用于文档模式的分类',
dependsOn: { key: 'docs_mode_enabled', value: true },
},
{
key: 'docs_mode_exclude',
label: '在首页排除此类内容',
type: 'switch',
default: true,
dependsOn: { key: 'docs_mode_enabled', value: true },
help: '开启后,所选文档分类的内容不会出现在首页常规文章流中。',
},
],
},
{
key: 'style',
title: '样式设置',
icon: 'fas fa-palette',
fields: [
{
key: 'docs_mode_style',
label: '文档样式',
type: 'select',
default: 'default',
options: [
{ label: '默认', value: 'default' },
{ label: '简洁', value: 'simple' },
{ label: '卡片', value: 'card' },
],
dependsOn: { key: 'docs_mode_enabled', value: true },
},
{ key: 'docs_mode_icon', label: '显示图标', type: 'switch', default: true, dependsOn: { key: 'docs_mode_enabled', value: true } },
{ key: 'docs_mode_count', label: '显示文章数', type: 'switch', default: true, dependsOn: { key: 'docs_mode_enabled', value: true } },
],
},
],
}
@@ -6,11 +6,9 @@
import { ipSchema } from './ip.js'
import { auditSchema } from './audit.js'
import { pushSchema } from './push.js'
import { docsModeSchema } from './docs-mode.js'
export const extensionsSchemas = {
ip: ipSchema,
audit: auditSchema,
push: pushSchema,
'docs-mode': docsModeSchema,
}
-240
View File
@@ -1,240 +0,0 @@
/**
* Document navigation page (zibll documentnav) styles.
*
* @package AKSRT
*/
.page-template-documentnav .document-nav-row {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin: 0;
}
.page-template-documentnav .document-nav-aside {
flex: 0 0 32%;
max-width: 100%;
min-width: 220px;
}
.page-template-documentnav .document-nav-main {
flex: 1 1 0;
min-width: 0;
}
.document-nav .panel-group {
margin: 0;
}
.document-nav .panel {
border: none;
box-shadow: none;
border-top: 1px solid var(--main-border-color, #eee);
}
.document-nav .panel-title {
margin: 0;
font-size: 1em;
}
.document-nav .panel-toggle {
padding: 10px;
display: block;
color: inherit;
text-decoration: none;
}
.document-nav .panel-toggle:not(.collapsed) {
color: var(--focus-color, var(--color-primary));
}
.document-nav .panel-toggle .fa {
transition: transform 0.2s;
}
.document-nav .panel-toggle:not(.collapsed) .fa {
transform: rotate(90deg);
}
.document-nav .panel-collapse {
display: none;
}
.document-nav .panel-collapse.is-open,
.document-nav .panel-collapse.in {
display: block;
}
.document-nav .panel-child,
.document-nav .category {
padding-left: 15px;
}
.document-nav .cat-load {
padding: 10px;
display: block;
color: inherit;
text-decoration: none;
position: relative;
}
.document-nav > .panel-group > .cat-load {
padding-left: 2px;
}
.document-nav > .panel-group > .cat-load::before {
content: '';
width: 0.5em;
height: 0.5em;
border: 0.1em solid var(--focus-color, var(--color-primary));
border-radius: 1em;
margin: 0 0.5em;
vertical-align: 0.1em;
display: inline-block;
}
.document-nav .cat-load.active {
background: var(--main-border-color, rgba(0, 0, 0, 0.06));
color: var(--focus-color, var(--color-primary));
border-radius: 4px;
}
.document-nav .loading {
overflow: hidden;
width: 0;
opacity: 0;
transition: 0.3s;
display: inline-block;
}
.document-nav .is-loading .loading,
.document-nav .cat-load.is-loading .loading {
width: 0.7em;
opacity: 1;
margin-right: 6px;
}
.document-nav-container {
transition: 0.3s;
min-height: 120px;
}
.document-nav-container.is-loading {
opacity: 0.55;
pointer-events: none;
}
.document-search {
position: relative;
margin-bottom: 16px;
padding: 12px;
}
.document-search .line-form {
position: relative;
}
.document-search .line-form-input {
width: 100%;
border: 1px solid var(--main-border-color, #ddd);
border-radius: 8px;
padding: 10px 40px 10px 12px;
box-sizing: border-box;
background: var(--main-bg-color, #fff);
}
.document-search .abs-right {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
border: 0;
background: transparent;
cursor: pointer;
color: var(--muted-2-color, #888);
}
.document-search .search-loading {
inset: 0;
display: none;
align-items: center;
justify-content: center;
border-radius: 8px;
background: color-mix(in srgb, var(--main-bg-color, #fff) 88%, transparent);
z-index: 2;
}
.document-search.is-loading .search-loading {
display: flex;
}
.list-group {
border-radius: 8px;
overflow: hidden;
}
.list-group-item {
display: block;
padding: 12px 14px;
background: var(--main-bg-color, #fff);
border-bottom: 1px solid var(--muted-border-color, #eee);
color: var(--main-color, inherit);
text-decoration: none;
}
.list-group-item:last-child {
border-bottom: 0;
}
a.list-group-item:hover,
a.list-group-item:focus {
background: var(--body-bg-color, #f7f7f8);
color: var(--focus-color, var(--color-primary));
}
.list-group-item .pull-right {
float: right;
}
.list-group-item .badg-sm {
font-size: 12px;
opacity: 0.75;
}
.list-group-item .focus-color {
color: var(--focus-color, var(--color-primary));
}
.document-nav-container .pagenav a {
display: inline-block;
min-width: 32px;
padding: 4px 8px;
margin: 2px;
border-radius: 4px;
text-decoration: none;
color: inherit;
background: var(--surface-2, #f0f0f0);
}
.document-nav-container .pagenav a.current {
background: var(--focus-color, var(--color-primary));
color: #fff;
}
.history-search .search_keywords {
display: inline-block;
margin: 0 6px 6px 0;
padding: 2px 8px;
border-radius: 999px;
background: var(--surface-2, #f0f0f0);
color: inherit;
text-decoration: none;
font-size: 12px;
}
@media (max-width: 767px) {
.page-template-documentnav .document-nav-aside,
.page-template-documentnav .document-nav-main {
flex: 1 1 100%;
}
}
+9
View File
@@ -701,6 +701,15 @@ body.aksrt-user-center .uc-content,
gap: 8px;
}
.uc-follow-management__list { display: grid; gap: 10px; margin-top: 16px; }
.uc-follow-management__item { display: flex; align-items: center; gap: 12px; padding: 12px; border: 1px solid var(--border-color, rgba(15, 23, 42, .08)); border-radius: 12px; }
.uc-follow-management__avatar img { display: block; width: 48px; height: 48px; border-radius: 50%; }
.uc-follow-management__identity { display: grid; gap: 4px; min-width: 0; }
.uc-follow-management__identity a { color: var(--color-ink-1); font-weight: 600; }
.uc-follow-management__identity span { color: var(--color-ink-4); font-size: 12px; }
.uc-follow-management__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-left: auto; }
@media (max-width: 560px) { .uc-follow-management__item { align-items: flex-start; flex-wrap: wrap; } .uc-follow-management__actions { width: 100%; margin-left: 60px; } }
.uc-service-grid__item {
display: flex;
flex-direction: column;
+6
View File
@@ -994,6 +994,8 @@
var nav = document.querySelectorAll('[data-aksrt-asset-tab]');
if (!nav.length) return;
nav.forEach(function (link) {
if (link.dataset.aksrtAssetTabBound === 'true') return;
link.dataset.aksrtAssetTabBound = 'true';
link.addEventListener('click', function (e) {
e.preventDefault();
var key = link.getAttribute('data-aksrt-asset-tab');
@@ -1054,4 +1056,8 @@
} else {
bootAssets();
}
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('credits', function () { initAssetTabs(); });
}
})();
-197
View File
@@ -1,197 +0,0 @@
/**
* Document navigation AJAX (zibll page-template documentnav subset).
*
* @package AKSRT
*/
(function () {
'use strict';
function ajaxUrl() {
if (window.aksrtData && window.aksrtData.ajaxUrl) return window.aksrtData.ajaxUrl;
return '/wp-admin/admin-ajax.php';
}
function toast(msg, type) {
if (window.aksrt && typeof window.aksrt.toast === 'function') {
window.aksrt.toast(msg, type || 'info');
return;
}
// eslint-disable-next-line no-alert
window.alert(msg);
}
function qs(sel, root) {
return (root || document).querySelector(sel);
}
function qsa(sel, root) {
return Array.prototype.slice.call((root || document).querySelectorAll(sel));
}
function documentAjax(triggerEl, data, url) {
var container = qs('.document-nav-container');
var searchBox = qs('.document-search');
if (!container) return;
container.classList.add('is-loading');
if (searchBox && data && data.search) searchBox.classList.add('is-loading');
data = data || {};
data.one_cat = container.getAttribute('data-one-cat') || '';
data.action = 'aksrt_documentnav_posts';
var body = new URLSearchParams();
Object.keys(data).forEach(function (k) {
if (data[k] !== undefined && data[k] !== null) body.append(k, data[k]);
});
fetch(url || ajaxUrl(), {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: body.toString(),
})
.then(function (r) { return r.json(); })
.then(function (n) {
if (!n || n.error || !n.html) {
toast((n && n.msg) || '加载失败,请重试', 'danger');
}
if (n && n.html) container.innerHTML = n.html;
if (n && n.history_search) {
var hist = qs('.history-search > div');
if (hist) hist.innerHTML = n.history_search;
}
})
.catch(function () {
toast('网络错误', 'danger');
})
.finally(function () {
container.classList.remove('is-loading');
if (searchBox) searchBox.classList.remove('is-loading');
if (triggerEl && triggerEl.classList) triggerEl.classList.remove('is-loading');
});
}
function initAccordion() {
var nav = qs('.document-nav');
if (!nav) return;
nav.addEventListener('click', function (e) {
var toggle = e.target.closest('.panel-toggle[data-toggle="collapse"]');
if (!toggle) return;
e.preventDefault();
var targetSel = toggle.getAttribute('data-target');
var panel = targetSel ? qs(targetSel, nav) : null;
if (!panel) return;
var parentSel = toggle.getAttribute('data-parent');
if (parentSel) {
var parent = qs(parentSel, nav);
if (parent) {
qsa('.panel-collapse.is-open, .panel-collapse.in', parent).forEach(function (p) {
if (p !== panel) {
p.classList.remove('is-open', 'in');
var t = parent.querySelector('[data-target="#' + p.id + '"]');
if (t) t.classList.add('collapsed');
}
});
}
}
var open = panel.classList.contains('is-open') || panel.classList.contains('in');
if (open) {
panel.classList.remove('is-open', 'in');
toggle.classList.add('collapsed');
} else {
panel.classList.add('is-open', 'in');
toggle.classList.remove('collapsed');
}
});
}
function initSearch() {
var form = qs('.document-search-form');
if (!form) return;
form.addEventListener('submit', function (e) {
e.preventDefault();
var input = form.querySelector('[name="s"]');
var search = input ? String(input.value || '').trim() : '';
if (!search.length) {
toast('请输入搜索关键词', 'warning');
return;
}
if (search.length < 2) {
toast('关键词太短', 'warning');
return;
}
qsa('.cat-load').forEach(function (el) {
el.classList.remove('is-loading', 'active');
});
documentAjax(qs('.document-search'), { search: search });
});
document.addEventListener('click', function (e) {
var kw = e.target.closest('.document-search .search_keywords');
if (!kw) return;
e.preventDefault();
var text = (kw.textContent || '').trim();
var input = qs('.document-search-form [name="s"]');
if (input) {
input.value = text;
input.dispatchEvent(new Event('input', { bubbles: true }));
}
if (form) form.dispatchEvent(new Event('submit', { cancelable: true, bubbles: true }));
});
}
function initCatLoad() {
document.addEventListener('click', function (e) {
var a = e.target.closest('.cat-load');
if (!a) return;
e.preventDefault();
qsa('.cat-load').forEach(function (el) {
el.classList.remove('is-loading', 'active');
});
a.classList.add('is-loading', 'active');
var catId = a.getAttribute('data-cat-id') || a.getAttribute('cat-id') || '';
documentAjax(a, { cat_id: catId });
});
}
function initPager() {
document.addEventListener('click', function (e) {
var a = e.target.closest('.document-nav-container .pagenav a');
if (!a) return;
e.preventDefault();
var paged = a.getAttribute('data-paged') || '';
var href = a.getAttribute('href') || '';
if (paged) {
// Prefer current context from last active cat or search.
var active = qs('.cat-load.active');
var searchInput = qs('.document-search-form [name="s"]');
var data = { paged: paged };
if (active) {
data.cat_id = active.getAttribute('data-cat-id') || '';
} else if (searchInput && searchInput.value) {
data.search = searchInput.value.trim();
}
documentAjax(a, data);
return;
}
if (href && href.indexOf('admin-ajax.php') !== -1) {
documentAjax(a, {}, href);
}
});
}
function init() {
if (!qs('.document-nav-container') && !qs('.document-nav')) return;
initAccordion();
initSearch();
initCatLoad();
initPager();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
+4
View File
@@ -97,6 +97,10 @@
btn.title = '关注该用户';
}
if (!isFollowing && btn.closest('.uc-follow-management__item')) {
btn.closest('.uc-follow-management__item').remove();
}
// Update follower count display if exists
var countEl = document.querySelector('.aksrt-user-home__stat-value[data-followers]');
if (countEl && count !== undefined) {
-221
View File
@@ -1,221 +0,0 @@
/**
* AKSRT 网址导航页 — search tabs, sticky cat nav, submit modal/AJAX.
*
* @package AKSRT
*/
(function () {
'use strict';
function qs(sel, root) {
return (root || document).querySelector(sel);
}
function qsa(sel, root) {
return Array.prototype.slice.call((root || document).querySelectorAll(sel));
}
function ajaxUrl() {
if (window.aksrtData && window.aksrtData.ajaxUrl) return window.aksrtData.ajaxUrl;
if (window.ajaxurl) return window.ajaxurl;
return '/wp-admin/admin-ajax.php';
}
function toast(msg, type) {
if (window.aksrt && typeof window.aksrt.toast === 'function') {
window.aksrt.toast(msg, type || 'info');
return;
}
if (window.notyf) {
try {
window.notyf(msg, type === 'danger' ? 'danger' : '');
return;
} catch (e) { /* ignore */ }
}
// eslint-disable-next-line no-alert
window.alert(msg);
}
/* ---------- search engine tabs ---------- */
function initSearchTabs() {
var cover = qs('.link-page-search-cover');
if (!cover) return;
qsa('.tab-nav-theme a[data-aksrt-tab-target]', cover).forEach(function (a) {
a.addEventListener('click', function (e) {
e.preventDefault();
var target = a.getAttribute('data-aksrt-tab-target');
if (!target) return;
qsa('.tab-nav-theme li', cover).forEach(function (li) {
li.classList.remove('active');
});
if (a.parentElement) a.parentElement.classList.add('active');
qsa('.tab-pane', cover).forEach(function (pane) {
pane.classList.remove('active', 'in');
});
var pane = qs(target, cover);
if (pane) pane.classList.add('active', 'in');
});
});
}
/* ---------- sticky category nav highlight ---------- */
function initCatNav() {
var wrap = qs('.links-page-container .fixed-wrap');
if (!wrap) return;
var nav = qs('.link-nav-box', wrap);
var links = qsa('.link-nav-box a[href^="#"]', wrap);
var sections = links
.map(function (a) {
var id = a.getAttribute('href');
return id ? document.getElementById(id.replace(/^#/, '')) : null;
})
.filter(Boolean);
if (!links.length || !sections.length) return;
links.forEach(function (a) {
a.addEventListener('click', function (e) {
var id = a.getAttribute('href');
var el = id ? document.getElementById(id.replace(/^#/, '')) : null;
if (!el) return;
e.preventDefault();
var top = el.getBoundingClientRect().top + window.pageYOffset - 90;
window.scrollTo({ top: top, behavior: 'smooth' });
});
});
function updateActive() {
var y = window.pageYOffset + 120;
var current = sections[0];
for (var i = 0; i < sections.length; i++) {
if (sections[i].offsetTop <= y) current = sections[i];
}
if (!current) return;
links.forEach(function (a) {
var li = a.parentElement;
if (!li) return;
if (a.getAttribute('href') === '#' + current.id) {
li.classList.add('active');
} else {
li.classList.remove('active');
}
});
if (nav) nav.classList.add('affix');
}
window.addEventListener('scroll', updateActive, { passive: true });
updateActive();
}
/* ---------- submit modal ---------- */
function initSubmitModal() {
var modal = qs('#submit-links-modal');
if (!modal) return;
function openModal(e) {
if (e) e.preventDefault();
modal.classList.add('is-open', 'show');
modal.setAttribute('aria-hidden', 'false');
document.documentElement.classList.add('aksrt-modal-open');
}
function closeModal(e) {
if (e) e.preventDefault();
modal.classList.remove('is-open', 'show');
modal.setAttribute('aria-hidden', 'true');
document.documentElement.classList.remove('aksrt-modal-open');
}
qsa('a[href="#submit-links-modal"], [data-toggle="modal"][href="#submit-links-modal"]').forEach(function (btn) {
btn.addEventListener('click', openModal);
});
qsa('[data-dismiss="modal"]', modal).forEach(function (btn) {
btn.addEventListener('click', closeModal);
});
modal.addEventListener('click', function (e) {
if (e.target === modal) closeModal(e);
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(e);
});
var fileInput = qs('.aksrt-links-logo-input', modal);
var preview = qs('.aksrt-links-logo-preview', modal);
if (fileInput && preview) {
fileInput.addEventListener('change', function () {
var file = fileInput.files && fileInput.files[0];
if (!file) return;
var url = URL.createObjectURL(file);
preview.src = url;
});
}
var form = qs('.aksrt-links-submit-form', modal);
if (!form) return;
form.addEventListener('submit', function (e) {
e.preventDefault();
var btn = qs('.aksrt-links-submit-btn', form);
if (btn) {
btn.disabled = true;
btn.classList.add('loading');
}
var fd = new FormData(form);
if (!fd.get('action')) fd.set('action', 'aksrt_frontend_links_submit');
fetch(ajaxUrl(), {
method: 'POST',
body: fd,
credentials: 'same-origin',
})
.then(function (r) {
return r.json();
})
.then(function (data) {
if (!data || data.error) {
toast((data && data.msg) || '提交失败', 'danger');
return;
}
toast(data.msg || '提交成功', 'success');
if (data.reload) {
window.setTimeout(function () {
window.location.reload();
}, 600);
} else {
closeModal();
form.reset();
}
})
.catch(function () {
toast('网络错误,请稍后重试', 'danger');
})
.finally(function () {
if (btn) {
btn.disabled = false;
btn.classList.remove('loading');
}
});
});
}
function init() {
if (!document.body.classList.contains('page-template-templates-pages-page-links-php')
&& !qs('.page-template-links')
&& !qs('.links-page-container')
&& !qs('#submit-links-modal')) {
// Still bind if modal exists alone.
}
initSearchTabs();
initCatNav();
initSubmitModal();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
+34 -5
View File
@@ -82,11 +82,11 @@
}
target.classList.add('is-loading');
var url = restUrl.replace(/\/$/, '') + '/user-center/panel/' + encodeURIComponent(name);
if (name === 'creator' || name === 'revenue') {
if (name === 'creator' || name === 'revenue' || name === 'posts' || name === 'follow') {
var params = queryParams || new URLSearchParams(window.location.search);
var allowedParams = name === 'creator'
? ['income_page']
: ['reward_tab', 'commission_page', 'invite_page'];
var allowedParams = name === 'creator' ? ['income_page'] :
(name === 'revenue' ? ['reward_tab', 'commission_page', 'invite_page'] :
(name === 'posts' ? ['post_status', 'posts_page'] : ['follow_list', 'follow_page']));
allowedParams.forEach(function(key) {
if (params.get(key)) url += (url.indexOf('?') === -1 ? '?' : '&') + key + '=' + encodeURIComponent(params.get(key));
});
@@ -328,6 +328,33 @@
});
}
function initPostDeletion() {
if (document.documentElement.dataset.aksrtPostDeletionBound === 'true') return;
document.documentElement.dataset.aksrtPostDeletionBound = 'true';
document.addEventListener('click', function(e) {
var btn = e.target && e.target.closest ? e.target.closest('[data-action="delete-post"]') : null;
if (!btn || btn.disabled || !confirm('确定删除这篇文章?')) return;
e.preventDefault();
btn.disabled = true;
var body = new URLSearchParams();
body.set('action', 'aksrt_delete_post');
body.set('post_id', btn.getAttribute('data-post-id') || '');
body.set('nonce', btn.getAttribute('data-nonce') || submitNonce);
fetch(ajaxUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, credentials: 'same-origin', body: body.toString() })
.then(function(response) { return response.json(); })
.then(function(response) {
if (!response || !response.success) throw new Error('delete-failed');
var item = btn.closest('.profile-list-item');
if (item) item.remove();
if (typeof showToast === 'function') showToast('文章已删除');
})
.catch(function() {
btn.disabled = false;
if (typeof showToast === 'function') showToast(i18n.operationFailed || '操作失败');
});
});
}
/* ==================== 通知中心 ==================== */
function initNotifications() {
var list = document.getElementById('aksrt-notification-list');
@@ -755,6 +782,7 @@
initProfileForm();
initAuthForm();
initUnfavorite();
initPostDeletion();
initNotifications();
initSettings();
});
@@ -763,7 +791,8 @@
initProfileForm();
initAuthForm();
initUnfavorite();
initNotifications();
initPostDeletion();
initNotifications();
initSettings();
}
-9
View File
@@ -262,12 +262,6 @@ if (class_exists('\AKSRT\Core\CommentActions')) {
if (class_exists('\AKSRT\Core\GoRedirect')) {
\AKSRT\Core\GoRedirect::get_instance()->init();
}
if (class_exists('\AKSRT\Core\LinksSubmit')) {
\AKSRT\Core\LinksSubmit::get_instance()->init();
}
if (class_exists('\AKSRT\Core\LinksPageMeta')) {
\AKSRT\Core\LinksPageMeta::get_instance()->init();
}
if (class_exists('\AKSRT\Core\PostsNavs')) {
\AKSRT\Core\PostsNavs::get_instance()->init();
}
@@ -277,9 +271,6 @@ if (class_exists('\AKSRT\Identity\Identity')) {
if (class_exists('\AKSRT\User\AuthApply')) {
\AKSRT\User\AuthApply::get_instance()->init();
}
if (class_exists('\AKSRT\Core\DocumentNav')) {
\AKSRT\Core\DocumentNav::get_instance()->init();
}
if (class_exists('\AKSRT\User\BanAppeal')) {
\AKSRT\User\BanAppeal::get_instance()->init();
}
-7
View File
@@ -13,13 +13,6 @@ add_action('wp_head', static function () {
add_filter('show_admin_bar', '__return_false');
// ============================================================
// WordPress 链接管理(网址导航 / 书签)
// zibll enables link_manager; AKSRT keeps it for page-links.
// ============================================================
add_filter('pre_option_link_manager_enabled', '__return_true');
// ============================================================
// SMTP 邮件配置注入
// ============================================================
+30 -4
View File
@@ -142,6 +142,8 @@ if (!function_exists('aksrt_capture_user_center_view')) {
'level' => 'level',
'profile' => 'form',
'creator' => 'creator',
'posts' => 'posts',
'follow' => 'follow',
'comments' => 'comments',
'favorites' => 'favorites',
'credits' => 'credits',
@@ -173,7 +175,7 @@ if (!function_exists('aksrt_capture_user_center_panel')) {
* Capture a built-in or registered user-center panel body.
*/
function aksrt_capture_user_center_panel(string $tab_key, array $tab, array $context): string {
$built_in_tabs = ['profile', 'level', 'creator', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'settings'];
$built_in_tabs = ['profile', 'level', 'creator', 'posts', 'follow', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'settings'];
if (in_array($tab_key, $built_in_tabs, true)) {
return aksrt_capture_user_center_view($tab_key, $context);
}
@@ -446,6 +448,26 @@ if (!function_exists('aksrt_get_user_center_render_context')) {
$follower_count = class_exists('\AKSRT\User\Follow')
? \AKSRT\User\Follow::get_instance()->get_follower_count($user_id)
: 0;
$following_count = class_exists('\AKSRT\User\Follow')
? \AKSRT\User\Follow::get_instance()->get_following_count($user_id)
: 0;
$allowed_post_statuses = ['publish', 'draft', 'pending', 'private'];
$post_status_filter = isset($_GET['post_status']) ? sanitize_key(wp_unslash($_GET['post_status'])) : 'publish';
if (!in_array($post_status_filter, array_merge($allowed_post_statuses, ['all']), true)) {
$post_status_filter = 'publish';
}
$user_status_counts = [];
foreach ($allowed_post_statuses as $status) {
$status_query = new WP_Query([
'author' => $user_id,
'post_type' => 'post',
'post_status' => $status,
'posts_per_page' => 1,
'fields' => 'ids',
'no_found_rows' => false,
]);
$user_status_counts[$status] = (int) $status_query->found_posts;
}
$svgs = aksrt_user_center_empty_svgs();
require_once AKSRT_DIR . '/templates/parts/user-shared-helpers.php';
@@ -478,6 +500,7 @@ if (!function_exists('aksrt_get_user_center_render_context')) {
'favorite_post_ids' => $favorite_post_ids,
'favorite_count' => $favorite_count,
'follower_count' => $follower_count,
'following_count' => $following_count,
'user_contacts' => $user_contacts,
'profile_signals' => $dashboard['profile_signals'] ?? [],
'user_background_url' => $user_background_url,
@@ -487,13 +510,16 @@ if (!function_exists('aksrt_get_user_center_render_context')) {
'messages_url' => $messages_enabled && function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : '',
'revenue_url' => $revenue_url,
'per_page' => 10,
'posts_page' => max(1, isset($_GET['posts_page']) ? (int) $_GET['posts_page'] : 1),
'post_status_filter' => $post_status_filter,
'user_status_counts' => $user_status_counts,
'new_post_url' => function_exists('aksrt_get_new_post_url') ? aksrt_get_new_post_url() : '',
'svg_empty_post' => $svgs['post'],
'comments_page' => max(1, isset($_GET['comments_page']) ? (int) $_GET['comments_page'] : 1),
'favorites_page' => max(1, isset($_GET['favorites_page']) ? (int) $_GET['favorites_page'] : 1),
'svg_empty_comment' => $svgs['comment'],
'svg_empty_favorite' => $svgs['favorite'],
'orders_url' => function_exists('aksrt_get_user_center_tab_url')
? aksrt_get_user_center_tab_url('orders')
: (function_exists('aksrt_get_orders_page_url') ? aksrt_get_orders_page_url() : ''),
'orders_url' => isset($tabs['tabs']['orders']) ? aksrt_get_user_center_tab_url('orders') : '',
'show_vip' => !empty($vip_settings['enabled']),
'show_credit_tabs' => $show_credit_tabs,
'affiliate_enabled' => $show_credit_tabs && !empty($affiliate_settings['enabled']),
+1 -1
View File
@@ -99,7 +99,7 @@ if (!function_exists('aksrt_get_header_nav_context')) :
? (int) aksrt_get_user_unread_notification_count($user_id)
: 0,
'messages_url' => function_exists('aksrt_get_messages_page_url') ? aksrt_get_messages_page_url() : '',
'orders_url' => function_exists('aksrt_get_orders_page_url') ? aksrt_get_orders_page_url() : '',
'orders_url' => function_exists('aksrt_get_user_center_tab_url') ? aksrt_get_user_center_tab_url('orders') : '',
'publish_url' => function_exists('aksrt_get_new_post_url') ? aksrt_get_new_post_url() : admin_url('post-new.php'),
'theme_settings_url' => function_exists('aksrt_get_admin_settings_url')
? aksrt_get_admin_settings_url('')
@@ -57,7 +57,12 @@ if ($q->have_posts()) :
?>
</div>
<?php
aksrt_render_profile_pagination((int) $q->max_num_pages, $posts_page, 'posts_page');
aksrt_render_profile_pagination(
(int) $q->max_num_pages,
$posts_page,
'posts_page',
add_query_arg('post_status', $post_status_filter, aksrt_get_user_center_tab_url('posts'))
);
wp_reset_postdata();
else :
aksrt_render_profile_empty(
@@ -26,10 +26,9 @@ $status_chip_defs = [
continue;
}
$chip_url = add_query_arg([
'tab' => 'posts',
'post_status' => $status_key,
'posts_page' => 1,
], remove_query_arg(['posts_page']));
], aksrt_get_user_center_tab_url('posts'));
$chip_active = ($post_status_filter === $status_key);
?>
<a href="<?php echo esc_url($chip_url); ?>" class="profile-status-chip<?php echo $chip_active ? ' is-active' : ''; ?>" role="tab" aria-selected="<?php echo $chip_active ? 'true' : 'false'; ?>">
@@ -44,10 +44,7 @@ if ($show_credits) {
// Tabbed ledgers (Zibll record tabs).
if ($show_wallet || $show_credits) :
$active_record = isset($_GET['asset_tab']) ? sanitize_key(wp_unslash($_GET['asset_tab'])) : '';
if ($active_record === '' || !in_array($active_record, ['balance', 'points', 'points_free', 'points_date'], true)) {
$active_record = $show_wallet ? 'balance' : 'points';
}
$active_record = $show_wallet ? 'balance' : 'points';
$show_tasks = $show_credits && class_exists('\AKSRT\Credits\PointsFree') && \AKSRT\Credits\PointsFree::enabled();
?>
<div class="uc-asset-tabs">
@@ -0,0 +1,67 @@
<?php
/**
* Private follow management panel.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$context = is_array($args['context'] ?? null) ? $args['context'] : [];
extract($context, EXTR_SKIP); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
$follow = class_exists('\\AKSRT\\User\\Follow') ? \AKSRT\User\Follow::get_instance() : null;
$active_list = isset($_GET['follow_list']) ? sanitize_key(wp_unslash($_GET['follow_list'])) : 'followers';
if (!in_array($active_list, ['followers', 'following'], true)) {
$active_list = 'followers';
}
$page = max(1, isset($_GET['follow_page']) ? (int) $_GET['follow_page'] : 1);
$result = $follow
? ($active_list === 'following' ? $follow->get_following((int) $user_id, $page, 20) : $follow->get_followers((int) $user_id, $page, 20))
: ['items' => [], 'total_pages' => 1];
$messages_enabled = !empty($messages_url);
?>
<div class="widget-card uc-follow-management">
<div class="profile-list-header">
<div>
<h2 class="widget-title widget-title--inline"><?php esc_html_e('粉丝与关注', 'aksrt'); ?></h2>
<p class="muted-2-color em09"><?php esc_html_e('查看谁关注了你,并管理你关注的作者。', 'aksrt'); ?></p>
</div>
</div>
<div class="profile-status-filter" role="tablist" aria-label="<?php esc_attr_e('关注关系筛选', 'aksrt'); ?>">
<?php foreach (['followers' => __('粉丝', 'aksrt'), 'following' => __('我关注的', 'aksrt')] as $key => $label) :
$url = add_query_arg(['follow_list' => $key, 'follow_page' => 1], aksrt_get_user_center_tab_url('follow'));
?>
<a class="profile-status-chip<?php echo $active_list === $key ? ' is-active' : ''; ?>" href="<?php echo esc_url($url); ?>" data-tab="follow" role="tab" aria-selected="<?php echo $active_list === $key ? 'true' : 'false'; ?>">
<?php echo esc_html($label); ?> <span class="profile-status-chip__count"><?php echo esc_html(number_format_i18n((int) ($key === 'followers' ? $follower_count : $following_count))); ?></span>
</a>
<?php endforeach; ?>
</div>
<?php if (!empty($result['items'])) : ?>
<div class="uc-follow-management__list">
<?php foreach ($result['items'] as $item) :
$member_id = (int) ($item['user_id'] ?? 0);
$home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($member_id) : '';
$message_url = $messages_enabled ? add_query_arg('msg_type', 'conversation', $messages_url) : '';
?>
<div class="uc-follow-management__item" data-follow-user="<?php echo esc_attr((string) $member_id); ?>">
<a href="<?php echo esc_url($home_url); ?>" class="uc-follow-management__avatar"><?php echo get_avatar($member_id, 48, '', (string) ($item['display_name'] ?? '')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a>
<div class="uc-follow-management__identity"><a href="<?php echo esc_url($home_url); ?>"><?php echo esc_html((string) ($item['display_name'] ?? '')); ?></a><span><?php echo esc_html(sprintf(__('关注于 %s', 'aksrt'), get_date_from_gmt((string) ($item['created_at'] ?? ''), get_option('date_format')))); ?></span></div>
<div class="uc-follow-management__actions">
<a class="btn-ghost btn-sm" href="<?php echo esc_url($home_url); ?>"><?php esc_html_e('主页', 'aksrt'); ?></a>
<?php if ($messages_enabled) : ?><a class="btn-ghost btn-sm" href="<?php echo esc_url($message_url); ?>"><?php esc_html_e('私信', 'aksrt'); ?></a><?php endif; ?>
<?php if ($active_list === 'following') : ?><button type="button" class="btn-outline btn-sm is-following" data-aksrt-follow="<?php echo esc_attr((string) $member_id); ?>"><i class="fas fa-heart" aria-hidden="true"></i><span><?php esc_html_e('已关注', 'aksrt'); ?></span></button><?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php aksrt_render_profile_pagination(
(int) ($result['total_pages'] ?? 1),
$page,
'follow_page',
add_query_arg('follow_list', $active_list, aksrt_get_user_center_tab_url('follow'))
); ?>
<?php else : ?>
<?php aksrt_render_profile_empty('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></svg>', $active_list === 'followers' ? __('还没有粉丝', 'aksrt') : __('还没有关注任何作者', 'aksrt')); ?>
<?php endif; ?>
</div>
@@ -28,7 +28,7 @@ $points = (float) ($credit_balance ?? 0);
</a>
<?php endif; ?>
<?php if ($show_credits) : ?>
<a class="uc-summary-card uc-summary-card--points" href="<?php echo esc_url(add_query_arg('asset_tab', 'points', $assets_url)); ?>" data-tab="credits">
<a class="uc-summary-card uc-summary-card--points" href="<?php echo esc_url($assets_url); ?>" data-tab="credits">
<span class="uc-summary-card__head"><?php echo esc_html((string) ($currency_name ?? __('积分', 'aksrt'))); ?><i class="fas fa-angle-right" aria-hidden="true"></i></span>
<span class="uc-summary-card__body">
<strong><?php echo esc_html(function_exists('aksrt_format_credits') ? aksrt_format_credits((int) $points) : number_format_i18n((int) $points)); ?></strong>
@@ -7,13 +7,12 @@
defined('ABSPATH') || exit;
$public_home_url = function_exists('aksrt_get_user_home_url') ? aksrt_get_user_home_url($target_user ?? null) : '';
$stats = [
[
'label' => __('文章', 'aksrt'),
'value' => (int) ($post_count ?? 0),
'url' => isset($dashboard_tabs['posts']) ? aksrt_get_user_center_tab_url('posts') : $public_home_url,
'tab' => isset($dashboard_tabs['posts']) ? 'posts' : '',
'url' => aksrt_get_user_center_tab_url('posts'),
'tab' => 'posts',
],
[
'label' => __('评论', 'aksrt'),
@@ -30,7 +29,8 @@ $stats = [
[
'label' => __('粉丝', 'aksrt'),
'value' => (int) ($follower_count ?? 0),
'url' => $public_home_url !== '' ? add_query_arg(['tab' => 'follow', 'follow_sub' => 'followers'], $public_home_url) : '',
'url' => aksrt_get_user_center_tab_url('follow'),
'tab' => 'follow',
],
];
?>
+1 -8
View File
@@ -16,8 +16,6 @@ add_filter('query_vars', function ($vars) {
$vars[] = 'aksrt_user_center';
$vars[] = 'aksrt_user_center_tab';
$vars[] = 'aksrt_author_identifier';
$vars[] = 'aksrt_orders';
$vars[] = 'aksrt_revenue';
return $vars;
});
@@ -57,7 +55,7 @@ add_action('template_redirect', function () {
$tab = $tab === 'balance' ? 'credits' : sanitize_key($tab);
$allowed = class_exists('\\AKSRT\\User\\Center')
? array_keys(\AKSRT\User\Center::get_instance()->get_dashboard_tabs())
: ['profile', 'level', 'creator', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'settings'];
: ['profile', 'level', 'creator', 'posts', 'follow', 'comments', 'favorites', 'credits', 'vip', 'orders', 'auth', 'revenue', 'settings'];
if (!in_array($tab, $allowed, true)) {
global $wp_query;
if ($wp_query instanceof WP_Query) { $wp_query->set_404(); }
@@ -70,13 +68,8 @@ add_action('init', function () {
add_rewrite_tag('%aksrt_user_center%', '([^&]+)');
add_rewrite_tag('%aksrt_user_center_tab%', '([^/]+)');
add_rewrite_tag('%aksrt_author_identifier%', '([^/]+)');
add_rewrite_tag('%aksrt_orders%', '([0-1])');
add_rewrite_tag('%aksrt_revenue%', '([0-1])');
add_rewrite_rule('^user/([a-z0-9_-]+)/?$', 'index.php?aksrt_user_center=1&aksrt_user_center_tab=$matches[1]', 'top');
add_rewrite_rule('^author/([^/]+)/?$', 'index.php?aksrt_author_identifier=$matches[1]', 'top');
// Commerce center aliases (work even before WP page is assigned).
add_rewrite_rule('^orders/?$', 'index.php?aksrt_orders=1', 'top');
add_rewrite_rule('^revenue/?$', 'index.php?aksrt_revenue=1', 'top');
});
// --- 3b. 统一模板拦截器(核心)---
@@ -106,19 +106,6 @@ $_aksrt_special_templates = [
'title' => '个人中心',
'fallback' => '/profile/',
],
// Commerce center pages (权益与消费 / 购物车 / 收益)
'orders' => [
'file' => 'templates/pages/page-orders.php',
'slug' => 'orders',
'title' => '我的订单',
'fallback' => '/orders/',
],
'revenue' => [
'file' => 'templates/pages/page-revenue.php',
'slug' => 'revenue',
'title' => '收益中心',
'fallback' => '/revenue/',
],
'messages' => [
'file' => 'templates/pages/page-messages.php',
'slug' => 'messages',
@@ -402,16 +389,6 @@ function aksrt_get_current_special_template_path(): string {
return $profile_template ?: '';
}
if (get_query_var('aksrt_orders')) {
$orders_template = aksrt_locate_template_file('templates/pages/page-orders.php');
return $orders_template ?: '';
}
if (get_query_var('aksrt_revenue')) {
$revenue_template = aksrt_locate_template_file('templates/pages/page-revenue.php');
return $revenue_template ?: '';
}
if (get_query_var('aksrt_sign')) {
$auth_template = aksrt_get_auth_template_file((string) get_query_var('aksrt_auth_tab'), true);
$auth_template_path = aksrt_locate_template_file($auth_template);
-31
View File
@@ -54,37 +54,6 @@ function aksrt_get_new_post_url(int $edit_id = 0): string {
}
endif;
if (!function_exists('aksrt_get_orders_page_url')) :
/**
* 订单中心 URL — 嵌在用户中心 tab=orders(zibll 同构)。
* 旧 /orders/ 页面模板会 302 到此地址。
*/
function aksrt_get_orders_page_url(): string {
if (function_exists('aksrt_get_user_center_tab_url')) {
return aksrt_get_user_center_tab_url('orders');
}
if (function_exists('aksrt_get_user_center_url')) {
return aksrt_get_user_center_url('orders');
}
return home_url('/user/orders/');
}
endif;
if (!function_exists('aksrt_get_revenue_page_url')) :
/**
* 收益中心 URL — 嵌在用户中心 tab=revenue。
*/
function aksrt_get_revenue_page_url(): string {
if (function_exists('aksrt_get_user_center_tab_url')) {
return aksrt_get_user_center_tab_url('revenue');
}
if (function_exists('aksrt_get_user_center_url')) {
return aksrt_get_user_center_url('revenue');
}
return home_url('/user/revenue/');
}
endif;
if (!function_exists('aksrt_get_messages_page_url')) :
/**
* 消息中心页面 URL(模板 templates/pages/page-messages.php)。
-1
View File
@@ -18,7 +18,6 @@ class SchemaExtensions {
'audit' => SchemaExtensionsAudit::build(),
'push' => SchemaExtensionsPush::build(),
'security' => SchemaExtensionsSecurity::build(),
'docs-mode' => SchemaExtensionsDocsMode::build(),
],
];
}
@@ -1,53 +0,0 @@
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Admin\Schema;
defined('ABSPATH') || exit;
class SchemaExtensionsDocsMode {
public static function build(): array {
return [
'key' => 'docs-mode',
'title' => '文档模式',
'version' => '1.0.0',
'option_key' => 'aksrt_settings',
'groups' => [
['key' => 'basic', 'title' => '基础配置', 'fields' => [
['key' => 'docs_mode_enabled', 'label' => '启用文档模式', 'type' => 'switch', 'default' => false],
['key' => 'docs_mode_default_template', 'label' => '文档首页模板', 'type' => 'select', 'default' => 'documentnav', 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true], 'options' => [
['label' => '文档导航页', 'value' => 'documentnav'],
['label' => '专题聚合页', 'value' => 'special'],
]],
]],
['key' => 'navigation', 'title' => '导航设置', 'fields' => [
['key' => 'docs_mode_toc_enabled', 'label' => '显示目录树', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
['key' => 'docs_mode_chapter_nav_enabled', 'label' => '显示章节导航', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
['key' => 'docs_mode_default_order', 'label' => '默认排序', 'type' => 'radio', 'default' => 'menu_order', 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true], 'options' => [
['label' => '按菜单顺序', 'value' => 'menu_order'],
['label' => '按发布时间', 'value' => 'date'],
['label' => '按标题排序', 'value' => 'title'],
]],
['key' => 'docs_mode_toc_depth', 'label' => '目录深度', 'type' => 'number', 'default' => 3, 'min' => 1, 'max' => 6, 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
['key' => 'docs_mode_chapter_title', 'label' => '章节标题', 'type' => 'input', 'default' => '', 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
]],
['key' => 'filter', 'title' => '筛选设置', 'fields' => [
['key' => 'docs_mode_cats', 'label' => '文档分类', 'type' => 'taxonomy_select', 'taxonomy' => 'category', 'multiple' => true, 'default' => [], 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
['key' => 'docs_mode_exclude', 'label' => '在首页排除此类内容', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
]],
['key' => 'style', 'title' => '样式设置', 'fields' => [
['key' => 'docs_mode_style', 'label' => '文档样式', 'type' => 'select', 'default' => 'default', 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true], 'options' => [
['label' => '默认', 'value' => 'default'],
['label' => '简洁', 'value' => 'simple'],
['label' => '卡片', 'value' => 'card'],
]],
['key' => 'docs_mode_icon', 'label' => '显示图标', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
['key' => 'docs_mode_count', 'label' => '显示文章数', 'type' => 'switch', 'default' => true, 'dependsOn' => ['key' => 'docs_mode_enabled', 'value' => true]],
]],
],
];
}
}
-316
View File
@@ -1,316 +0,0 @@
<?php
/**
* Document navigation (zibll documentnav) — category tree + AJAX posts.
*
* @package AKSRT
*/
namespace AKSRT\Core;
defined('ABSPATH') || exit;
class DocumentNav {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
add_action('wp_ajax_aksrt_documentnav_posts', [$this, 'ajax_posts']);
add_action('wp_ajax_nopriv_aksrt_documentnav_posts', [$this, 'ajax_posts']);
add_action('add_meta_boxes', [$this, 'register_meta_box']);
add_action('save_post_page', [$this, 'save_meta_box']);
}
/**
* @return array{cat:int,initial_content:string}
*/
public static function get_options(int $post_id): array {
$raw = get_post_meta($post_id, 'documentnav_options', true);
if (!is_array($raw)) {
$raw = [];
}
$cat = isset($raw['cat']) ? absint($raw['cat']) : absint(get_post_meta($post_id, 'documentnav_cat', true));
$initial = isset($raw['initial_content']) ? sanitize_key((string) $raw['initial_content']) : sanitize_key((string) get_post_meta($post_id, 'documentnav_initial_content', true));
if (!in_array($initial, ['page_content', 'date_posts', 'updated_posts', 'views_posts'], true)) {
$initial = 'updated_posts';
}
return [
'cat' => $cat,
'initial_content' => $initial,
];
}
/**
* Render posts list HTML (zibll zib_get_documentnav_posts).
*/
public static function render_posts_html(\WP_Query $query): string {
if (!$query->have_posts()) {
$empty = function_exists('aksrt_get_null')
? aksrt_get_null(__('暂无相应内容', 'aksrt'), 40)
: '<p class="muted-2-color text-center">' . esc_html__('暂无相应内容', 'aksrt') . '</p>';
return '<div class="zib-widget">' . $empty . '</div>';
}
$html = '<div class="list-group main-shadow">';
while ($query->have_posts()) {
$query->the_post();
$time_ago = human_time_diff(get_the_time('U'), current_time('timestamp')) . __('前', 'aksrt');
$subtitle = (string) get_post_meta(get_the_ID(), 'subtitle', true);
if ($subtitle === '') {
$subtitle = (string) get_post_meta(get_the_ID(), 'aksrt_subtitle', true);
}
$html .= '<a class="list-group-item" href="' . esc_url(get_permalink()) . '">';
$html .= '<span class="badg badg-sm pull-right" title="' . esc_attr(get_the_time('Y-m-d H:i:s')) . '">' . esc_html($time_ago) . '</span>';
$html .= esc_html(get_the_title());
if ($subtitle !== '') {
$html .= '<span class="focus-color"> ' . esc_html($subtitle) . '</span>';
}
$html .= '</a>';
}
$html .= '</div>';
wp_reset_postdata();
return $html;
}
/**
* Simple paginate links for document nav container.
*/
public static function render_paginate(int $total, int $current, int $page_size, array $args = []): string {
$max = (int) ceil(max(0, $total) / max(1, $page_size));
if ($max <= 1) {
return '';
}
$current = max(1, min($current, $max));
$html = '<div class="pagenav notop text-center mt15">';
for ($i = 1; $i <= $max; $i++) {
$url = add_query_arg(array_merge($args, ['paged' => $i]));
$cls = $i === $current ? 'current' : '';
$html .= '<a class="' . esc_attr($cls) . '" href="' . esc_url($url) . '" data-paged="' . (int) $i . '">' . (int) $i . '</a> ';
}
$html .= '</div>';
return $html;
}
/**
* Recursive category accordion HTML.
*/
public static function render_panels(int $cat_id, string $data_parent = 'accordion', string $class = 'panel', bool &$is_on_open = true): string {
if ($cat_id <= 0) {
return '';
}
$terms = _get_term_hierarchy('category');
if (empty($terms[$cat_id]) || !is_array($terms[$cat_id])) {
return '';
}
$html = '<div class="panel-group posts-panel-group" id="' . esc_attr($data_parent) . '">';
foreach ($terms[$cat_id] as $child) {
$child = (int) $child;
if ($child === $cat_id) {
continue;
}
$cat_obj = get_category($child);
if (!$cat_obj || is_wp_error($cat_obj)) {
continue;
}
$has_kids = !empty($terms[$child]);
if ($has_kids) {
$open = $is_on_open;
$is_on_open = false;
$html .= '<div class="' . esc_attr($class) . '">';
$html .= '<h4 class="panel-title">';
$html .= '<a class="panel-toggle' . ($open ? '' : ' collapsed') . ' text-ellipsis" href="javascript:;" data-toggle="collapse" data-target="#dosc-nav-catid_' . $child . '" data-parent="#' . esc_attr($data_parent) . '">';
$html .= '<i class="fa fa-angle-right mr10" aria-hidden="true"></i>' . esc_html($cat_obj->name);
$html .= '</a></h4>';
$html .= '<div id="dosc-nav-catid_' . $child . '" class="panel-collapse' . ($open ? ' collapse in is-open' : ' collapse') . '">';
$html .= '<div class="category">';
$html .= self::render_panels($child, 'dosc-cat-nav-panel-' . $child, 'panel panel-child', $is_on_open);
$html .= '</div></div></div>';
} else {
$html .= '<a class="cat-load text-ellipsis" data-cat-id="' . $child . '" href="javascript:;" title="' . esc_attr($cat_obj->name) . '">';
$html .= '<i class="loading" aria-hidden="true"></i>' . esc_html($cat_obj->name);
$html .= '</a>';
}
}
$html .= '</div>';
return $html;
}
public function ajax_posts(): void {
$search = isset($_REQUEST['search']) ? sanitize_text_field(wp_unslash((string) $_REQUEST['search'])) : '';
$cat_id = isset($_REQUEST['cat_id']) ? absint($_REQUEST['cat_id']) : 0;
$one_cat = isset($_REQUEST['one_cat']) ? absint($_REQUEST['one_cat']) : 0;
$paged = isset($_REQUEST['paged']) ? max(1, absint($_REQUEST['paged'])) : 1;
$page_size = 12;
if ($search === '' && $cat_id <= 0) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('参数传入错误!', 'aksrt')]);
}
$args = [
'ignore_sticky_posts' => true,
'paged' => $paged,
'posts_per_page' => $page_size,
'post_status' => 'publish',
'post_type' => 'post',
];
if ($search !== '') {
$args['s'] = $search;
$args['cat'] = $one_cat > 0 ? $one_cat : 0;
// Persist simple history (user meta or cookie-less option list is optional).
$this->remember_search($search);
} else {
$args['cat'] = $cat_id;
}
$query = new \WP_Query($args);
$html = self::render_posts_html($query);
$html .= self::render_paginate(
(int) $query->found_posts,
$paged,
$page_size,
[
'action' => 'aksrt_documentnav_posts',
'cat_id' => $cat_id,
'search' => $search,
'one_cat' => $one_cat,
]
);
wp_send_json([
'error' => 0,
'html' => $html,
'total' => (int) $query->found_posts,
'history_search' => $this->history_buttons_html(),
]);
}
private function remember_search(string $term): void {
$term = trim($term);
if ($term === '' || mb_strlen($term) < 2) {
return;
}
$key = is_user_logged_in() ? 'aksrt_doc_search_history_' . get_current_user_id() : '';
$list = [];
if ($key !== '') {
$saved = get_user_meta(get_current_user_id(), 'aksrt_doc_search_history', true);
$list = is_array($saved) ? $saved : [];
} else {
$cookie = isset($_COOKIE['aksrt_doc_search_history']) ? wp_unslash((string) $_COOKIE['aksrt_doc_search_history']) : '';
$decoded = json_decode($cookie, true);
$list = is_array($decoded) ? $decoded : [];
}
$list = array_values(array_unique(array_filter(array_map('strval', $list))));
array_unshift($list, $term);
$list = array_slice(array_values(array_unique($list)), 0, 8);
if ($key !== '') {
update_user_meta(get_current_user_id(), 'aksrt_doc_search_history', $list);
} elseif (!headers_sent()) {
setcookie('aksrt_doc_search_history', wp_json_encode($list), time() + WEEK_IN_SECONDS, COOKIEPATH ?: '/', COOKIE_DOMAIN, is_ssl(), true);
}
}
/**
* @return list<string>
*/
private function get_history(): array {
if (is_user_logged_in()) {
$saved = get_user_meta(get_current_user_id(), 'aksrt_doc_search_history', true);
return is_array($saved) ? array_values(array_filter(array_map('strval', $saved))) : [];
}
$cookie = isset($_COOKIE['aksrt_doc_search_history']) ? wp_unslash((string) $_COOKIE['aksrt_doc_search_history']) : '';
$decoded = json_decode($cookie, true);
return is_array($decoded) ? array_values(array_filter(array_map('strval', $decoded))) : [];
}
public function history_buttons_html(): string {
$items = $this->get_history();
if (!$items) {
return '';
}
$html = '';
foreach ($items as $term) {
$html .= '<a href="javascript:;" class="search_keywords but badg mr6 mb6">' . esc_html($term) . '</a>';
}
return $html;
}
public function register_meta_box(): void {
add_meta_box(
'aksrt_documentnav_meta',
__('文档导航设置', 'aksrt'),
[$this, 'render_meta_box'],
'page',
'side',
'default'
);
}
public function render_meta_box(\WP_Post $post): void {
$tpl = (string) get_post_meta($post->ID, '_wp_page_template', true);
if ($tpl !== 'templates/pages/page-documentnav.php') {
echo '<p class="description">' . esc_html__('将模板设为「AKSRT - 文档导航」后配置。', 'aksrt') . '</p>';
return;
}
$opt = self::get_options((int) $post->ID);
wp_nonce_field('aksrt_documentnav_meta', 'aksrt_documentnav_meta_nonce');
$cats = get_categories(['hide_empty' => false, 'parent' => 0]);
?>
<p>
<label for="documentnav_cat"><strong><?php esc_html_e('一级分类', 'aksrt'); ?></strong></label>
<select name="documentnav_cat" id="documentnav_cat" class="widefat">
<option value="0"><?php esc_html_e('请选择', 'aksrt'); ?></option>
<?php foreach ($cats as $c) : ?>
<option value="<?php echo (int) $c->term_id; ?>" <?php selected($opt['cat'], (int) $c->term_id); ?>>
<?php echo esc_html($c->name); ?>
</option>
<?php endforeach; ?>
</select>
</p>
<p>
<label for="documentnav_initial_content"><strong><?php esc_html_e('初始内容', 'aksrt'); ?></strong></label>
<select name="documentnav_initial_content" id="documentnav_initial_content" class="widefat">
<?php
$opts = [
'page_content' => __('显示页面内容', 'aksrt'),
'date_posts' => __('最近发布文章', 'aksrt'),
'updated_posts' => __('最近更新文章', 'aksrt'),
'views_posts' => __('查看最多文章', 'aksrt'),
];
foreach ($opts as $k => $label) :
?>
<option value="<?php echo esc_attr($k); ?>" <?php selected($opt['initial_content'], $k); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
</p>
<?php
}
public function save_meta_box(int $post_id): void {
if (!isset($_POST['aksrt_documentnav_meta_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_documentnav_meta_nonce'])), 'aksrt_documentnav_meta')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
$cat = isset($_POST['documentnav_cat']) ? absint($_POST['documentnav_cat']) : 0;
$initial = isset($_POST['documentnav_initial_content']) ? sanitize_key((string) wp_unslash($_POST['documentnav_initial_content'])) : 'updated_posts';
if (!in_array($initial, ['page_content', 'date_posts', 'updated_posts', 'views_posts'], true)) {
$initial = 'updated_posts';
}
update_post_meta($post_id, 'documentnav_options', [
'cat' => $cat,
'initial_content' => $initial,
]);
}
}
-302
View File
@@ -1,302 +0,0 @@
<?php
/**
* Page meta for 网址导航 template (page_links_* keys, zibll metabox parity).
*
* @package AKSRT
*/
namespace AKSRT\Core;
defined('ABSPATH') || exit;
class LinksPageMeta {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
add_action('add_meta_boxes', [$this, 'register_meta_box']);
add_action('save_post_page', [$this, 'save_meta_box']);
}
public function register_meta_box(): void {
add_meta_box(
'aksrt_page_links_meta',
__('网址导航设置', 'aksrt'),
[$this, 'render_meta_box'],
'page',
'normal',
'high'
);
}
/**
* Whether current page uses the links template.
*/
private function is_links_template(int $post_id): bool {
$tpl = (string) get_post_meta($post_id, '_wp_page_template', true);
return $tpl === 'templates/pages/page-links.php';
}
public function render_meta_box(\WP_Post $post): void {
$post_id = (int) $post->ID;
if (!$this->is_links_template($post_id)) {
echo '<p class="description">' . esc_html__('将页面模板设为「AKSRT - 网址导航」后在此配置分类、样式与提交模块。', 'aksrt') . '</p>';
return;
}
$s = function_exists('aksrt_get_page_links_settings')
? aksrt_get_page_links_settings($post_id)
: [];
wp_nonce_field('aksrt_page_links_meta', 'aksrt_page_links_meta_nonce');
$terms = get_terms([
'taxonomy' => 'link_category',
'hide_empty' => false,
]);
if (is_wp_error($terms)) {
$terms = [];
}
$selected_cats = array_map('intval', (array) ($s['category'] ?? []));
$submit_cats = array_map('intval', (array) ($s['submit_cats'] ?? []));
$search_types = (array) ($s['search_types'] ?? []);
$engine_options = [
'self' => __('站内', 'aksrt'),
'baidu' => __('百度', 'aksrt'),
'google' => __('谷歌', 'aksrt'),
'bing' => __('必应', 'aksrt'),
'sogou' => __('搜狗', 'aksrt'),
'360' => '360',
];
?>
<table class="form-table aksrt-page-links-meta" role="presentation">
<tr>
<th scope="row"><?php esc_html_e('显示页面内容', 'aksrt'); ?></th>
<td>
<label>
<input type="checkbox" name="page_links_content_s" value="1" <?php checked(!empty($s['content_s'])); ?>>
<?php esc_html_e('在链接列表上下方展示页面正文', 'aksrt'); ?>
</label>
<p>
<label>
<input type="radio" name="page_links_content_position" value="top" <?php checked(($s['content_position'] ?? 'top'), 'top'); ?>>
<?php esc_html_e('链接列表上面', 'aksrt'); ?>
</label>
&nbsp;
<label>
<input type="radio" name="page_links_content_position" value="bottom" <?php checked(($s['content_position'] ?? ''), 'bottom'); ?>>
<?php esc_html_e('链接列表下面', 'aksrt'); ?>
</label>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('搜索引擎模块', 'aksrt'); ?></th>
<td>
<label>
<input type="checkbox" name="page_links_search_s" value="1" <?php checked(!empty($s['search_s'])); ?>>
<?php esc_html_e('显示搜索封面(使用特色图作背景)', 'aksrt'); ?>
</label>
<p class="description"><?php esc_html_e('选择引擎(可多选,按住 Ctrl)', 'aksrt'); ?></p>
<select name="page_links_search_types[]" multiple class="widefat" size="6">
<?php foreach ($engine_options as $key => $label) : ?>
<option value="<?php echo esc_attr($key); ?>" <?php selected(in_array($key, $search_types, true)); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
</select>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('链接分类(必选)', 'aksrt'); ?></th>
<td>
<?php if (empty($terms)) : ?>
<p class="description">
<?php
printf(
/* translators: %s: link manager URL */
wp_kses_post(__('暂无链接分类。请先在 <a href="%s">链接管理</a> 中创建分类与链接。', 'aksrt')),
esc_url(admin_url('link-manager.php'))
);
?>
</p>
<?php else : ?>
<select name="page_links_category[]" multiple class="widefat" size="8">
<?php foreach ($terms as $term) : ?>
<option value="<?php echo (int) $term->term_id; ?>" <?php selected(in_array((int) $term->term_id, $selected_cats, true)); ?>>
<?php echo esc_html($term->name . ' (' . (int) $term->count . ')'); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e('多选时按导航目录展示;一个都不选则不显示链接。', 'aksrt'); ?></p>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('显示样式', 'aksrt'); ?></th>
<td>
<?php
$style = (string) ($s['style'] ?? 'card');
$styles = [
'card' => __('图文列表', 'aksrt'),
'bigcard' => __('大卡片', 'aksrt'),
'image' => __('纯图片', 'aksrt'),
];
foreach ($styles as $key => $label) :
?>
<label style="margin-right:12px;">
<input type="radio" name="page_links_style" value="<?php echo esc_attr($key); ?>" <?php checked($style, $key); ?>>
<?php echo esc_html($label); ?>
</label>
<?php endforeach; ?>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('数量与排序', 'aksrt'); ?></th>
<td>
<label>
<?php esc_html_e('每类最多', 'aksrt'); ?>
<input type="number" name="page_links_limit" value="<?php echo esc_attr((string) (int) ($s['limit'] ?? 0)); ?>" min="0" step="1" class="small-text">
<?php esc_html_e('个(0=不限)', 'aksrt'); ?>
</label>
<p>
<select name="page_links_orderby">
<?php
$orderbys = [
'name' => __('名称', 'aksrt'),
'updated' => __('更新时间', 'aksrt'),
'rating' => __('评分', 'aksrt'),
'rand' => __('随机', 'aksrt'),
];
$orderby = (string) ($s['orderby'] ?? 'name');
foreach ($orderbys as $key => $label) :
?>
<option value="<?php echo esc_attr($key); ?>" <?php selected($orderby, $key); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
<select name="page_links_order">
<option value="ASC" <?php selected(($s['order'] ?? 'ASC'), 'ASC'); ?>><?php esc_html_e('升序', 'aksrt'); ?></option>
<option value="DESC" <?php selected(($s['order'] ?? ''), 'DESC'); ?>><?php esc_html_e('降序', 'aksrt'); ?></option>
</select>
</p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('链接行为', 'aksrt'); ?></th>
<td>
<label><input type="checkbox" name="page_links_go_s" value="1" <?php checked(!empty($s['go_s'])); ?>> <?php esc_html_e('外链重定向 /go/', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="page_links_blank_s" value="1" <?php checked(!empty($s['blank_s'])); ?>> <?php esc_html_e('新标签打开', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="page_links_nofollow_s" value="1" <?php checked(!empty($s['nofollow_s'])); ?>> <?php esc_html_e('添加 nofollow', 'aksrt'); ?></label>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e('提交链接', 'aksrt'); ?></th>
<td>
<label><input type="checkbox" name="page_links_submit_s" value="1" <?php checked(!empty($s['submit_s'])); ?>> <?php esc_html_e('显示申请入驻', 'aksrt'); ?></label><br>
<label><input type="checkbox" name="page_links_submit_sign_s" value="1" <?php checked(!empty($s['submit_sign_s'])); ?>> <?php esc_html_e('登录后才能提交', 'aksrt'); ?></label>
<p>
<label><?php esc_html_e('按钮标题', 'aksrt'); ?>
<input type="text" class="regular-text" name="page_links_submit_title" value="<?php echo esc_attr((string) ($s['submit_title'] ?? '')); ?>">
</label>
</p>
<p>
<label for="page_links_submit_dec"><?php esc_html_e('提交说明(HTML)', 'aksrt'); ?></label><br>
<textarea class="large-text" rows="5" id="page_links_submit_dec" name="page_links_submit_dec"><?php echo esc_textarea((string) ($s['submit_dec'] ?? '')); ?></textarea>
</p>
<?php if (!empty($terms)) : ?>
<p class="description"><?php esc_html_e('提交时可选分类(空=全部分类)', 'aksrt'); ?></p>
<select name="page_links_submit_cats[]" multiple class="widefat" size="6">
<?php foreach ($terms as $term) : ?>
<option value="<?php echo (int) $term->term_id; ?>" <?php selected(in_array((int) $term->term_id, $submit_cats, true)); ?>>
<?php echo esc_html($term->name); ?>
</option>
<?php endforeach; ?>
</select>
<?php endif; ?>
</td>
</tr>
</table>
<?php
}
public function save_meta_box(int $post_id): void {
if (!isset($_POST['aksrt_page_links_meta_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST['aksrt_page_links_meta_nonce'])), 'aksrt_page_links_meta')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
if (!$this->is_links_template($post_id) && empty($_POST['page_links_category']) && empty($_POST['page_links_submit_s'])) {
// Still allow save if user just switched template in same request.
$tpl = isset($_POST['page_template']) ? (string) wp_unslash($_POST['page_template']) : '';
if ($tpl !== 'templates/pages/page-links.php') {
return;
}
}
$bool_keys = [
'page_links_content_s',
'page_links_search_s',
'page_links_go_s',
'page_links_blank_s',
'page_links_nofollow_s',
'page_links_submit_s',
'page_links_submit_sign_s',
];
foreach ($bool_keys as $key) {
update_post_meta($post_id, $key, isset($_POST[$key]) ? 1 : 0);
}
$position = isset($_POST['page_links_content_position']) ? sanitize_key((string) wp_unslash($_POST['page_links_content_position'])) : 'top';
if (!in_array($position, ['top', 'bottom'], true)) {
$position = 'top';
}
update_post_meta($post_id, 'page_links_content_position', $position);
$style = isset($_POST['page_links_style']) ? sanitize_key((string) wp_unslash($_POST['page_links_style'])) : 'card';
if (!in_array($style, ['card', 'bigcard', 'image', 'simple'], true)) {
$style = 'card';
}
update_post_meta($post_id, 'page_links_style', $style);
$orderby = isset($_POST['page_links_orderby']) ? sanitize_key((string) wp_unslash($_POST['page_links_orderby'])) : 'name';
if (!in_array($orderby, ['name', 'updated', 'rating', 'rand', 'id', 'length'], true)) {
$orderby = 'name';
}
update_post_meta($post_id, 'page_links_orderby', $orderby);
$order = isset($_POST['page_links_order']) ? strtoupper(sanitize_text_field(wp_unslash((string) $_POST['page_links_order']))) : 'ASC';
update_post_meta($post_id, 'page_links_order', $order === 'DESC' ? 'DESC' : 'ASC');
$limit = isset($_POST['page_links_limit']) ? max(0, (int) $_POST['page_links_limit']) : 0;
update_post_meta($post_id, 'page_links_limit', $limit);
$cats = isset($_POST['page_links_category']) ? array_values(array_filter(array_map('intval', (array) wp_unslash($_POST['page_links_category'])))) : [];
update_post_meta($post_id, 'page_links_category', $cats);
$submit_cats = isset($_POST['page_links_submit_cats']) ? array_values(array_filter(array_map('intval', (array) wp_unslash($_POST['page_links_submit_cats'])))) : [];
update_post_meta($post_id, 'page_links_submit_cats', $submit_cats);
$engines = isset($_POST['page_links_search_types']) ? array_values(array_filter(array_map('sanitize_key', (array) wp_unslash($_POST['page_links_search_types'])))) : [];
$allowed = ['self', 'baidu', 'google', 'bing', 'sogou', '360'];
$engines = array_values(array_intersect($engines, $allowed));
update_post_meta($post_id, 'page_links_search_types', $engines);
$title = isset($_POST['page_links_submit_title']) ? sanitize_text_field(wp_unslash((string) $_POST['page_links_submit_title'])) : '';
update_post_meta($post_id, 'page_links_submit_title', $title);
$dec = isset($_POST['page_links_submit_dec']) ? wp_kses_post(wp_unslash((string) $_POST['page_links_submit_dec'])) : '';
update_post_meta($post_id, 'page_links_submit_dec', $dec);
}
}
-123
View File
@@ -1,123 +0,0 @@
<?php
/**
* Frontend links (bookmarks) submit — zibll frontend_links_submit parity.
*
* @package AKSRT
*/
namespace AKSRT\Core;
defined('ABSPATH') || exit;
class LinksSubmit {
private static $instance = null;
public static function get_instance(): self {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
public function init(): void {
add_action('wp_ajax_aksrt_frontend_links_submit', [$this, 'ajax_submit']);
add_action('wp_ajax_nopriv_aksrt_frontend_links_submit', [$this, 'ajax_submit']);
}
/**
* AJAX: create a pending WordPress bookmark (link).
*/
public function ajax_submit(): void {
if (!check_ajax_referer('aksrt_frontend_links_submit', '_wpnonce', false)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('安全校验失败,请刷新后重试', 'aksrt')]);
}
if (isset($_COOKIE['aksrt_links_submit_time'])) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('操作过于频繁,请稍候再试', 'aksrt')]);
}
$link_name = isset($_POST['link_name']) ? sanitize_text_field(wp_unslash((string) $_POST['link_name'])) : '';
$link_url = isset($_POST['link_url']) ? esc_url_raw(wp_unslash((string) $_POST['link_url'])) : '';
$link_desc = isset($_POST['link_description']) ? sanitize_text_field(wp_unslash((string) $_POST['link_description'])) : '';
$cat_raw = isset($_POST['link_category']) ? wp_unslash($_POST['link_category']) : [];
if ($link_name === '') {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('请填写网站名称', 'aksrt')]);
}
if ($link_url === '' || !wp_http_validate_url($link_url)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('网站地址格式错误', 'aksrt')]);
}
// Optional logo upload.
$logo_url = '';
if (!empty($_FILES['image']['name'])) {
require_once ABSPATH . 'wp-admin/includes/file.php';
require_once ABSPATH . 'wp-admin/includes/media.php';
require_once ABSPATH . 'wp-admin/includes/image.php';
$attachment_id = media_handle_upload('image', 0);
if (is_wp_error($attachment_id)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => $attachment_id->get_error_message()]);
}
$src = wp_get_attachment_image_src((int) $attachment_id, 'medium');
$logo_url = !empty($src[0]) ? (string) $src[0] : (string) wp_get_attachment_url((int) $attachment_id);
}
$categories = [];
if (is_array($cat_raw)) {
$categories = array_values(array_filter(array_map('intval', $cat_raw)));
} elseif ($cat_raw !== '' && $cat_raw !== null) {
$categories = [absint($cat_raw)];
}
// Duplicate URL guard.
global $wpdb;
$exists = (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(1) FROM {$wpdb->links} WHERE link_url = %s",
$link_url
)
);
if ($exists > 0) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => __('您的链接已提交,请勿重复提交', 'aksrt')]);
}
if (!function_exists('wp_insert_link')) {
require_once ABSPATH . 'wp-admin/includes/bookmark.php';
}
$linkdata = [
'link_name' => $link_name,
'link_url' => $link_url,
'link_description' => $link_desc,
'link_image' => $logo_url,
// Non-admins create invisible links pending review (zibll parity).
'link_visible' => current_user_can('manage_links') ? 'Y' : 'N',
'link_category' => $categories,
];
$links_id = wp_insert_link($linkdata, true);
if (is_wp_error($links_id)) {
wp_send_json(['error' => 1, 'ys' => 'danger', 'msg' => $links_id->get_error_message()]);
}
// 30s client throttle cookie.
if (!headers_sent()) {
setcookie('aksrt_links_submit_time', (string) time(), time() + 30, COOKIEPATH ?: '/', COOKIE_DOMAIN, is_ssl(), true);
}
$msg = $linkdata['link_visible'] === 'Y'
? __('提交成功', 'aksrt')
: __('提交成功,等待管理员处理', 'aksrt');
/**
* Fires after a frontend link submission is stored.
*
* @param array $linkdata Sanitized bookmark data.
* @param int $links_id New link ID.
*/
do_action('aksrt_frontend_links_submit_success', $linkdata, (int) $links_id);
wp_send_json(['error' => 0, 'msg' => $msg, 'reload' => true]);
}
}
-108
View File
@@ -564,28 +564,6 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
}
// 网址导航页(zibll links.php)
if (is_page_template('templates/pages/page-links.php')) {
$links_css = get_template_directory() . '/assets/css/routes/links.css';
if (file_exists($links_css)) {
wp_enqueue_style(
'aksrt-route-links',
$base_uri . '/assets/css/routes/links.css',
['aksrt-tokens'],
(string) filemtime($links_css)
);
}
$links_js = get_template_directory() . '/assets/js/links-page.js';
if (file_exists($links_js)) {
wp_enqueue_script(
'aksrt-links-page',
$base_uri . '/assets/js/links-page.js',
['jquery'],
(string) filemtime($links_js),
true
);
}
}
// 文章导航页(zibll postsnavs.php)
if (is_page_template('templates/pages/page-posts-navs.php')) {
$page_navs_js = get_template_directory() . '/assets/js/page-navs.js';
@@ -643,29 +621,6 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
}
}
// 文档导航页(zibll documentnav.php)
if (is_page_template('templates/pages/page-documentnav.php')) {
$dn_css = get_template_directory() . '/assets/css/routes/documentnav.css';
if (file_exists($dn_css)) {
wp_enqueue_style(
'aksrt-route-documentnav',
$base_uri . '/assets/css/routes/documentnav.css',
['aksrt-tokens'],
(string) filemtime($dn_css)
);
}
$dn_js = get_template_directory() . '/assets/js/documentnav-page.js';
if (file_exists($dn_js)) {
wp_enqueue_script(
'aksrt-documentnav-page',
$base_uri . '/assets/js/documentnav-page.js',
[],
(string) filemtime($dn_js),
true
);
}
}
// 禁封申诉页
if (is_page_template('templates/pages/page-ban-appeal.php')) {
$ba_css = get_template_directory() . '/assets/css/routes/ban-appeal.css';
@@ -689,69 +644,6 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
}
}
// 交易中心共享壳层(订单 / 收益)
// Virtual rewrite routes (aksrt_orders / aksrt_revenue) also count.
$is_commerce_center = is_page_template('templates/pages/page-orders.php')
|| is_page_template('templates/pages/page-revenue.php')
|| (bool) get_query_var('aksrt_orders')
|| (bool) get_query_var('aksrt_revenue');
if ($is_commerce_center) {
$commerce_css = get_template_directory() . '/assets/css/routes/commerce.css';
if (file_exists($commerce_css)) {
wp_enqueue_style(
'aksrt-commerce-shell',
$base_uri . '/assets/css/routes/commerce.css',
['aksrt-tokens'],
(string) filemtime($commerce_css)
);
}
}
// 订单中心:独立页 / 虚拟 rewrite;用户中心在上方按可用 tab 预载。
$is_orders_surface = is_page_template('templates/pages/page-orders.php')
|| (bool) get_query_var('aksrt_orders');
if ($is_orders_surface) {
$orders_css = get_template_directory() . '/assets/css/routes/orders.css';
if (file_exists($orders_css)) {
wp_enqueue_style(
'aksrt-orders',
$base_uri . '/assets/css/routes/orders.css',
['aksrt-tokens'],
(string) filemtime($orders_css)
);
}
$order_center_js = get_template_directory() . '/assets/js/order-center.js';
if (file_exists($order_center_js)) {
wp_enqueue_script(
'aksrt-order-center',
$base_uri . '/assets/js/order-center.js',
['aksrt-script', 'aksrt-rehydrate', 'aksrt-core-request', 'aksrt-core-modal-runtime'],
(string) filemtime($order_center_js),
true
);
}
}
// 收益中心:旧路由重定向页 / 用户中心 tab=revenue。
$is_revenue_surface = is_page_template('templates/pages/page-revenue.php')
|| (bool) get_query_var('aksrt_revenue')
|| (
(function_exists('aksrt_is_user_center_page') && aksrt_is_user_center_page())
&& isset($_GET['tab'])
&& 'revenue' === sanitize_key((string) wp_unslash($_GET['tab']))
);
if ($is_revenue_surface && !$is_profile_page) {
$revenue_js = get_template_directory() . '/assets/js/revenue.js';
wp_enqueue_script(
'aksrt-revenue',
$base_uri . '/assets/js/revenue.js',
['aksrt-script'],
file_exists($revenue_js) ? (string) filemtime($revenue_js) : $version,
true
);
}
// 消息中心页
if (is_page_template('templates/pages/page-messages.php')) {
$messages_css = get_template_directory() . '/assets/css/frontend/messages.css';
+1 -1
View File
@@ -264,7 +264,7 @@ trait ScriptsPart2 {
*/
private function resolve_frontend_routes(): array {
return [
'orders' => $this->resolve_template_page_url('templates/pages/page-orders.php', '/orders/'),
'orders' => function_exists('aksrt_get_user_center_tab_url') ? aksrt_get_user_center_tab_url('orders') : home_url('/user/orders/'),
'resources' => home_url('/resources/'),
];
}
-4
View File
@@ -39,17 +39,13 @@ class Templates {
'templates/pages/page-profile.php' => __('AKSRT - 个人中心', 'aksrt'),
'templates/pages/page-user-home.php' => __('AKSRT - 用户主页', 'aksrt'),
'templates/pages/page-archives.php' => __('AKSRT - 文章归档', 'aksrt'),
'templates/pages/page-documentnav.php' => __('AKSRT - 文档导航', 'aksrt'),
'templates/pages/page-categories.php' => __('AKSRT - 分类汇总', 'aksrt'),
'templates/pages/page-newposts.php' => __('AKSRT - 最新文章', 'aksrt'),
// 注意:page-newposts = 最新文章列表;投稿页是 page-submit(写文章/投稿)
'templates/pages/page-submit.php' => __('AKSRT - 写文章/投稿', 'aksrt'),
'templates/pages/page-posts-navs.php' => __('AKSRT - 文章导航', 'aksrt'),
'templates/pages/page-friends.php' => __('AKSRT - 友情链接', 'aksrt'),
'templates/pages/page-links.php' => __('AKSRT - 网址导航', 'aksrt'),
'templates/pages/page-ban-appeal.php' => __('AKSRT - 禁封申诉', 'aksrt'),
'templates/pages/page-orders.php' => __('AKSRT - 订单中心', 'aksrt'),
'templates/pages/page-revenue.php' => __('AKSRT - 收益中心', 'aksrt'),
'templates/pages/page-messages.php' => __('AKSRT - 消息中心', 'aksrt'),
];
}
+3 -15
View File
@@ -408,20 +408,8 @@ class Gateway {
}
private function orders_page_url(): string {
$pages = get_posts([
'post_type' => 'page',
'post_status' => 'publish',
'posts_per_page' => 1,
'meta_key' => '_wp_page_template',
'meta_value' => 'templates/pages/page-orders.php',
'fields' => 'ids',
'no_found_rows' => true,
]);
if (!empty($pages)) {
return (string) get_permalink((int) $pages[0]);
}
return home_url('/orders/');
return function_exists('aksrt_get_user_center_tab_url')
? aksrt_get_user_center_tab_url('orders')
: home_url('/user/orders/');
}
}
-7
View File
@@ -27,13 +27,6 @@ trait DomainGroups {
Integration::update_wechat_settings($settings);
}
/**
* 获取文档模式设置
*/
public static function get_docs_mode_settings(): array {
return Integration::get_docs_mode_settings();
}
/**
* 获取全局背景设置
*/
-51
View File
@@ -567,55 +567,4 @@ class Integration {
update_option('aksrt_wechat_settings', $clean);
}
/**
* 获取文档模式设置
*/
public static function get_docs_mode_settings(): array {
$all = Settings::get_all();
$template = in_array($all['docs_mode_default_template'] ?? '', ['documentnav', 'special'], true)
? $all['docs_mode_default_template']
: 'documentnav';
$order = in_array($all['docs_mode_default_order'] ?? '', ['menu_order', 'date', 'title'], true)
? $all['docs_mode_default_order']
: 'menu_order';
$style = in_array($all['docs_mode_style'] ?? '', ['default', 'simple', 'card'], true)
? $all['docs_mode_style']
: 'default';
$raw_cats = $all['docs_mode_cats'] ?? [];
if (!is_array($raw_cats)) {
$raw_cats = array_filter(array_map('trim', explode(',', (string) $raw_cats)));
}
$cats = array_values(array_filter(array_map('absint', $raw_cats)));
return [
'enabled' => !empty($all['docs_mode_enabled']),
'default_template' => $template,
'toc_enabled' => !isset($all['docs_mode_toc_enabled']) || !empty($all['docs_mode_toc_enabled']),
'chapter_nav_enabled' => !isset($all['docs_mode_chapter_nav_enabled']) || !empty($all['docs_mode_chapter_nav_enabled']),
'default_order' => $order,
'cats' => $cats,
'exclude' => !isset($all['docs_mode_exclude']) || !empty($all['docs_mode_exclude']),
'toc_depth' => max(1, min(6, (int) ($all['docs_mode_toc_depth'] ?? 3))),
'chapter_title' => sanitize_text_field((string) ($all['docs_mode_chapter_title'] ?? '')),
'style' => $style,
'icon' => !isset($all['docs_mode_icon']) || !empty($all['docs_mode_icon']),
'count' => !isset($all['docs_mode_count']) || !empty($all['docs_mode_count']),
'docs_mode_enabled' => !empty($all['docs_mode_enabled']),
'docs_mode_default_template' => $template,
'docs_mode_toc_enabled' => !isset($all['docs_mode_toc_enabled']) || !empty($all['docs_mode_toc_enabled']),
'docs_mode_chapter_nav_enabled' => !isset($all['docs_mode_chapter_nav_enabled']) || !empty($all['docs_mode_chapter_nav_enabled']),
'docs_mode_default_order' => $order,
'docs_mode_cats' => $cats,
'docs_mode_exclude' => !isset($all['docs_mode_exclude']) || !empty($all['docs_mode_exclude']),
'docs_mode_toc_depth' => max(1, min(6, (int) ($all['docs_mode_toc_depth'] ?? 3))),
'docs_mode_chapter_title' => sanitize_text_field((string) ($all['docs_mode_chapter_title'] ?? '')),
'docs_mode_style' => $style,
'docs_mode_icon' => !isset($all['docs_mode_icon']) || !empty($all['docs_mode_icon']),
'docs_mode_count' => !isset($all['docs_mode_count']) || !empty($all['docs_mode_count']),
];
}
}
+22 -4
View File
@@ -141,6 +141,18 @@ class Center {
'priority' => 18,
'type' => 'panel',
],
'posts' => [
'label' => __('文章', 'aksrt'),
'icon' => 'file-alt',
'priority' => 20,
'type' => 'panel',
],
'follow' => [
'label' => __('粉丝', 'aksrt'),
'icon' => 'users',
'priority' => 25,
'type' => 'panel',
],
'comments' => [
'label' => __('我的评论', 'aksrt'),
'icon' => 'comment',
@@ -200,10 +212,7 @@ class Center {
];
$this->panels = [];
$configured = array_values(array_filter(
array_map('sanitize_key', (array) ($settings['tabs'] ?? [])),
static fn(string $key): bool => $key !== 'posts'
));
$configured = array_values(array_map('sanitize_key', (array) ($settings['tabs'] ?? [])));
if ($configured === []) {
$configured = ['profile', 'comments', 'favorites'];
}
@@ -232,6 +241,15 @@ class Center {
$order = array_values(array_filter($order, static fn(string $key): bool => $key !== 'creator'));
}
if (!in_array('posts', $order, true)) {
$insert_at = array_search('creator', $order, true);
array_splice($order, $insert_at === false ? 0 : (int) $insert_at + 1, 0, ['posts']);
}
if (!in_array('follow', $order, true)) {
$insert_at = array_search('posts', $order, true);
array_splice($order, $insert_at === false ? count($order) : (int) $insert_at + 1, 0, ['follow']);
}
// Growth level is a dedicated user-center surface, separate from spendable points.
if ($level_on && !in_array('level', $order, true)) {
$insert_at = array_search('profile', $order, true);
-116
View File
@@ -1,116 +0,0 @@
<?php
/**
* Template Name: AKSRT - 文档导航
* Template Post Type: page
*
* Zibll pages/documentnav.php 1:1 — category accordion + search + AJAX posts.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
use AKSRT\Core\DocumentNav;
$post_id = (int) get_queried_object_id();
$opt = class_exists(DocumentNav::class)
? DocumentNav::get_options($post_id)
: ['cat' => 0, 'initial_content' => 'updated_posts'];
$one_cat = (int) ($opt['cat'] ?? 0);
$initial = (string) ($opt['initial_content'] ?? 'updated_posts');
get_header();
?>
<main class="container page-template-documentnav">
<div class="content-wrap">
<div class="content-layout">
<div class="theme-box radius8 main-bg main-shadow box-body mb20">
<h1 class="article-title"><?php echo esc_html(get_the_title() ?: __('文档导航', 'aksrt')); ?></h1>
</div>
<div class="row document-nav-row">
<div class="col-sm-4 document-nav-aside">
<div class="theme-box zib-widget dosc-nav document-nav">
<?php
if ($one_cat > 0 && class_exists(DocumentNav::class)) {
$open = true;
echo DocumentNav::render_panels($one_cat, 'accordion', 'panel', $open); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} elseif (current_user_can('edit_pages')) {
echo '<p class="muted-2-color box-body">' . esc_html__('请在页面设置中选择一级分类。', 'aksrt') . '</p>';
} else {
echo '<p class="muted-2-color box-body">' . esc_html__('暂无文档分类', 'aksrt') . '</p>';
}
?>
</div>
</div>
<div class="col-sm-8 document-nav-main" role="main">
<div class="theme-box zib-widget document-search relative-h">
<form class="document-search-form" action="javascript:;" method="get">
<div class="line-form">
<input type="search" name="s" class="line-form-input document-search-input" placeholder="<?php esc_attr_e('搜索文档…', 'aksrt'); ?>" autocomplete="off" required minlength="2">
<button type="submit" class="abs-right null" aria-label="<?php esc_attr_e('搜索', 'aksrt'); ?>">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
</div>
</form>
<div class="history-search mt10">
<div>
<?php
if (class_exists(DocumentNav::class)) {
echo DocumentNav::get_instance()->history_buttons_html(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
</div>
</div>
<div class="search-loading absolute main-bg flex jc" hidden>
<i class="loading mr10 em12" aria-hidden="true"></i> <?php esc_html_e('正在搜索', 'aksrt'); ?>
</div>
</div>
<div class="document-nav-container" data-one-cat="<?php echo (int) $one_cat; ?>">
<?php
if ($initial === 'page_content') {
while (have_posts()) {
the_post();
echo '<div class="nopw-sm box-body theme-box radius8 main-bg main-shadow">';
echo '<article class="article wp-posts-content">';
the_content();
echo '</article></div>';
}
} elseif ($one_cat > 0 && class_exists(DocumentNav::class)) {
$args = [
'cat' => $one_cat,
'order' => 'DESC',
'orderby' => 'modified',
'posts_per_page' => 12,
'no_found_rows' => true,
'ignore_sticky_posts' => true,
'post_status' => 'publish',
];
if ($initial === 'date_posts') {
$args['orderby'] = 'date';
} elseif ($initial === 'views_posts') {
$args['orderby'] = 'meta_value_num';
$args['meta_key'] = 'aksrt_post_views';
}
$q = new WP_Query($args);
echo DocumentNav::render_posts_html($q); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
echo '<div class="zib-widget"><p class="muted-2-color text-center">' . esc_html__('请配置文档分类', 'aksrt') . '</p></div>';
}
?>
</div>
</div>
</div>
<?php
if (comments_open() || get_comments_number()) {
comments_template();
}
?>
</div>
</div>
<?php get_sidebar(); ?>
</main>
<?php
get_footer();
-82
View File
@@ -1,82 +0,0 @@
<?php
/**
* Template Name: AKSRT - 网址导航
* Template Post Type: page
*
* Zibll pages/links.php 1:1 — multi-category bookmarks, search cover, submit modal.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$post_id = (int) get_queried_object_id();
$s = function_exists('aksrt_get_page_links_settings')
? aksrt_get_page_links_settings($post_id)
: [];
get_header();
?>
<main class="container page-content-default page-template-links">
<div class="content-wrap">
<div class="content-layout">
<?php
while (have_posts()) :
the_post();
// Optional page header (style 1 / 2 / 3 via existing page meta).
if (function_exists('aksrt_get_page_header_html')) {
echo aksrt_get_page_header_html($post_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
$header_style = (string) get_post_meta($post_id, 'page_header_style', true);
if ($header_style !== 'not') {
echo '<div class="theme-box radius8 main-bg main-shadow box-body mb20"><h1 class="article-title">' . esc_html(get_the_title()) . '</h1></div>';
}
}
if (function_exists('aksrt_link_get_search_cover')) {
echo aksrt_link_get_search_cover($post_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
$links_html = function_exists('aksrt_page_links_html') ? aksrt_page_links_html($post_id) : '';
$content_s = !empty($s['content_s']);
$position = (string) ($s['content_position'] ?? 'top');
if ($position !== 'top') {
echo $links_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
if ($content_s) {
echo '<div class="zib-widget"><article class="article wp-posts-content">';
the_content();
wp_link_pages([
'before' => '<p class="text-center post-nav-links radius8 padding-6">',
'after' => '</p>',
]);
echo '</article></div>';
}
if ($position === 'top') {
echo $links_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
endwhile;
if (comments_open() || get_comments_number()) {
comments_template();
}
?>
</div>
</div>
<?php get_sidebar(); ?>
</main>
<?php
if (!empty($s['submit_s']) && function_exists('aksrt_submit_links_modal')) {
echo aksrt_submit_links_modal([ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
'title' => (string) ($s['submit_title'] ?? __('申请入驻', 'aksrt')),
'dec' => (string) ($s['submit_dec'] ?? ''),
'sign' => !empty($s['submit_sign_s']),
'cats' => (array) ($s['submit_cats'] ?? []),
]);
}
get_footer();
-31
View File
@@ -1,31 +0,0 @@
<?php
/**
* Template Name: AKSRT - Orders
* Template Post Type: page
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$target = function_exists('aksrt_get_user_center_url')
? aksrt_get_user_center_url('orders')
: home_url('/profile/?tab=orders');
if (!is_user_logged_in()) {
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url($target);
wp_safe_redirect($login_url);
exit;
}
$raw = [
'status' => $_GET['order_status'] ?? '',
'type' => $_GET['order_type'] ?? '',
'order_number' => $_GET['order_number'] ?? '',
'page' => $_GET['order_page'] ?? 1,
];
$query = \AKSRT\Payment\OrderCenterQuery::from_array($raw);
$params = array_merge(['tab' => 'orders'], $query->url_values());
wp_safe_redirect(add_query_arg($params, $target), 302);
exit;
-27
View File
@@ -1,27 +0,0 @@
<?php
/**
* Template Name: AKSRT - Revenue
* Template Post Type: page
*
* Legacy revenue route canonicalizes to the private user-center tab.
*
* @package AKSRT
*/
defined('ABSPATH') || exit;
$target = function_exists('aksrt_get_user_center_tab_url')
? aksrt_get_user_center_tab_url('revenue')
: home_url('/profile/?tab=revenue');
$rev_page = isset($_GET['rev_page']) ? max(1, (int) $_GET['rev_page']) : 1;
if ($rev_page > 1) {
$target = add_query_arg('rev_page', $rev_page, $target);
}
if (!is_user_logged_in()) {
wp_safe_redirect(aksrt_get_login_url($target));
exit;
}
wp_safe_redirect($target);
exit;
-5
View File
@@ -29,9 +29,6 @@ if ($presenter) {
$root_page = get_post($root_id);
// Quick search for docs
$doc_settings = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_docs_mode_settings() : [];
$show_search = !empty($doc_settings['toc_enabled']);
?>
<aside class="aksrt-doc-sidebar" role="navigation" aria-label="<?php esc_attr_e('文档导航', 'aksrt'); ?>">
<div class="aksrt-doc-sidebar__header">
@@ -41,12 +38,10 @@ $show_search = !empty($doc_settings['toc_enabled']);
</a>
</div>
<?php if ($show_search) : ?>
<div class="aksrt-doc-sidebar__search">
<input type="text" class="aksrt-doc-sidebar__search-input" placeholder="<?php esc_attr_e('搜索文档...', 'aksrt'); ?>" aria-label="<?php esc_attr_e('搜索文档', 'aksrt'); ?>">
<i class="fas fa-search aksrt-doc-sidebar__search-icon"></i>
</div>
<?php endif; ?>
<?php if (!empty($doc_pages)) : ?>
<ul class="aksrt-doc-sidebar__nav">
+3 -2
View File
@@ -17,11 +17,12 @@ if (!function_exists('aksrt_render_profile_pagination')) {
* @param int $current 当前页
* @param string $param URL 参数名
*/
function aksrt_render_profile_pagination($total_pages, $current, $param) {
function aksrt_render_profile_pagination($total_pages, $current, $param, $base_url = '') {
if ($total_pages <= 1) {
return;
}
$base = remove_query_arg($param);
$base = $base_url !== '' ? $base_url : remove_query_arg($param);
$base = remove_query_arg($param, $base);
$base = add_query_arg($param, '%#%', $base);
$links = paginate_links([
'base' => $base,