- Rewrite post_list/submit schema (PHP + JS) with the reference theme's field set, order, defaults and dependencies; topics fields excluded - Map PostDisplay::get_submit_settings() and the FrontSubmit runtime to the new keys without legacy fallback - Guest submission follows the capability matrix; category required on submit; pay limits read from the cash/points fieldset - Drop dead front_submit_* settings, template branches and JS config - Add submit settings reference contract test
184 lines
8.0 KiB
JavaScript
184 lines
8.0 KiB
JavaScript
'use strict';
|
||
|
||
/**
|
||
* 「前台投稿」后台设置页契约(对齐参考主题 1:1,不含专题):
|
||
* 1) PHP schema(SchemaPostList.php submit section)字段键、顺序、默认值与
|
||
* JS schema(assets/admin/src/schema/sections/post-list/submit.js)一致;
|
||
* 2) 参考主题字段全集存在:post_article_s / post_article_btn_txte / post_article_cat /
|
||
* post_article_custom_filter_s / post_article_tag / post_article_user /
|
||
* post_article_title_strlen_limit / post_article_pay_free_s /
|
||
* post_article_pay_price_limit / post_article_pay_vip_price_s /
|
||
* post_article_pay_vip_1_discount / post_article_pay_vip_2_discount /
|
||
* post_t_placeholder / post_c_placeholder;
|
||
* 3) 专题(topics)相关字段不存在;
|
||
* 4) 旧 AKSRT 私有字段(front_submit_*、post_article_title_strlen_limit_min/max、
|
||
* post_article_pay_price_min/max)已彻底移除,无双路兼容;
|
||
* 5) 运行时(PostDisplay::get_submit_settings)只读取新键。
|
||
*/
|
||
|
||
const assert = require('node:assert/strict');
|
||
const fs = require('node:fs');
|
||
const path = require('node:path');
|
||
const test = require('node:test');
|
||
|
||
const root = path.resolve(__dirname, '..', '..');
|
||
const php = fs.readFileSync(path.join(root, 'src/Admin/Schema/SchemaPostList.php'), 'utf8');
|
||
const js = fs.readFileSync(path.join(root, 'assets/admin/src/schema/sections/post-list/submit.js'), 'utf8');
|
||
const runtime = fs.readFileSync(path.join(root, 'src/Settings/PostDisplay.php'), 'utf8');
|
||
|
||
const REFERENCE_KEYS = [
|
||
'post_article_s',
|
||
'post_article_btn_txte',
|
||
'post_article_cat',
|
||
'post_article_custom_filter_s',
|
||
'post_article_tag',
|
||
'post_article_user',
|
||
'post_article_title_strlen_limit',
|
||
'post_article_pay_free_s',
|
||
'post_article_pay_price_limit',
|
||
'post_article_pay_vip_price_s',
|
||
'post_article_pay_vip_1_discount',
|
||
'post_article_pay_vip_2_discount',
|
||
'post_t_placeholder',
|
||
'post_c_placeholder',
|
||
];
|
||
|
||
// PHP 源码中字段定义的完整出现顺序(含 post_article_pay_price_limit 的
|
||
// fieldset 嵌套子字段 price / points)。
|
||
const PHP_FIELD_ORDER = [
|
||
...REFERENCE_KEYS.slice(0, REFERENCE_KEYS.indexOf('post_article_pay_vip_price_s')),
|
||
'price',
|
||
'points',
|
||
...REFERENCE_KEYS.slice(REFERENCE_KEYS.indexOf('post_article_pay_vip_price_s')),
|
||
];
|
||
|
||
const LEGACY_KEYS = [
|
||
'front_submit_enabled',
|
||
'front_submit_roles',
|
||
'front_submit_title',
|
||
'front_submit_content',
|
||
'front_submit_excerpt',
|
||
'front_submit_cover',
|
||
'front_submit_category',
|
||
'front_submit_guest_enabled',
|
||
'front_submit_tags',
|
||
'front_submit_custom_fields',
|
||
'front_submit_review_mode',
|
||
'front_submit_cover_required',
|
||
'front_submit_max_images',
|
||
'front_submit_editor_mode',
|
||
'front_submit_draft_save',
|
||
'front_submit_leave_warning',
|
||
'post_article_title_strlen_limit_min',
|
||
'post_article_title_strlen_limit_max',
|
||
'post_article_pay_price_min',
|
||
'post_article_pay_price_max',
|
||
];
|
||
|
||
/** Extract the PHP 'submit' => [ ... ] section body (balanced brackets). */
|
||
function phpSubmitSection(src) {
|
||
const start = src.indexOf("'submit' => [");
|
||
assert.notEqual(start, -1, 'PHP schema has a submit section');
|
||
const open = src.indexOf('[', start);
|
||
let depth = 0;
|
||
for (let i = open; i < src.length; i++) {
|
||
const ch = src[i];
|
||
if (ch === "'" || ch === '"') {
|
||
const quote = ch;
|
||
i++;
|
||
while (i < src.length) {
|
||
if (src[i] === '\\') { i += 2; continue; }
|
||
if (src[i] === quote) break;
|
||
i++;
|
||
}
|
||
continue;
|
||
}
|
||
if (ch === '[') depth++;
|
||
else if (ch === ']') {
|
||
depth--;
|
||
if (depth === 0) return src.slice(open, i + 1);
|
||
}
|
||
}
|
||
throw new Error('unbalanced submit section');
|
||
}
|
||
|
||
const submitPhp = phpSubmitSection(php);
|
||
|
||
test('PHP submit section 字段键与参考主题一致且顺序相同', () => {
|
||
// 字段定义均为 ['key' => '...', 'label' => ...] 开头;dependsOn 内的
|
||
// ['key' => '...', 'value' => ...] 不是字段定义,须排除。
|
||
const found = [];
|
||
const re = /'key'\s*=>\s*'([a-z0-9_]+)',\s*'label'/g;
|
||
let m;
|
||
while ((m = re.exec(submitPhp)) !== null) found.push(m[1]);
|
||
assert.deepEqual(found, PHP_FIELD_ORDER);
|
||
});
|
||
|
||
test('PHP submit section 不含专题字段', () => {
|
||
assert.doesNotMatch(submitPhp, /post_article_topics/);
|
||
assert.doesNotMatch(submitPhp, /'topics'/);
|
||
});
|
||
|
||
test('PHP submit section 默认值对齐参考主题', () => {
|
||
assert.match(submitPhp, /'post_article_s',\s*'label'\s*=>\s*'启用前台投稿',\s*'type'\s*=>\s*'switcher',\s*'default'\s*=>\s*true/);
|
||
assert.match(submitPhp, /'post_article_btn_txte',\s*'label'\s*=>\s*'按钮文字',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'发布文章'/);
|
||
assert.match(submitPhp, /'post_article_user',\s*'label'\s*=>\s*'免登陆投稿发布用户',\s*'type'\s*=>\s*'user_select',\s*'default'\s*=>\s*1/);
|
||
assert.match(submitPhp, /'post_article_title_strlen_limit',\s*'label'\s*=>\s*'标题字数限制',\s*'type'\s*=>\s*'between_number',\s*'default'\s*=>\s*\['min'\s*=>\s*5,\s*'max'\s*=>\s*30\]/);
|
||
assert.match(submitPhp, /'post_article_pay_vip_1_discount',\s*'label'\s*=>\s*'一级会员折扣',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*100/);
|
||
assert.match(submitPhp, /'post_article_pay_vip_2_discount',\s*'label'\s*=>\s*'二级会员折扣',\s*'type'\s*=>\s*'number',\s*'default'\s*=>\s*100/);
|
||
assert.match(submitPhp, /'post_t_placeholder',\s*'label'\s*=>\s*'投稿标题占位符',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'请输入标题'/);
|
||
assert.match(submitPhp, /'post_c_placeholder',\s*'label'\s*=>\s*'投稿内容占位符',\s*'type'\s*=>\s*'input',\s*'default'\s*=>\s*'请输入内容'/);
|
||
assert.match(submitPhp, /'price',\s*'label'\s*=>\s*'现金金额限制'/);
|
||
assert.match(submitPhp, /'points',\s*'label'\s*=>\s*'积分金额限制'/);
|
||
});
|
||
|
||
test('会员折扣仅在关闭手动会员价时显示(对齐参考主题 dependency)', () => {
|
||
assert.match(
|
||
submitPhp,
|
||
/'post_article_pay_vip_1_discount'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'post_article_s',\s*'value'\s*=>\s*true\],\s*\['key'\s*=>\s*'post_article_pay_vip_price_s',\s*'value'\s*=>\s*false\]\]\]/
|
||
);
|
||
assert.match(
|
||
submitPhp,
|
||
/'post_article_pay_vip_2_discount'[\s\S]*?'dependsOn'\s*=>\s*\['all'\s*=>\s*\[\['key'\s*=>\s*'post_article_s',\s*'value'\s*=>\s*true\],\s*\['key'\s*=>\s*'post_article_pay_vip_price_s',\s*'value'\s*=>\s*false\]\]\]/
|
||
);
|
||
});
|
||
|
||
test('JS schema 与 PHP 字段键一致(ui_only content 行除外)', () => {
|
||
// 字段定义为 { key: '...', label: ... };dependsOn 内的 { key, value } 须排除。
|
||
const keys = [];
|
||
const re = /key:\s*'([a-z0-9_]+)',\s*label:/g;
|
||
let m;
|
||
while ((m = re.exec(js)) !== null) keys.push(m[1]);
|
||
const persisted = keys.filter((k) => REFERENCE_KEYS.includes(k));
|
||
assert.deepEqual(persisted, REFERENCE_KEYS);
|
||
for (const legacy of LEGACY_KEYS) {
|
||
assert.ok(!keys.includes(legacy), `JS schema 残留旧字段: ${legacy}`);
|
||
}
|
||
// 两个 ui_only 提示行承载参考主题 desc 中的【去设置】链接。
|
||
assert.match(js, /permissions\/capabilities/);
|
||
assert.match(js, /post-list\/filter/);
|
||
assert.match(js, /ui_only:\s*true/);
|
||
});
|
||
|
||
test('JS schema 默认值对齐参考主题', () => {
|
||
assert.match(js, /post_article_user:\s*1\b/);
|
||
assert.match(js, /post_article_pay_vip_1_discount:\s*100\b/);
|
||
assert.match(js, /post_article_pay_vip_2_discount:\s*100\b/);
|
||
assert.match(js, /post_t_placeholder:\s*'请输入标题'/);
|
||
assert.match(js, /post_c_placeholder:\s*'请输入内容'/);
|
||
assert.match(js, /price:\s*\{\s*min:\s*0,\s*max:\s*99999\s*\}/);
|
||
});
|
||
|
||
test('旧字段在 schema 与运行时中均已移除(无双路)', () => {
|
||
for (const legacy of LEGACY_KEYS) {
|
||
assert.ok(!php.includes(legacy), `PHP schema 残留旧字段: ${legacy}`);
|
||
assert.ok(!runtime.includes(legacy), `PostDisplay 残留旧字段: ${legacy}`);
|
||
}
|
||
// 运行时读取新键。
|
||
assert.match(runtime, /\$settings\['post_article_s'\]/);
|
||
assert.match(runtime, /\$settings\['post_article_title_strlen_limit'\]/);
|
||
assert.match(runtime, /\$settings\['post_article_pay_price_limit'\]/);
|
||
assert.match(runtime, /\$settings\['post_article_tag'\]/);
|
||
assert.match(runtime, /\$settings\['post_article_user'\]/);
|
||
});
|