先说结论
很多人第一次看到 Odoo 的用户资料页,会以为这是一个“论坛社区功能的小页面”:
- 用户能公开头像和简介;
- 别人可以点进去看;
- 完成。
但 website_profile 真正做的事情,比公开个人主页复杂得多。
它同时处理:
- 用户是否主动公开自己的资料;
- 当前访问者有没有足够 karma 看别人资料;
- 头像读取是否允许走 sudo;
- 用户能编辑哪些字段、哪些字段不能碰;
- 邮箱验证成功后如何奖励 karma;
- 徽章与等级页面如何只展示网站可发布内容。
所以 website_profile 的核心不是“把用户挂到网站上”,而是:
在公开身份、社区激励和隐私边界之间,做一个有限开放的资料系统。
一、为什么“资料存在”不等于“任何人都能看”
_check_user_profile_access() 这段逻辑很能说明 Odoo 的态度。
它不是先问“有没有这个用户”,而是依次判断:
- 当前访问的是否就是本人;
- 资料是否
website_published; - 当前访问者的 karma 是否达到
website.karma_profile_min; - 用户记录是否真实存在。
这背后的意思很明确:
资料页不是天然公开页面
即便一个用户已经存在,也不代表网站访客就应该看到他的资料。
“是否公开”比“是否存在”更优先
源码里甚至先检查 website_published,再去处理后续访问逻辑。这说明公开开关是第一层边界。
社区不是完全开放的
想看别人资料,不只是对方要公开,你自己也得有足够 karma。
这是一种很典型的 Odoo 设计:
- 不是绝对匿名开放;
- 也不是完全封闭;
- 而是用社区积分构建逐步开放的可见性。
二、为什么自己永远能看自己的资料
同一段访问检查里,有个特别重要的优先条件:
- 如果访问者就是自己,直接允许访问。
这非常合理。
因为资料页同时还是“自助编辑入口”的一部分。
如果系统把“对外可见性”与“本人可管理性”混为一谈,就会出现一个荒唐结果:
- 你把资料设成私密后,自己也看不到和改不了。
Odoo 明确避免了这个问题。
所以这里的设计边界是:
- 公开访问权限 和 本人管理权限 是两套逻辑,不该互相覆盖。
三、为什么邮箱验证不是为了登录,而是为了社区可信度
res_users.py 里,_send_profile_validation_email() 和 _process_profile_validation_token() 组成了一条非常有意思的链路。
它们不是在做“注册激活”,而是在做:
- 资料邮箱验证;
- 验证成功后给用户增加
VALIDATION_KARMA_GAIN。
而生成 token 的方式也很讲究:
- 基于当天日期;
- 基于一个系统级
website_profile.uuid; - 再加
user_id与email; - 生成哈希。
这说明它追求的不是长期永久 token,而是:
- 一个和当前身份、当前邮箱、当前日期绑定的短期验证凭证。
为什么这么设计?
因为资料邮箱验证的商业意义,不是“让你能登录系统”,而是:
让社区知道,这个公开资料至少经过一次有效邮箱确认。
所以验证成功加 karma,本质上是在给可信度打分。
四、为什么资料编辑不是想改什么就改什么
save_edited_profile() 并不会把前端传来的字段全量写回。
它会先做预处理,再从 user._self_accessible_fields() 的白名单里筛可写字段。
常见可编辑内容包括:
namewebsiteemailcitycountry_idwebsite_descriptionwebsite_published
而且还有额外限制:
- 只有本人能改自己的隐私开关;
- 某些情况下国家不能随意改;
- 非白名单字段直接不会进入 write。
这说明资料页编辑不是“前台自由写用户表”,而是一种非常克制的 self-service。
这对网站安全很关键。
五、为什么头像读取单独走一套权限判断
/profile/avatar/<user_id> 这条路由并不简单返回图片。
它会先检查字段是否允许,再根据访问条件决定是否用 sudo 去读图像流。
这说明在 Odoo 看来,头像虽然是“看起来最公开”的资源,但也仍然属于受控资料的一部分。
这是一个很值得借鉴的细节:
- 不要因为资源是图片,就默认它不需要权限边界。
尤其是在用户资料、员工资料、客户门户这类场景里,头像往往也是身份数据的一部分。
六、为什么徽章和等级页只展示 website_published 内容
在 website_profile 里,徽章相关逻辑会显式加上:
website_published = True
这意味着:
- 后台存在的 badge,不代表前台都该公开;
- 社区激励内容也需要走网站发布边界。
这个设计和资料页本身是一致的:
- 不是有数据就展示;
- 而是先确认这份数据被允许进入网站公开空间。
这也是为什么 Odoo 的社区/网站功能虽然看着轻,但内部其实一直在坚持“发布”与“存在”分离。
七、karma_profile_min 为什么是一个很聪明的设置
网站对象上有个字段:
karma_profile_min
它控制“查看其他用户资料”所需的最低 karma。
这不是一个花哨参数,而是社区治理参数。
它解决的是一个很现实的问题:
- 完全开放资料页,容易变成低质量爬取目标;
- 完全关闭资料页,又会损失社区互信和身份沉淀。
而 karma 门槛提供了一个中间地带:
- 先参与社区;
- 再逐步获得更多可见性权限。
这比“开放/关闭”二选一更细腻,也更符合 Odoo 社区产品的气质。
八、做资料页定制时最容易踩的坑
1)把资料页当普通 CMS 页面改
这样很容易绕开原有访问检查与白名单写入。
2)只改前端显示,不看 website_published
最后常见结果就是:私密资料被错误暴露。
3)自己实现一套邮箱验证,却不接 karma 逻辑
表面上功能做出来了,但和 Odoo 社区激励体系脱节。
4)头像、徽章、排行这些“看似公共内容”没有复用原边界
往往最容易造成隐私泄露的,不是简介文本,而是这些被误判成“天然公开”的资源。
最后一句
Odoo 的 website_profile 真正有意思的地方,在于它不是一个毫无边界的公开主页系统,而是把:
资料发布开关、访问 karma 门槛、受限自助编辑、邮箱验证加分、徽章公开边界
织成了一套“可见,但不是谁都能看;可编辑,但不是想改什么都行”的公开身份机制。
这也解释了为什么它看起来轻,实际却很适合做有社区氛围、又不想完全裸奔的网站资料系统。
DISCUSSION
评论区