Files
wordpress-kratos-yang/inc/options-framework/js/options-custom.js
T
laoyang 0a5486e195 3.2.57 新增「写作增强」:发布时自动生成别名/标签/分类/摘要 #25
需求(工单 #25):写文章时自动生成别名(翻译)、自动生成标签、
自动选择分类、自动生成摘要,问主题能不能做、是否必须用插件。
结论:主题自己就能做,不依赖任何插件。

新增 inc/theme-writing.php(四个独立引擎 + 设置读写 + 密钥处理):

- 触发点:transition_post_status,且要求
  new_status === publish && old_status !== publish
  ⇒ 只在「第一次点发布」时动手,自动保存不触发,已发布的文章一概不碰。
- 幂等锁 _yang_writing_done:同一篇只做一次,避免反复覆写。

四道「不覆盖」守卫(用户逐条确认的决策):
- 摘要:只在 post_excerpt 为空时才写。
- 分类:只在文章还挂默认分类(未分类)时才动。
- 别名:只在「空」或「不是纯 ASCII」时才生成。判据不是"空"而是
  "不是纯 ASCII" —— 中文标题下 WP 生成的是 %e4%bf%a1... 这种编码串,
  它非空但不可用。已有英文别名的 966 篇一概不动(别名=URL,改了
  就是成批 404)。
- 标签:wp_set_post_tags(..., true) 追加,绝不删除手工标签。

修一个真实产品 bug:3.2.56 及以前 apply() 里写的是
list($suggest, $_) = yang_writing_make_tags(...),$new 被整个丢掉;
接着又拿 $suggest 去 get_term_by 二次分类 —— 而 $suggest 里每个词
都来自词典、必然全部命中 ⇒「新建」分支永远进不去 ⇒ 这条功能在真实
发布流程里一次都没生效过。改为 list($exist_hits, $new_cands),
两段分别复核后合并。

标签两级策略(新博客也能用):先匹配已有标签池,匹配不到才新建,
每篇新增数量受 w_tag_new_limit 约束(默认 3,设 0 = 永不新建)。

中文分词(服务器无 scws/jieba):先按虚词与标点切短语,再在短语
内部取 2~6 字 n-gram。不切句直接取窗口会产出「星等换算与赤」
这类跨词碎片,且字面上"出现 ≥2 次"能通过所有频次闸门 —— 这是
n-gram 的固有缺陷,只能靠切分规避,调阈值无效。
虚词表统一由 yang_writing_seps() 提供(闸门④ 复用同一函数)。

翻译:默认 MyMemory(免注册),可选百度/有道。任何失败都返回空串
跳过该项,绝不阻塞发布。

密钥:存独立选项 yang_writing_secrets,不进 kratos;password 类型 +
std 空串;页面只显示末 4 位;留空提交 = 不修改。

设置页:新增「写作增强」独立 heading 标签页,四个开关默认全关
(升级不改变现有行为)。framework 里 'requires' 这个键根本没被读取,
条件显示改用 options-custom.js 显式逻辑;info 类型不生成 .section
包裹层,补一条 .section-info 规则才能藏住服务商申请说明。

验证(发布前):
- 写作端到端 71 条、后台渲染 21 条、密钥路径 9 条,合计 101 条全过
- 反证 22 组(W1~W16 端到端 / A1~A6 后台)× R1~R14 发布包注入,
  全部让失败数从 0 增加
- 测试自身不污染:每节差分回滚 + 总闸门,标签库稳定 703 / 967 篇
- 发布走 theme-update/,主题目录仍是 3.2.56,未被覆盖
2026-10-27 16:05:43 +08:00

231 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Custom scripts needed for the colorpicker, image button selectors,
* and navigation tabs.
*/
jQuery(document).ready(function ($) {
$('input[id^="s_"]').click(function () {
jQuery('#section-' + this.id + '_url').fadeToggle(400)
jQuery('#section-' + this.id + '_links').fadeToggle(400)
})
for (i = 0; i < $('input[id^="s_"]' + ':checked').length; i++) {
let id = $('input[id^="s_"]' + ':checked')[i].id
$('#section-' + id + '_url').show()
$('#section-' + id + '_links').show()
}
jQuery('#g_cos').click(function () {
jQuery('#section-g_cos_bucketname').fadeToggle(400)
jQuery('#section-g_cos_url').fadeToggle(400)
jQuery('#section-g_cos_accesskey').fadeToggle(400)
jQuery('#section-g_cos_secretkey').fadeToggle(400)
})
if (jQuery('#g_cos:checked').val() !== undefined) {
jQuery('#section-g_cos_bucketname').show()
jQuery('#section-g_cos_url').show()
jQuery('#section-g_cos_accesskey').show()
jQuery('#section-g_cos_secretkey').show()
}
jQuery('#g_cc_switch').click(function () {
jQuery('#section-g_cc').fadeToggle(400)
})
if (jQuery('#g_cc_switch:checked').val() !== undefined) {
jQuery('#section-g_cc').show()
}
jQuery('#g_donate').click(function () {
jQuery('#section-g_donate_alipay').fadeToggle(400)
jQuery('#section-g_donate_wechat').fadeToggle(400)
})
if (jQuery('#g_donate:checked').val() !== undefined) {
jQuery('#section-g_donate_alipay').show()
jQuery('#section-g_donate_wechat').show()
}
jQuery('#m_smtp').click(function () {
jQuery('#section-m_host').fadeToggle(400)
jQuery('#section-m_sec').fadeToggle(400)
jQuery('#section-m_port').fadeToggle(400)
jQuery('#section-m_username').fadeToggle(400)
jQuery('#section-m_passwd').fadeToggle(400)
jQuery('#section-m_sendmail').fadeToggle(400)
})
if (jQuery('#m_smtp:checked').val() !== undefined) {
jQuery('#section-m_host').show()
jQuery('#section-m_sec').show()
jQuery('#section-m_port').show()
jQuery('#section-m_username').show()
jQuery('#section-m_passwd').show()
jQuery('#section-m_sendmail').show()
}
jQuery('#g_thumbnail').click(function () {
jQuery('#section-g_postthumbnail').fadeToggle(400)
})
if (jQuery('#g_thumbnail:checked').val() !== undefined) {
jQuery('#section-g_postthumbnail').show()
}
jQuery('#top_select').change(function () {
if (jQuery("#top_select").val() == 'color') {
jQuery('#section-top_color').fadeIn(400)
jQuery('#section-top_img').fadeOut(400)
jQuery('#section-top_title').fadeOut(400)
jQuery('#section-top_describe').fadeOut(400)
} else {
jQuery('#section-top_color').fadeOut(400)
jQuery('#section-top_img').fadeIn(400)
jQuery('#section-top_title').fadeIn(400)
jQuery('#section-top_describe').fadeIn(400)
}
})
if (jQuery('#top_select').val() == 'color') {
jQuery('#section-top_color').show()
jQuery('#section-top_img').hide()
jQuery('#section-top_title').hide()
jQuery('#section-top_describe').hide()
} else {
jQuery('#section-top_color').hide()
jQuery('#section-top_img').show()
jQuery('#section-top_title').show()
jQuery('#section-top_describe').show()
}
// 自动目录:字段显隐跟着两个开关走。
//
// 「在正文中显示」勾了 → 「正文中的对齐」「自动插入位置」才有意义
// 「浮动显示」勾了 → 「浮动方向」才有意义
//
// 为什么必须藏起来:两个开关任一关闭时,它下面那几个子选项完全无效。
// 若还留着,用户会以为「选了文章结尾却没插进正文」是个 bug。
//
// 用 class 切换而不是 fadeIn/fadeOut:.section 的 display 是 flow-root
// (3.2.28 靠它建独立 BFC,避免被左侧浮动标签列的 clear:both 拉长)。
// jQuery 的 show()/hide() 会写行内 display,把那套布局打回 block,
// 等于悄悄回退 3.2.28 的修复。
//
// ⚠️ 绑定事件用 delegation('change' 挂 document、选择器过滤):
// 这两个是 checkbox,框架的 checkbox 控件可能在外面再包一层 label/inline,
// 直接绑在 input 上偶尔会因为 DOM 顺序变了而漏绑。delegation 不受影响。
function yq_toggle_toc_fields () {
var inline = jQuery('#toc_inline').is(':checked')
var float = jQuery('#toc_float').is(':checked')
jQuery('#section-toc_inline_align').toggleClass('yq-cond-hide', !inline)
jQuery('#section-toc_anchor').toggleClass('yq-cond-hide', !inline)
jQuery('#section-toc_float_side').toggleClass('yq-cond-hide', !float)
}
jQuery(document).on('change', '#toc_inline, #toc_float', yq_toggle_toc_fields)
yq_toggle_toc_fields()
// 写作增强:翻译服务商的四个凭据 + 说明,只在「自动生成英文别名」勾选时才有意义。
//
// 为什么用 requires 之外的方式:`requires` 这个键在框架里**根本没被读取**
// (grep 整个 options-framework 目录零命中,条件显示全靠本文件里这些
// 硬编码的 jQuery)。所以主题里其它 19 处 'requires' 其实都是死代码,
// 这里不再往那个坑里加,直接写显式逻辑。
//
// 为什么连说明文字一起藏:用户没勾别名生成时,那段「去百度申请 APPID」
// 只会是噪声。
//
// ⚠️ 用 delegation + toggleClass('yq-cond-hide'),不直接绑 input.hide():
// 原因与上面的自动目录一致(.section 的 display 是 flow-root,
// jQuery 会把行内 display 写成 block,回退 3.2.28 的布局修复)。
// info 那块没有 .section 类,靠 CSS 里补的 .section-info 规则命中。
function yq_toggle_writing_slug_fields () {
var on = jQuery('#w_on_slug').is(':checked')
jQuery('#section-w_provider').toggleClass('yq-cond-hide', !on)
jQuery('#section-w_baidu_appid').toggleClass('yq-cond-hide', !on)
jQuery('#section-w_baidu_key').toggleClass('yq-cond-hide', !on)
jQuery('#section-w_youdao_appid').toggleClass('yq-cond-hide', !on)
jQuery('#section-w_youdao_key').toggleClass('yq-cond-hide', !on)
jQuery('#section-w_timeout').toggleClass('yq-cond-hide', !on)
jQuery('#w_provider_help').toggleClass('yq-cond-hide', !on)
}
jQuery(document).on('change', '#w_on_slug', yq_toggle_writing_slug_fields)
yq_toggle_writing_slug_fields()
// 写作增强:「每篇最多新增几个标签」只在「自动生成标签」勾选时才有意义。
function yq_toggle_writing_tag_fields () {
var on = jQuery('#w_on_tag').is(':checked')
jQuery('#section-w_tag_new_limit').toggleClass('yq-cond-hide', !on)
}
jQuery(document).on('change', '#w_on_tag', yq_toggle_writing_tag_fields)
yq_toggle_writing_tag_fields()
// Loads the color pickers
$('.of-color').wpColorPicker()
// Image Options
$('.of-radio-img-img').click(function () {
$(this).parent().parent().find('.of-radio-img-img').removeClass('of-radio-img-selected')
$(this).addClass('of-radio-img-selected')
})
$('.of-radio-img-label').hide()
$('.of-radio-img-img').show()
$('.of-radio-img-radio').hide()
// Loads tabbed sections if they exist
if ($('.nav-tab-wrapper').length > 0) {
options_framework_tabs()
}
function options_framework_tabs () {
var $group = $('.group'),
$navtabs = $('.nav-tab-wrapper a'),
active_tab = ''
// Hides all the .group sections to start
$group.hide()
// Find if a selected tab is saved in localStorage
if (typeof (localStorage) != 'undefined') {
active_tab = localStorage.getItem('active_tab')
}
// If active tab is saved and exists, load it's .group
if (active_tab != '' && $(active_tab).length) {
$(active_tab).fadeIn()
$(active_tab + '-tab').addClass('nav-tab-active')
} else {
$('.group:first').fadeIn()
$('.nav-tab-wrapper a:first').addClass('nav-tab-active')
}
// Bind tabs clicks
$navtabs.click(function (e) {
e.preventDefault()
// Remove active class from all tabs
$navtabs.removeClass('nav-tab-active')
$(this).addClass('nav-tab-active').blur()
if (typeof (localStorage) != 'undefined') {
localStorage.setItem('active_tab', $(this).attr('href'))
}
var selected = $(this).attr('href')
$group.hide()
$(selected).fadeIn()
})
}
})