关于 ui-actions-policies
用于管理 ServiceNow UI Actions 和 UI Policies 的技能,介绍表单与列表操作、动态字段行为及脚本配置,并建议先分析现有表单。
主要功能
- 01配置表单与列表 UI Actions
- 02通过 UI Policies 控制字段
- 03按条件动态显示字段
- 04使用 ES5 脚本创建元素
- 05分析表单现有配置
适用场景
- 01添加事件处理按钮或列表操作
- 02按记录条件调整字段行为
- 03排查表单策略与脚本冲突
MCP 相关
文档介绍了使用 snow_create_ui_action 和 snow_create_ui_policy 创建元素,并通过 snow_artifact_manage 查找或更新现有 UI 元素;修改表单前可调用 snow_analyze_form 分析配置。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ serac-labs/ serac --skill ui-actions-policies安装后在 Claude Code 中输入/ui-actions-policies确认已加载。
详细介绍
ServiceNow 的 UI 操作与 UI 策略
UI 操作可添加按钮、链接和上下文菜单。UI 策略可动态控制表单字段行为。
修改现有表单前,先使用 snow_analyze_form——它会通过一次调用,返回目标表上所有启用的 UI 策略、客户端脚本、数据策略、记录级与字段级 ACL,以及 UI 操作。先用它回答“这个表单为什么会这样运行?”,再进行推测。
UI 操作
UI 操作类型
| 类型 | 位置 | 示例 |
|---|---|---|
| 表单按钮 | 表单页眉 | “解决事件” |
| 表单上下文菜单 | 右键菜单 | “复制记录” |
| 表单链接 | 相关链接 | “查看 CI” |
| 列表按钮 | 列表页眉 | “导出所选项” |
| 列表上下文菜单 | 右键单击行 | “分配给我” |
| 列表选项 | 操作下拉菜单 | “更新状态” |
| 列表链接 | 列表页眉链接 | “新建记录” |
表单按钮 UI 操作(ES5)
// Table: sys_ui_action
// Name: Resolve Incident
// Table: incident
// Form button: true
// Active: true
// Condition: current.active == true && current.state != 6
// Script (Server-side - ES5 ONLY):
;(function executeAction() {
// Validate before resolving
if (!current.resolution_code) {
gs.addErrorMessage("Please select a resolution code")
action.setRedirectURL(current)
return
}
if (!current.close_notes) {
gs.addErrorMessage("Please provide resolution notes")
action.setRedirectURL(current)
return
}
// Set resolved state
current.state = 6 // Resolved
current.resolved_at = new GlideDateTime()
current.resolved_by = gs.getUserID()
current.update()
gs.addInfoMessage("Incident " + current.number + " has been resolved")
action.setRedirectURL(current)
})()
客户端 UI 操作(ES5)
// Table: sys_ui_action
// Name: Quick Assign
// Client: true
// Onclick: quickAssign()
// Client script (ES5 ONLY):
function quickAssign() {
// Get current user
var userId = g_user.userID
var userName = g_user.getFullName()
// Confirm action
var confirmed = confirm("Assign this incident to yourself (" + userName + ")?")
if (!confirmed) {
return false
}
// Set the field value
g_form.setValue("assigned_to", userId)
g_form.setValue("assignment_group", g_user.getGroupID())
// Save the form
gsftSubmit(null, g_form.getFormElement(), "save")
return false
}
列表 UI 操作(ES5)
// Table: sys_ui_action
// Name: Close Selected Incidents
// Table: incident
// List button: true
// List choice: true
// Condition: gs.hasRole('itil')
// Script (Server-side - ES5 ONLY):
;(function executeAction() {
// Get selected records
var selectedRecords = RP.getParameterValue("sysparm_checked_items")
if (!selectedRecords) {
gs.addErrorMessage("No records selected")
return
}
var sysIds = selectedRecords.split(",")
var closedCount = 0
for (var i = 0; i < sysIds.length; i++) {
var gr = new GlideRecord("incident")
if (gr.get(sysIds[i])) {
if (gr.state != 7) {
// Not already closed
gr.state = 7 // Closed
gr.closed_at = new GlideDateTime()
gr.closed_by = gs.getUserID()
gr.update()
closedCount++
}
}
}
gs.addInfoMessage("Closed " + closedCount + " incident(s)")
})()
使用 GlideAjax 的 UI 操作(ES5)
// Client-side UI Action calling server
// Client: true
// Onclick: checkAndEscalate()
function checkAndEscalate() {
var incidentId = g_form.getUniqueValue()
// Check if escalation is allowed
var ga = new GlideAjax("IncidentAjax")
ga.addParam("sysparm_name", "canEscalate")
ga.addParam("sysparm_incident_id", incidentId)
ga.getXMLAnswer(function (answer) {
var result = JSON.parse(answer)
if (result.canEscalate) {
// Proceed with escalation
g_form.setValue("priority", 1)
g_form.setValue("escalation", 1)
gsftSubmit(null, g_form.getFormElement(), "escalate_incident")
} else {
alert("Cannot escalate: " + result.reason)
}
})
return false
}
UI 策略
UI 策略结构
| 字段 | 用途 |
|---|---|
| 简短描述 | 策略名称 |
| 表 | 目标表 |
| 条件 | 何时应用 |
| 条件为假时反转 | 条件为假时撤销 |
| 加载时 | 表单加载时运行 |
| UI 策略操作 | 字段行为 |
基本 UI 策略(无脚本)
# UI Policy: Make Resolution Required on Resolve
Table: incident
Short description: Require resolution fields when resolving
Conditions: state = 6 (Resolved)
On load: true
Reverse if false: true
# UI Policy Actions:
- Field: resolution_code
Mandatory: true
Visible: true
- Field: close_notes
Mandatory: true
Visible: true
- Field: resolved_by
Read only: true
带脚本的 UI 策略(ES5)
// UI Policy Script - Execute if true
// Runs when condition becomes true (ES5 ONLY!)
function onCondition() {
// Show/hide fields based on category
var category = g_form.getValue("category")
if (category === "hardware") {
g_form.setDisplay("cmdb_ci", true)
g_form.setMandatory("cmdb_ci", true)
g_form.setDisplay("software", false)
} else if (category === "software") {
g_form.setDisplay("software", true)
g_form.setMandatory("software", true)
g_form.setDisplay("cmdb_ci", false)
}
}
复杂 UI 策略脚本(ES5)
// UI Policy: VIP Caller Handling
// Condition: None (script handles logic)
// On load: true
// Run scripts: true
// Script - Execute if true (ES5 ONLY!):
function onCondition() {
var callerId = g_form.getValue("caller_id")
if (!callerId) {
return
}
// Check if VIP via GlideAjax
var ga = new GlideAjax("UserAjax")
ga.addParam("sysparm_name", "isVIP")
ga.addParam("sysparm_user_id", callerId)
ga.getXMLAnswer(function (answer) {
var isVIP = answer === "true"
if (isVIP) {
// Highlight form
g_form.flash("caller_id", "#FFD700", 0)
g_form.showFieldMsg("caller_id", "VIP Customer", "info")
// Set default priority
if (!g_form.getValue("priority")) {
g_form.setValue("priority", 2)
}
// Make assignment group mandatory
g_form.setMandatory("assignment_group", true)
}
})
}
动态字段可见性(ES5)
// UI Policy: Show fields based on incident type
// Table: incident
// On load: true
function onCondition() {
var incidentType = g_form.getValue("u_incident_type")
// Reset all conditional fields
var conditionalFields = ["u_network_details", "u_hardware_model", "u_software_name"]
for (var i = 0; i < conditionalFields.length; i++) {
g_form.setDisplay(conditionalFields[i], false)
g_form.setMandatory(conditionalFields[i], false)
}
// Show relevant fields
switch (incidentType) {
case "network":
g_form.setDisplay("u_network_details", true)
g_form.setMandatory("u_network_details", true)
break
case "hardware":
g_form.setDisplay("u_hardware_model", true)
g_form.setMandatory("u_hardware_model", true)
break
case "software":
g_form.setDisplay("u_software_name", true)
g_form.setMandatory("u_software_name", true)
break
}
}
通过脚本创建(ES5)
以编程方式创建 UI 操作
// Create UI Action via background script (ES5 ONLY!)
var uiAction = new GlideRecord("sys_ui_action")
uiAction.initialize()
uiAction.setValue("name", "Escalate to Manager")
uiAction.setValue("table", "incident")
uiAction.setValue("active", true)
uiAction.setValue("form_button", true)
uiAction.setValue("form_style", "btn-warning")
uiAction.setValue("hint", "Escalate this incident to the caller's manager")
uiAction.setValue("condition", "current.active == true && current.priority > 2")
uiAction.setValue(
"script",
"(function executeAction() {\n" +
" current.priority = 2;\n" +
" current.escalation = 1;\n" +
' current.work_notes = "Escalated by " + gs.getUserDisplayName();\n' +
" current.update();\n" +
' gs.addInfoMessage("Incident escalated");\n' +
" action.setRedirectURL(current);\n" +
"})();",
)
uiAction.insert()
以编程方式创建 UI 策略
// Create UI Policy (ES5 ONLY!)
var policy = new GlideRecord("sys_ui_policy")
policy.initialize()
policy.setValue("short_description", "Require Close Notes on Close")
policy.setValue("table", "incident")
policy.setValue("active", true)
policy.setValue("on_load", true)
policy.setValue("reverse_if_false", true)
policy.setValue("conditions", "state=7")
var policySysId = policy.insert()
// Add UI Policy Action
var action = new GlideRecord("sys_ui_policy_action")
action.initialize()
action.setValue("ui_policy", policySysId)
action.setValue("field", "close_notes")
action.setValue("mandatory", true)
action.setValue("visible", true)
action.setValue("disabled", false)
action.insert()
MCP 工具集成
可用工具
| 工具 | 用途 |
|---|---|
snow_create_ui_action | 创建 UI 操作 |
snow_create_ui_policy | 创建 UI 策略 |
snow_artifact_manage (action=find) | 查找现有 UI 元素 |
snow_artifact_manage (action=update) | 修改 UI 元素 |
工作流示例
// 1. Create UI Action
await snow_create_ui_action({
name: "Approve Change",
table: "change_request",
form_button: true,
condition: 'current.state == "assess"',
script: "/* approval script */",
})
// 2. Create UI Policy
await snow_create_ui_policy({
short_description: "Require justification for high priority",
table: "change_request",
conditions: "priority=1",
actions: [{ field: "justification", mandatory: true }],
})
最佳实践
- 使用描述性名称 - 在名称中清楚说明用途
- 优先设置条件 - 使用条件,而非脚本
- 脚本精简 - 保持脚本简短
- 条件为假时反转 - 清理字段状态
- 充分测试 - 测试多种场景
- 角色安全性 - 添加角色条件
- 仅使用 ES5 - 不使用现代 JavaScript 语法
- 区分表单与列表 - 选择合适的操作类型
评论
支持 Markdown