企业网站 / 知识门户

Odoo 企业版 Knowledge 门户为什么不是“发个邀请链接就能看文档”:invite 注册、member-only 可见性与 share user 跳转边界讲透

很多人以为 Odoo 企业版 Knowledge 对外协作只需要生成一条邀请链接;但标准实现真正处理的是成员邀请注册、share user 的前后台分流、member-only 可见性与 root article 继承。本文从 `article_invite()`、`_redirect_to_portal_view()`、`_compute_user_permission()` 与 `_compute_is_article_visible()` 把这条门户权限链讲透。

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

很多团队做企业知识门户时,第一反应都是:

给外部客户或合作方发一个链接,让他们点进去看文章就行。

但 Odoo 企业版 Knowledge 的设计并不是“链接即访问”。

它真正解决的是下面这串问题:

  1. 这个人是内部员工、portal/share user,还是还没注册的外部伙伴;
  2. 邀请链接点开后,是应该先登录、先注册,还是直接看文章;
  3. 文章是“everyone 可见”,还是“只有被邀请成员可见”;
  4. 子文章是不是继承根文章的可见性;
  5. 用户打开 /knowledge/home/knowledge/article/<id> 时,应该进后台表单,还是前端门户壳。

所以这套模块真正保护的不是“让人看到文档”,而是:

把外部协作访问限制在被显式授予的成员范围内,同时给内部用户和 share user 两套不同入口。

这也是为什么你明明看到文章勾了某种“对所有人可见”的效果,portal 用户却不一定能看。

主链路怎么走

1)邀请链接不是直接放行,而是先校验 member + hash

入口在 knowledge/controllers/main.pyarticle_invite()

这条路由先根据 member_idknowledge.article.member,再用 _get_invitation_hash() 校验 URL 里的 invitation_hash。只有 member 记录存在且 token 对得上,才会继续。

这意味着 Knowledge 的邀请不是“谁拿到 URL 谁都能进”,而是建立在具体成员记录上的一次性访问桥。

如果 token 不对,标准行为是直接 NotFound,不是给你展示一个“也许你没权限”的模糊提示。这种处理更像安全边界,而不是营销页入口。

2)未注册外部成员先被送去注册,而不是直接变成 portal 访客

article_invite() 里最值得注意的一段,是当 partner.user_ids 不存在时的处理。

也就是说,这个合作伙伴虽然已经被邀请为 article member,但还没有真正的用户账号。此时系统会:

  • 检查当前实例是否允许 signup invitation;
  • 必要时对 partner 调 signup_prepare()
  • 生成一个最终会跳回 /knowledge/article/<id> 的 signup URL;
  • 强制用户先完成注册,再回文章。

这里的意思非常明确:

Knowledge 的外部访问不是匿名阅读,而是“先把你变成一个明确可识别的用户,再给你成员权限”。

这和很多人理解的“发个公共知识库链接”完全不同。

3)已注册用户也不是“直接看”,而是先走登录,再按身份分流

如果 partner 已经有用户账号,article_invite() 会把请求重定向到:

/web/login?redirect=/knowledge/article/<id>

登录完成后,真正决定去哪里的是 redirect_to_article()access_knowledge_home()

  • 内部用户 _is_internal() 为真,走 _redirect_to_backend_view(),进入 /odoo/knowledge/<id> 或 action form;
  • 非内部用户,也就是 share/portal user,走 _redirect_to_portal_view(),渲染 knowledge_portal_view

这就是企业版非常明确的产品切分:

  • 内部员工看后台知识工作台;
  • 外部成员看前端门户壳。

不是一个页面套两种权限,而是两种入口从路由层就分叉了。

为什么“everyone 可见”不等于 portal 都能看

这是最容易误解的一点。

knowledge.article._compute_is_article_visible() 里,源码明确写了注释:

portal users are forced to be members of the articles and do not benefit from is_article_visible_by_everyone

实现也确实如此:

  • 对内部用户,如果文章 is_article_visible_by_everyone,就可以直接算 visible;
  • 对非内部用户,visible_articles 一开始就是空集;
  • 后续只能靠 knowledge.article.member 里 partner 对 article 或 root article 的成员权限,去把 is_article_visible 算成真。

换句话说:

“everyone 可见”主要服务于内部范围的可见性继承,不是给外部门户用户发无限制通行证。

这背后的产品逻辑很合理:Knowledge 的 share user 协作场景是“受控共享”,不是“公开文档站”。

root article 继承同样是权限边界的一部分

_compute_is_article_visible() 不只检查文章本身的 member,还会看 root_article_id 对应的 member。也就是说,如果你被加在一棵根文章上,下面的子树可以沿着这条根关系继承可见性。

这就让企业版可以支持一种很常见的对外交付方式:

  • 不必逐篇邀请;
  • 以根文章为单位,把一整棵知识树交给某个客户或合作方;
  • 但仍然不是整个系统里的所有文章都开放。

门户首页为什么看起来“全都有”,其实不是

knowledge/controllers/portal.py 里,_prepare_knowledge_article_domain() 默认返回空 domain,看起来像没做限制;但真正生效的限制并不靠这里硬编码,而是靠文章自身的 is_article_visible 搜索逻辑和成员关系。

这是一种常见但容易被误判的企业版写法:

  • 门户控制器保持轻;
  • 权限判断下沉到模型层;
  • 无论你从首页、文章页还是搜索过来,最终都吃同一套可见性规则。

这能避免“列表能看到、详情打不开”或“详情能直达、搜索却漏结果”的权限分裂问题。

实战里最常见的误区

误区 1:把 Knowledge 门户当成网站 CMS

如果你的需求是“任何拿到链接的人都能匿名看文档”,Knowledge 不是最合适的第一选择。它天生更偏向成员协作和受控共享,而不是 public website 文档站。

误区 2:只配 article member,不检查 partner 是否真的有用户

很多实施以为把联系人加成 member 就结束了。实际上,如果对方还没有 user_ids,访问链会先进入注册流程。没理解这一层,就会把“客户打不开文档”误判成权限 bug。

误区 3:以为子文章必须逐篇单独授权

如果权限是沿 root article 继承的,那么更好的做法往往是按文档树来设计协作边界,而不是每篇文章都单独维护成员,后者既脆弱又难审计。

给实施和二开的建议

  1. 先定义协作模型,再选模块。 想做匿名公开文档站,用 Website/Helpdesk Knowledge 这类公开链路;想做受控共享,用 Knowledge 门户。
  2. 邀请失败优先查三件事: member 记录、hash 是否匹配、partner 是否已完成注册。
  3. 按根文章建权限。 如果一组内容天然属于同一客户或项目,优先把权限挂在 root article,而不是散到叶子文章。
  4. 自定义门户搜索时,别绕开模型层域。 列表、全文搜索、直达详情都应该复用 is_article_visible 一致判定。

一句话总结

Odoo 企业版 Knowledge 门户不是“发个邀请链接,对方就能看文档”。

它的真实设计是:先校验 member 邀请,再把外部对象变成明确用户,随后按 internal/share user 分流到后台或门户,并且只让成员关系覆盖到的文章树可见。

理解了这点,你就不会再把门户权限问题误判成“链接失效”,而会回到真正的诊断顺序:邀请记录、注册状态、身份分流、文章树可见性。

DISCUSSION

评论区

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