网站

Odoo 企业版知识库为什么不是“勾上网站发布就完了”:公开子树、发布传播与侧边栏可见性边界讲透

很多人以为企业版 website_knowledge 只是给 Knowledge 多一个前台页面。但从 models、controller 和测试一起看,官方真正做的是一套“公开子树”机制:发布状态会向后代传播、公开访问按根祖先裁剪、侧边栏只展示可达节点,连 summary / OG / Twitter 元信息都按前台阅读场景重新计算。

企业 网站
进阶 开发者 3 分钟阅读
0 评论 0 点赞 0 收藏 7 阅读

先说结论

website_knowledge 不是简单把 Knowledge 文章“前台渲染一下”。

models/knowledge_article.pycontrollers/main.pymodels/website.py 和几组测试连起来看,Odoo 企业版真正实现的是一个可公开的知识子树机制,而不是“整库公开”机制。

它至少同时守住了 5 条边界:

  1. 公开不是单篇行为,而是树状传播行为:父文章一旦发布,子孙节点会一起继承 website_published
  2. 公开访问按可达祖先裁剪:访客能看到的,不是所有文章,而是某个公开根以下、且自己沿祖先链可达的节点;
  3. 侧边栏不是全量目录:只展示当前文章相关、且公开可访问的祖先 / 子节点;
  4. 前台搜索只搜已发布内容website.searchable.mixin 接入后,Knowledge 被并入网站搜索,但 domain 明确锁在已发布文章;
  5. 前台 SEO 不是后台字段直出:summary、OpenGraph、Twitter meta 都是根据文章 body 重新整理,嵌入块还会被排除。

所以更准确的理解是:

Odoo 企业版 Knowledge 的网站公开能力,本质上是在私有知识树里切出一段“可安全暴露给访客的公开子站”,而不是把后台知识库直接搬到前台。

这也是为什么它放在企业版 website_knowledge 里,而不是社区版随手加一个路由就算完成。


一、核心心智:它公开的是“子树”,不是“整库”

最容易误解的一点,是把 website_published 理解成普通网站页面上的“这篇文章是否公开”。

website_knowledge/models/knowledge_article.py 里的 write() 已经说明,官方模型不是按“单页”想的,而是按“树”想的。

当写入值里出现:

  • website_published
  • is_published

模块会:

  • 找到当前记录的所有 child_of 后代;
  • 排除自己;
  • ignore_published_propagation 上下文再次写入;
  • 把新的发布状态同步给整棵后代子树。

也就是说:

  • 发布一个父节点,不只是发布父节点自己;
  • 取消发布一个父节点,也不只是收回当前页;
  • 它是在维护一段前台可访问的连续知识结构。

为什么官方要这样设计

因为知识库和博客不一样。

博客文章常常天然独立;但知识文章通常带有明显的层级:

  • 产品说明
  • 子主题
  • FAQ
  • 子流程
  • 附属条目

如果只公开某个父节点,不公开其子节点,访客点开后目录会断裂; 如果只公开子节点,不公开路径中的父节点,导航和上下文又会失真。

所以官方的选择是:

一旦你决定把某个知识树根公开给网站,系统默认帮你把这一段结构保持完整。

这就是“发布传播”的真实产品含义。


二、为什么新建在已公开父节点下的文章会自动公开

_prepare_article_create_values() 里还有一个很关键的小动作。

当你在某个 parent_id 下创建新文章,而且当前操作者是内部用户或 su 时,如果父文章已经 website_published,新文章会直接带上:

  • website_published = True

这意味着官方不仅在“改状态”时做传播,连“往公开子树里继续长新节点”时也会自动继承公开性。

这条规则解决了什么问题

如果没有它,就会出现一种很烦的运营故障:

  • 今天你公开了一个知识中心根节点;
  • 明天同事在这个根下面补了一篇“退货 FAQ”;
  • 结果后台看得到,网站前台却突然缺一块;
  • 最后你还得再回去补勾一次网站发布。

Odoo 直接把这个坑堵死了。

测试 test_knowledge_published_propagation 也明确验证:

  • 父节点发布后,孙辈也跟着公开;
  • 在已公开节点下新建文章时,新的文章会自动公开;
  • 但如果文章后来被移动成根节点,或被手动改成不公开,再移回公开树下,也不会无脑重新覆盖其状态。

这说明官方做得并不粗暴。它的策略不是“只要放进树里就强制公开”,而是:

  • 初次承接公开上下文时,给出合理默认;
  • 后续用户若明确改过状态,系统尊重人工决定。

这类边界非常企业化:

