关于 b2c-business-manager-extensions
面向 Salesforce B2C Commerce 开发者的技能指南,介绍如何用 Business Manager 扩展自定义管理界面。涵盖菜单、对话框操作和表单扩展,并强调 XML 模式与权限配置细节。
主要功能
- 01创建 Business Manager 扩展 Cartridge
- 02配置菜单项和菜单操作
- 03为现有页面添加对话框操作
- 04扩展搜索表单字段
- 05参考模式与控制器实现规范
适用场景
- 01为 Business Manager 添加自定义导航和页面
- 02在订单或商品页面加入操作按钮
- 03为订单或客户搜索表单增加字段
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ salesforcecommercecloud/ b2c-developer-tooling --skill b2c-business-manager-extensions安装后在 Claude Code 中输入/b2c-business-manager-extensions确认已加载。
附带文件evals/trigger-evals.jsonreferences/EXTENSIONS-XML.md
详细介绍
Business Manager 扩展技能
本技能将指导你创建 Business Manager(BM)扩展 cartridge,以自定义管理界面。
概述
BM 扩展通过一个 bm_extensions.xml 描述文件和一个用于渲染 ISML 页面的控制器,为 Business Manager 添加自定义功能:
| 扩展类型 | 用途 |
|---|---|
| 菜单项 | 添加导航分区(管理菜单或站点菜单中的容器) |
| 菜单操作 | 在菜单项下添加功能链接/页面,并关联到控制器节点 |
| 对话框操作 | 在定义的扩展点处向现有 BM 页面添加按钮 |
| 表单扩展 | 向现有 BM 搜索表单(订单/客户搜索)添加字段 |
⚠️ 架构要点(请先阅读)
以下是最常见的错误。描述文件使用 bmmodules/2007-12-11 架构(bmext.xsd),而非 extensibility/* 命名空间。
- 命名空间是
http://www.demandware.com/xml/bmmodules/2007-12-11。 name、short_description和description是带有必需xml:lang属性的子元素,不是属性,也不是资源键。通过按语言重复元素来进行本地化。没有name="..."属性。<icon>是保存路径的普通字符串元素(<icon>icons/q.png</icon>),不是<icon path="..."/>。position按升序排列:数字越小,在菜单中越靠上。核心 BM 项目的位置范围为 2000–12000,因此自定义项目通常使用较大的数字(例如99999)以显示在底部,或使用-1将“主页”操作置顶。- 对话框操作的扩展点属性是
xp_ref(下划线),对话框操作通过子元素<menuactions><menuaction-ref action-id="..."/></menuactions>关联菜单操作——不存在menuaction-ref属性。 - 使用文件编码
ISO-8859-1,以与平台示例一致。
若要根据权威架构确认任何细节:
b2c docs schema bmext
文件结构
/bm_my_extension
/cartridge
bm_extensions.xml # Extension definitions (required)
bm_my_extension.properties # Cartridge properties (required)
/controllers
MyExtension.js # Controller for menu actions
/templates
/default
/extensions
mypage.isml # Custom BM pages
/static
/default
/icons
my-icon.png # Menu icons
bm_my_extension.properties 是标准的 cartridge 标记文件:
## cartridge.properties for cartridge bm_my_extension
demandware.cartridges.bm_my_extension.multipleLanguageStorefront=true
demandware.cartridges.bm_my_extension.id=bm_my_extension
基本 bm_extensions.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<extensions xmlns="http://www.demandware.com/xml/bmmodules/2007-12-11"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.demandware.com/xml/bmmodules/2007-12-11 bmext.xsd">
<menuitem id="acme_id01" position="99999" site="false">
<name xml:lang="x-default">ACME Tools</name>
<short_description xml:lang="x-default">ACME admin tools</short_description>
<description xml:lang="x-default">Tools and utilities from the ACME team</description>
<icon>icons/acme.png</icon>
</menuitem>
<menuaction id="acme-home" menupath="acme_id01" position="-1" site="false">
<name xml:lang="x-default">Home</name>
<short_description xml:lang="x-default">ACME Home</short_description>
<exec pipeline="ACME" node="Start"/>
<sub-pipelines>
<pipeline name="ACME-Start"/>
</sub-pipelines>
<icon>icons/acme.png</icon>
</menuaction>
</extensions>
pipeline="ACME" 会解析到控制器 ACME.js;node="Start" 会解析到它导出的 Start 函数(必须设置 Start.public = true)。
菜单项
顶层导航容器。site="false" 会将项目放入管理菜单;site="true"(默认值)会将项目放入站点菜单。
<menuitem id="acme_id01" position="99999" site="false">
<name xml:lang="x-default">ACME Tools</name>
<name xml:lang="de">ACME Werkzeuge</name>
<short_description xml:lang="x-default">ACME admin tools</short_description>
<icon>icons/acme.png</icon>
</menuitem>
| 属性 | 必需 | 默认值 | 描述 |
|---|---|---|---|
id | 是 | - | 唯一标识符。使用前缀以避免与核心 BM ID 冲突。 |
site | 否 | true | true = 站点菜单,false = 管理菜单 |
position | 否 | - | 排序位置——数值越小,位置越靠上。使用较大的值可将项目放在底部。 |
type | 否 | BM | BM 或 CSC(客户服务中心) |
子元素:name(必需,本地化)、short_description、description(本地化)、icon(字符串路径)、sldsIconName(SLDS 实用程序图标名称,在现代 BM UI 中优先使用)、exec。
菜单操作
菜单项下的功能页面。通过 <exec> 将每项连接到一个控制器节点,并在 <sub-pipelines> 中注册其使用的每个控制器节点。
<menuaction id="acme-export" menupath="acme_id01" position="20" site="false">
<name xml:lang="x-default">Product Export</name>
<short_description xml:lang="x-default">Export products to CSV</short_description>
<exec pipeline="ACME" node="Export"/>
<sub-pipelines>
<pipeline name="ACME-Export"/>
<pipeline name="ACME-Download"/>
</sub-pipelines>
<parameters>
<parameter name="defaultFormat" value="csv"/>
</parameters>
<icon>icons/export.png</icon>
</menuaction>
| 属性 | 必填 | 默认值 | 描述 |
|---|---|---|---|
id | 是 | - | 唯一标识符 |
menupath | 否 | - | 父菜单项的 id(或要附加到的核心 BM 菜单/项的 id) |
site | 否 | true | 必须与父菜单项一致 |
position | 否 | - | 排序顺序(数值越小,位置越靠前;-1 会置顶) |
extern | 否 | false | true 表示在新窗口中打开此操作 |
关键子元素:name(必填)、short_description、description、extended_description、exec、sub-pipelines、parameters、icon。
注意:
<exec>只接受pipeline和node(两者均为必填),没有https属性。架构中的apis、scapis和required-features元素不支持自定义菜单操作。
NoPermissionCheck 惯用模式
BM 会检查已登录用户是否有权访问注册了所请求控制器节点的菜单操作的 sub-pipelines。对于必须不经过模块权限检查即可使用的端点——例如保持连接的 ping、服务工作线程,以及由页面 JavaScript 调用的轮询/AJAX 端点——请将其节点注册到一个专用操作下,该操作使用保留 ID NoPermissionCheck,且不包含 <exec>:
<menuaction id="NoPermissionCheck" site="false">
<name xml:lang="x-default">Dummy</name>
<short_description xml:lang="x-default">Holds pipelines that do not require a permission check</short_description>
<sub-pipelines>
<pipeline name="ACME-Keepalive"/>
<pipeline name="ACME-ServiceWorker"/>
<pipeline name="ACME-Poll"/>
</sub-pipelines>
</menuaction>
否则,未获授模块权限的用户访问这些节点时会收到 403。这里只注册确实可以安全地向任何已通过身份验证的 BM 用户开放的节点。
对话框操作
在现有 BM 页面上的指定扩展点(xp_ref)添加按钮。
<dialogaction id="acme-order-sync" xp_ref="OrderPage-OrderDetails" position="100">
<name xml:lang="x-default">Sync Order</name>
<exec pipeline="ACME" node="OrderSync"/>
<parameters>
<parameter name="OrderNo"/>
</parameters>
<menuactions>
<menuaction-ref action-id="acme-home"/>
</menuactions>
</dialogaction>
| 属性 | 必填 | 默认值 | 描述 |
|---|---|---|---|
id | 是 | - | 唯一标识符 |
xp_ref | 是 | - | 扩展点 ID(注意其中的下划线) |
position | 否 | - | 排序顺序 |
class | 否 | - | 可选 CSS 类 |
extern | 否 | false | true 表示在新窗口中打开 |
子元素:name(可选,本地化)、exec(必填)、parameters、menuactions(一个或多个 <menuaction-ref action-id="..."/>,用于关联负责权限检查的菜单操作)。对话框操作不接受 icon 子元素。
常见扩展点:OrderPage-OrderDetails、ProductPage-General、ProductPage-Images、CustomerPage-Profile、ContentPage-General、CatalogPage-CategoryGeneral。
表单扩展
向现有 BM 搜索表单(例如订单或客户搜索表单)添加字段。
<formextension id="order-search">
<valueinput type="string" name="invoiceNumber">
<label xml:lang="x-default">Invoice Number</label>
<label xml:lang="de">Rechnungsnummer</label>
</valueinput>
<valueinput type="string" name="syncStatus" defaultvalue="">
<label xml:lang="x-default">Sync Status</label>
<option>All</option>
<option>Synced</option>
<option>Pending</option>
<option>Failed</option>
</valueinput>
<valueinput type="int" name="minQuantity">
<label xml:lang="x-default">Minimum Quantity</label>
</valueinput>
</formextension>
id 与目标表单匹配(例如 order-search、customer-search)。valueinput 必须包含 type(string、int 或 double)和 name;defaultvalue 为可选项。每个 valueinput 都包含本地化的 <label> 子元素;对于下拉列表,还可包含可选的 <option> 子元素。
控制器示例
控制器位于 controllers/ 中,并导出标记为 .public = true 的函数。使用 */cartridge/... 引入模块,以便共享逻辑,并确保页面节点受 BM 会话保护。
'use strict';
var ISML = require('dw/template/ISML');
var URLUtils = require('dw/web/URLUtils');
var Logger = require('dw/system/Logger');
var log = Logger.getLogger('bm', 'ACME');
/**
* Render the dashboard. Wired to <exec pipeline="ACME" node="Start"/>.
*/
function Start() {
// BM nodes should only be reachable in an authenticated BM session
if (!session.userAuthenticated) {
response.setStatus(403);
return;
}
ISML.renderTemplate('extensions/acme/dashboard', {
stats: getStats()
});
}
Start.public = true;
module.exports.Start = Start;
/**
* Process an action, then redirect back to the dashboard.
*/
function Process() {
var action = request.httpParameterMap.action.stringValue;
try {
performAction(action);
response.redirect(URLUtils.url('ACME-Start', 'message', 'Done'));
} catch (e) {
log.error('Action failed: {0}', e.message);
response.redirect(URLUtils.url('ACME-Start', 'error', e.message));
}
}
Process.public = true;
module.exports.Process = Process;
/**
* Lightweight endpoint called by page JS — registered under NoPermissionCheck.
*/
function Keepalive() {
response.setContentType('application/json');
response.writer.print('{"ok":true}');
}
Keepalive.public = true;
module.exports.Keepalive = Keepalive;
<exec> 或 <sub-pipelines> 引用的每个节点都必须已导出并设置 .public = true,否则 BM 会返回 403。
模板示例
BM 模板在 templates/default/extensions/ 下渲染。使用 URLUtils.url('Controller-Node', ...) 引用 node。
<isdecorate template="application/MenuFrame">
<isinclude template="inc/Modules"/>
<div class="acme-dashboard">
<h1>ACME Dashboard</h1>
<isif condition="${request.httpParameterMap.message.submitted}">
<div class="success">${request.httpParameterMap.message.stringValue}</div>
</isif>
<table class="data">
<thead><tr><th>Metric</th><th>Value</th></tr></thead>
<tbody>
<isloop items="${pdict.stats}" var="stat">
<tr><td>${stat.label}</td><td>${stat.value}</td></tr>
</isloop>
</tbody>
</table>
<a href="${URLUtils.url('ACME-Process', 'action', 'refresh')}" class="button">
Refresh
</a>
</div>
</isdecorate>
使用 application/MenuFrame 装饰(并包含 inc/Modules),即可为页面提供标准的 BM 界面框架。如果需要完全自定义页面,也可以使用纯 HTML 文档(现代 qlabs 风格的方案会将单页应用挂载到容器模板中)。
附加到现有 BM 菜单
无需新建菜单项,只需将菜单操作的 menupath 指向核心 BM 菜单 ID。所有核心 ID 的完整列表见参考文档;最常用的有:
| 菜单 | id | site |
|---|---|---|
| 站点首选项 | site-prefs | true |
| 商家工具 › 产品和目录 | prod-cat | true |
| 商家工具 › 订单 | orders | true |
| 管理 › 操作 | operations | false |
| 管理 › 站点开发 | studio | false |
| 管理 › 全局首选项 | global-prefs | false |
<menuaction id="acme-pref" menupath="site-prefs" position="2000" site="true">
<name xml:lang="x-default">ACME Preferences</name>
<exec pipeline="ACME" node="Preferences"/>
<sub-pipelines>
<pipeline name="ACME-Preferences"/>
</sub-pipelines>
</menuaction>
为自己的
menuitem/menuaction复用核心 BMid会隐藏你的扩展。请始终使用唯一且带前缀的 ID,并且仅通过menupath引用核心 ID。
启用扩展
- 将 cartridge 添加到Business Manager 站点的 cartridge 路径中:
- 管理 › 站点 › 管理站点 › Business Manager › 设置
- 将 cartridge ID(例如
bm_my_extension)添加到 cartridge 路径。
- 为角色授予模块权限:
- 管理 › 组织 › 角色 › (角色) › Business Manager 模块
- 启用自定义模块(扩展加入路径后,该模块便会显示)。
- 在
NoPermissionCheck下注册的节点无需此权限即可访问。
最佳实践
- 不确定时,使用
b2c docs schema bmext根据 XSD 验证。 - 为所有 ID 添加前缀,避免与核心 BM ID 冲突(并意外隐藏在核心 ID 后面)。
- 在
sub-pipelines中注册页面使用的每个节点,否则会返回 403。 - 仅对适用于任何已认证 BM 用户的安全端点使用
NoPermissionCheck(例如 keepalive、服务工作线程、轮询)。 - 将 BM cartridge 与店面 cartridge 分开。
- 使用
session.userAuthenticated对页面节点进行限制。 - 使用非管理员角色进行测试,确认权限边界符合预期。
详细参考
- 扩展 XML 参考 — 完整架构、所有元素和属性、
NoPermissionCheck模式,以及完整的核心 BM 菜单 ID 表。