自动化负责减少漏操作,但不能永久剥夺人工控制。


三、公开访问为什么看的是“可访问根祖先”而不是单纯 parent 链

controllers/main.py 里最值得看的,不是路由本身,而是公开侧边栏和公开详情页如何判断“你现在到底属于哪棵公开树”。

比如:

  • /knowledge/article/<id>
  • /knowledge/public/sidebar
  • /knowledge/public/search

这些接口背后,都不只是简单按 parent_id 往上找。

特别是 get_public_sidebar_articles(),它先取:

  • _get_accessible_root_ancestors()

然后才基于这些 ancestor 生成前台可见目录。

这意味着什么

这意味着公开可见性不是“这篇文章自己勾了公开即可”,而是要看:

  • 它是否属于一条对当前访客真正可达的祖先链;
  • 这条祖先链上哪些节点是公开的;
  • 当前节点在这个可达结构里应该展示哪些兄弟 / 子项。

测试 test_get_accessible_root_ancestor 很能说明这层设计:

  • 若文章未发布,portal 用户拿不到任何可访问根祖先;
  • 若某个中间 child 被发布,则 grandchild 会把 child 和自己都识别为可达祖先;
  • 若改成通过成员邀请获得访问,则又会按成员权限重建可达祖先集合;
  • 某个父节点权限被拿掉时,祖先集合会重新收缩。

所以 _get_accessible_root_ancestors() 的意义,不只是“找父节点”,而是在统一回答一个问题:

对于当前用户,这篇文章在什么结构里是可见且可导航的?

这就是为什么前台目录不会简单照搬后台树。


四、侧边栏为什么只显示“相关且可访问”的节点

_redirect_to_public_view() 里,模板变量 show_sidebar 的判断很克制:

  • no_sidebar=True,当然不显示;
  • 若当前文章没有任何已发布父级或已发布子级,也不显示。

get_public_sidebar_articles() 返回的数据同样经过裁剪:

  • 必须 is_article_item = False
  • 必须在可访问祖先集合里,或是这些祖先的直接子级;
  • 并且会排除“当前文章自己作为 parent 的重复展开”场景。

测试 test_knowledge_public_article_routes 进一步把规则钉死了:

1. 公开根节点的侧边栏,只需要显示根本身

因为对访客而言,这是某个公开子站的入口页,而不是后台整库导航。

2. 当你打开公开子孙文章时,侧边栏会显示:

  • 根节点;
  • 中间父节点;
  • 当前节点;
  • 当前节点同层的公开兄弟(如果它们属于同一公开路径);

3. 未公开文章请求侧边栏时,直接返回空列表

这点很重要。官方不是“返回目录但点不开”,而是根本不把未公开结构暴露给前台

这正是安全边界应该有的样子。

为什么这套目录裁剪很重要

因为知识库和普通 CMS 页面不同,知识库最大的价值之一就是“结构化导航”。

但如果导航不做裁剪,会立刻出现两个问题:

  • 信息泄露:访客能看到不该看到的节点标题;
  • 体验混乱:访客会看到一堆跳不过去的灰色目录。

Odoo 这里选择的是更稳的路线:

前台导航宁可少,也不展示不可访问结构。


五、为什么 article item 不进公开 children / sidebar

在测试里,官方专门构造了:

  • 普通已发布子文章
  • 未发布子文章
  • is_article_item=True 的已发布 item

然后验证 /knowledge/public/children 只返回:

  • 已发布、且不是 item 的节点

这背后其实体现了 Knowledge 的两种内容形态:

  1. 可作为知识树节点导航的 article
  2. 更像叶子内容 / 内容项的 article item

对于后台协作来说,两者都可能有意义; 但对网站前台公开导航来说,官方认为:

  • 树形目录应该突出“结构节点”;
  • item 不应该把导航层级搅乱。

这点特别值得做实施时记住。

很多团队会误把所有知识内容都做成同一层级的页面,最后网站知识中心变成:

  • 层级太深,导航失控;
  • 或叶子太多,目录无法浏览。

Odoo 这里其实已经给出暗示:

公开知识中心的导航,应该优先服务结构,而不是穷举所有内容碎片。


六、前台搜索为什么只搜已发布知识文章

models/website.py 里,website._search_get_details() 被扩展后,当搜索类型是:

  • all
  • knowledge

会把 knowledge.article._search_get_detail() 挂进网站搜索。

_search_get_detail() 自己又明确加了 domain:

  • ('is_published', '=', True)
  • ('is_template', '=', False)

并把:

  • name
  • body
  • website_url

纳入搜索与返回映射。

这说明两件事

1. Knowledge 前台搜索不是独立孤岛

只要接上 website.searchable.mixin,知识文章就会和站内其他公开内容一起进入网站搜索体系。

2. 但它的公开范围被限制得很明确

没发布的文章、模板文章,都不进前台搜索。

这就避免了一种很常见的事故:

  • 正文页面没公开;
  • 结果却能被站内搜索、搜索建议或 sitemap 间接暴露。

Odoo 在这里的取舍很清晰:

只要内容还没完成“可公开”这一步,就不让它进入任何前台发现链路。

这比很多 CMS 靠页面模板层面做“隐藏”要稳得多,因为它直接把限制写进搜索数据源层了。


七、为什么 summary 和 SEO 元信息要重新从 body 计算

很多人会忽略 get_website_meta()_compute_summary(),但这部分恰恰最像“网站产品化”而不是“后台对象曝光”。

_compute_summary() 做了几件事:

  • 如果 body 为空,summary 为空;
  • 把 body 解析成 HTML 片段;
  • 删除所有 data-embedded 的元素;
  • 再把纯文本压平、去空白;
  • 截前 100 个字符,超长补 ...

get_website_meta() 再基于这个 summary 生成:

  • og:title
  • og:description
  • twitter:title
  • twitter:description
  • meta_description
  • 若存在 cover,还会补 og:imagetwitter:image

为什么要先删掉 data-embedded

测试 test_knowledge_meta_tags 已经说得很明白:

  • 目录块
  • 文件块
  • 嵌入视图
  • 视频等特殊嵌入块

这些内容不应该污染前台摘要。

因为搜索引擎和社交卡片需要的是:

  • 这篇文章在说什么;
  • 而不是文章里嵌了哪些 UI 组件。

如果不做这一步,经常会出现很糟的 SEO 摘要:

  • 一串目录标题;
  • 一段按钮文案;
  • 一些完全不适合被拿去当 description 的界面碎片。

所以官方这里不是简单把 body 截断,而是在做一次前台阅读语义重建

这也是很多二开最容易偷懒、却最影响外部体验的地方。


八、公开路由为什么仍然保留“未发布就去登录”的分流

/knowledge/article/<id> 这条路由对 public user 的处理非常典型:

  • 文章不存在:NotFound
  • 文章存在且 website_published=True:转公开视图
  • 文章存在但未发布:跳去 /web/login?redirect=...

这说明官方并没有把网站知识库简单设计成“两种状态:公开 / 404”。

它还保留了一条很重要的第三种可能:

  • 这篇文章不是公开内容,但它可能是登录后有权看的内容。

这对企业知识中心非常重要。

因为很多团队并不是把 Knowledge 当纯官网内容,而是同时承接:

  • 对外帮助中心
  • 客户 Portal 文档
  • 内部协作知识
  • 混合可见性知识树

所以当 public user 访问到未公开文章时,系统不是武断地说“页面不存在”,而是说:

  • “你作为公开访客不能看,但登录后也许可以。”

这条分流其实体现了 website_knowledge 的产品定位:

它不是独立 CMS,而是企业知识权限体系向网站前台延伸出来的一层公开视图。


九、对实施与二开的启发

1. 不要把 website_published 当普通单页开关

在 Knowledge 里,它更接近“公开子树开关”。

2. 公开知识中心最好按“根节点”规划,而不是逐篇零散发布

因为官方的导航、搜索和发布传播,都是围绕子树思维设计的。

3. 如果你要魔改前台目录,先确认不会泄露未公开节点标题

很多“增强导航”的需求,第一步就把安全边界弄丢了。

4. SEO 摘要别偷懒直接截 HTML

官方已经告诉你,嵌入块和正文摘要是两回事。

5. 内外混合知识库场景下,别强行把“未公开”做成 404

保留登录分流,往往更贴近真实业务:同一篇文档对匿名访客不可见,但对客户或成员可能可见。


一句话记忆法

Odoo 企业版 website_knowledge 公开的不是一篇篇孤立文章,而是一段可访问、可导航、可搜索、且不会泄露私有结构的知识子树。

参考源码

  • enterprise/website_knowledge/models/knowledge_article.py
  • enterprise/website_knowledge/models/website.py
  • enterprise/website_knowledge/controllers/main.py
  • enterprise/website_knowledge/tests/test_knowledge_public.py
  • enterprise/website_knowledge/tests/test_knowledge_public_business.py
  • enterprise/website_knowledge/tests/test_knowledge_security.py

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。