服务器端框架

插件接收来自客户机对特定菜单的 HTTP 请求,并且使用 JSON 格式的菜单订阅源进行响应。此响应标记为内容类型 application/JSON

菜单订阅源提供程序的请求 URL 处于 /wps/contenthandler/wps/mycontenthandler URL 下,这视请求是否来源于当前已登录用户上下文而定。请求 URL 可能具有门户网站 URL 导航状态,但它需要一个特定的查询字符串来访问菜单订阅源提供程序. 用于访问缺省 JSON 订阅源提供程序的特定查询字符串为:
?uri=menu:<specific menu name>&navID=<navigation node OID or custom unique name>
[&windowID=<portlet window control ID on the page> ]
其中:
navID
此参数是序列化字符串格式 ObjectID 或门户网站导航节点(门户网站页面)的定制唯一名称(在请求菜单时,将显示此名称)。此参数为菜单订阅源提供程序提供了构建菜单时使用的上下文,这样我们就知道要使用的主题,因为主题显式或通过继承与页面关联。
windowID
此参数是可选的,并且是通过 navID 指定的页面上 Portlet 窗口控件的序列化字符串格式 ObjectID。此参数仅对于包含 portlet 级操作的菜单(即skin menus)才是必需的。此参数必须是序列化的 ObjectID。 定制唯一名称不适用于 Portlet 窗口标识。
WebSphere® Portal 为三个随时可用的菜单定义文件提供了 Portal 8.5 优化主题:
  • pageAction
  • skinAction
  • moreActions

这些文件存在于 WebDAV 中主题根下的 menuDefinitions 目录中。主题根在主题元数据中的元数据条目名称 com.ibm.portal.theme.template.ref 下设置。该目录中的其他文件为 JSON 语法,但不由 JSON 菜单框架使用

WebDAV 中主题根的截屏
V8.0.0.1 中的新起点: 可以指定不存在的菜单标识(请求中 ?uri=menu: 后的 specific menu name)。8.0.0.1 之前,此菜单标识将导致有关请求的菜单名称无效或不存在的错误消息。在 8.0.0.1 和更高版本中,此菜单标识会被视为好像具有此名称的菜单文件存在,且具有为空数组 ("[ ]") 的内容。然后,会生成菜单订阅源响应,此响应仅包含对动态菜单添加项或使用 ref-id (匹配请求时的菜单名称)标记的当前主题概要文件的 JSON 子添加项的处理。
要点: 不会以任何方式控制或保证动态添加到菜单的项顺序。尽管测试时服务器在相同系统上重新启动期间顺序一致,却发现跨不同操作系统时,顺序不一致。尤其是,在 IBM® JVM 和不是由 IBM 提供的 JVM 之间。如果有必要严格控制菜单项的顺序,那么在菜单定义文件中使用显式 "type":"ModuleRef" 条目以显式插入菜单项,而不依赖于动态添加机制。

JSON 菜单定义文件语法

有关 JSON 自身的更多信息,请参阅 JSON 主页。

菜单定义文件的 JSON 语法由一组 JSON 对象组成,此数组由外层方括号 [ ] 指示。 每个 JSON 对象都包括在花括号 ({ }) 中并以逗号分隔:
[
   // optional one-line comment 
   {
     JSON object 1 (first menu item)
   },
   /*
     optional multi-line comment
   */
   {
     JSON object 2 (second menu item)
   },
   ...
]

您可以通过使用特定于门户网站的扩展名对此文件中的 JSON 语法添加注释。

注: 最小有效 JSON 菜单定义文件包含左数组括号和右数组括号 ([])。空文件将导致非法语法异常。

数组中的最后一个对象在其花括号与数组右括号之间没有逗号。

在菜单定义文件中,数组中对象的顺序确定了各个项在客户机菜单中的外观顺序。

数组中的每个对象都是一个独立的自包含菜单项定义,或者是对可提供更多直接插入 JSON 菜单定义文件标记的主题优化模块的引用。添加的这个 JSON 以直接插入形式包括,就好像是在菜单文件中一样。

每个对象由多个逗号分隔的 JSON 成员组成,每个成员都具有以冒号分隔的名称和值。成员名总是括在引号内的字符串。值可以是字符串、布尔值、嵌套对象或数组,具体取决于成员名。某些成员名只能具有特定的值,下表对此进行了定义。

每个菜单项定义都必须具有 type 条目,该条目定义特定菜单条目项。
要点: 所有条目名称都区分大小写。

下列菜单项名称和值是 type 条目可接受的值。

"type" : "Header"
为菜单中的后续条目定义标签。在客户机 UI 中,通常以突出显示或缩进方式显示,尽管此菜单项由应用于菜单项的样式控制。菜单中的 header 通常不可单击。

可以通过 titles 值为其他语言添加更多标题对象条目。

可以添加一个 itemClass 成员以控制 header 的外观。

{
"type" : "Header",
"titles" : [{"lang":"en","value":"<English text>"}, {"lang":"de", "value" : "German text"},...]
}
"type" : "Separator"

定义菜单项之间的分隔符。分隔符可以显示为空白、线条或其他外观,具体取决于应用于菜单的样式。

通常,分隔符不需要任何其他成员,尽管可以添加 itemClass 用于控制外观。

{
"type" : "Separator",
}
"type" : "DynamicMenuitem"

可能可单击并具有操作,DynamicMenuitem 项目具有一个值为插件名称的 id 成员。菜单订阅源提供程序使用此标识来检索所指定操作的实例,然后查询该实例以获取为此 menuitem 构建菜单订阅源内容的充分信息。WebSphere Portal 提供了一些即时可用的插件以用于缺省菜单定义,并且这些插件可由定制编写的菜单和主题复用。

此插件提供指示其是否活动的指示符(包括当前用户的访问控制许可权),还提供其自己的本地化标题和描述(可选)以及 actionHttpMethod 值,以供菜单订阅源提供程序为此项构建菜单时使用。此操作插件的 OperationURI 将成为相应菜单条目中的 actionUrl。另外,还允许出现其他对象成员,包括 actionFn、actionHttpMethod、visibilityFn、itemClass 和元数据。可以添加 markupID 成员为产生的菜单项创建 html 标记的标识。

如果 isActive() 方法由菜单订阅源提供程序代码调用时返回 false,那么菜单项将显示在菜单订阅源中,但对订阅源添加了 "visibility" : false 布尔成员。此布尔成员向客户机端代码指出,不应对用户显示此操作,并且客户机端代码在最终呈示的菜单中不包括此项。

(可选)还可以指定 moduleArgs 成员。此成员是名称和值由“&”符分隔的查询参数格式字符串。 如果指定此项,那么这些参数将在订阅源提供程序访问插件时传递到此插件,以构建当前菜单条目。

{
"type" : "DynamicMenuitem",
"id" : "operations.framework.plugin.name"
}
"type" : "StaticMenuitem"

可能可单击,并具有操作。通常,此项用于插入一个具有客户机端实现(而不是服务器端实现)的菜单项。

允许菜单订阅源定义文件作者完全指定任意菜单项条目。由于不存在 StaticMenuitem 的相应操作,因此所有必要的信息都必须由菜单定义文件提供。id 参数是可选的,并且将被 StaticMenuitem 的菜单订阅源提供程序代码忽略,尽管未将其标记为语法错误(如果存在)。

可以根据需要添加其他可选成员。

{
"type" : "StaticMenuitem",
 "titles" : [{"lang" : "en", "value" : "My English menu item text"},
        {"lang" : "de", "value" : "Mein menu item text auf Deutsch"},
         ...
        ],
"descriptions" : [{"lang" : "en", "value" : "My English menu item longer description flowing beautiful prose"},
             {"lang" : "de", "value" : "Mein menu item longer description flowing beautiful prose auf Deutsch"},
             ...
             ],
"actionUrl" : "http://www.yourco.com/wps/myportal/some_useful_url",
"actionHttpMethod" : "POST",
"actionFn" : "client_method_to_override_actionUrl",
"metadata" : {
        "navID" : "${navID}",
        "some_name" : "some_value",
        "some_other_name" : "${SubVar_From_Request_Query_Parms}"
        }
"markupId" : "my.item.markupId"
}
"type" : "ModuleRef"

菜单定义文件具有一个 id 成员,其值是插件的名称。此插件必须具有 menu 类型的添加项和 JSON 类型的子添加项。 菜单订阅源提供程序使用此 JSON 对象中 id 成员的值来检索此模块菜单添加项中对 JSON 子添加项的引用。这个 JSON 子添加项必须是有效的独立 JSON 菜单定义标记,包括两旁的数组左括号和右括号。菜单订阅源提供程序将除去数组左括号和右括号,并将这个添加的标记直接插入到菜单订阅源响应中,就像它包括在主定义文件中一样。

按名称指向提供了更多 JSON 格式和菜单定义文件语法标记的插件,订阅源提供程序将该标记直接插入菜单订阅源以替换 ModuleRef 条目。

(可选)可以添加 moduleArgs 成员。 如果指定此项,moduleArgs 成员的值将作为参数进行传递,以便从插件中检索 JSON 菜单定义文件标记。

{
"type" : "ModuleRef",
"id" : "Theme Optimization Framework module name"
}
"type" : "Submenu"

在当前菜单中定义一个占位符(在此位置附加新的子级菜单)。此项目可以支持多级菜单。对嵌套级别没有强行限制。鼠标悬停在显示的菜单中的子菜单条目上时,将通过向 JSON 菜单订阅源提供程序发送另一个独立的菜单请求对子菜单进行检索。

按照屏幕位置的指示将菜单追加到一侧。submenu 由客户机在另一个后续请求中访存。

Submenu 通过 idmoduleId 成员来命名其菜单内容源,且必须具有 titles 成员来提供占位符菜单项的文本,并且可具有 descriptions 成员(可选)。

这两个成员之间的唯一区别在于处理下一个请求的方式,在展开子菜单并且客户机代码在新的 HTTP 请求中检索扩展菜单订阅源时:
  • 对于 id,下一个请求被视为命名菜单定义文件,其中 id 值是文件名并且扩展名是 .json
  • 对于 moduleId,下一个请求被视为命名提供了必要 JSON 标记的插件。将访问此请求(就好像菜单定义文件已指定它)
    {
    "type" : "ModuleRef",
    "id" : "moduleId_value"
    }

还可以添加 moduleArgs 成员。如果与 moduleRef 成员一起出现在 SubMenu 条目中,那么 moduleArgs 成员的值将追加到作为菜单项的标识构建的 URL 中。该值用作客户机中的菜单引用,用于检索级联子菜单的菜单标记。

    {
    "type" : "SubMenu",
    "id" : "name_of_submenu_definition_JSON_file",
     "titles" : [{"lang" : "en", "value" : "My English sub-menu item text"},
          {"lang" : "de", "value" : "Mein sub-menu item text auf Deutsch"},
          ...
          ],
    "descriptions" : [{"lang" : "en", "value" : "My English sub-menu item longer description flowing beautiful prose"},
          {"lang" : "de", "value" : "Mein sub-menu item longer description flowing beautiful prose auf Deutsch"},
          ...
          ]
     }
    or
     {
    "type" : "SubMenu",
    "moduleId" : "name_of_theme_opt_framework_module_which_contributes_submenu_definition_JSON_",
    ...
    }  

JSON 菜单定义文件中的有效成员

成员名 值和示例 syntax 注释
类型 “Header”、“Separator”、“DynamicMenuitem”、“StaticMenuitem”、“ModuleRef”和“Submenu” 确定此菜单定义文件对象创建的菜单项类型
id 字符串 对于 DynamicMenuitemModuleRefid 成员是必需的。对于 Submenu,idmoduleId 是必需的。
DynamicMenuitem
标识是由菜单订阅源提供程序访问以获取 actionUrl(即操作的 OperationURI)、本地化标题和描述以及操作的 isActive 方法(此方法指示操作是否活动并是否可由当前用户访问)的插件的名称。如果用户在呈示的菜单中单击此菜单项,那么 actionUrl 将在另一请求中发送到服务器。
ModuleRef
标识是将对其进行访问以检索更多菜单文件定义标记的插件的名称。
Submenu
存在 id,它是客户机在 HTTP 菜单请求中请求创建各个项的子菜单列表的菜单名称。
titles 对象数组,每个对象具有三个成员:

定义标题条目语言的“lang”,以及包含该语言的标题字符串的“value”。

"titles" : [ {"lang":"en", "value":"Title in English"}, {"lang":"de", "value":"Title auf Deutsch"}, ... ]

Header、StaticMenuitem 和 Submenu 条目必需。 提供的语言列表仅涵盖门户网站用户可能需要的必需语言。也可用于 DynamicMenuitemDynamicMenuitem 从相应的插件中检索标题。实现本地化界面并提供相应语言的本地化字符串需要插件。您可以使用 JSON 中的相应元素替代标题和描述。然而,替代将应用于所有语言。您无法选择要替代的特定语言。Separator 没有与其关联的文本。ModuleRef 已被其他标记取代。
descriptions 与 titles 一样。"descriptions" : [ {"lang":"en", "value":"Title in English"}, {"lang":"de", "value":"Title auf Deutsch"}, ... ] 对所有类型而言均为可选。如果指定此项,那么缺省客户机代码将此成员解释为菜单项的悬浮式帮助文本。
itemClass 字符串 对所有类型而言均为可选。此成员是应用于菜单项的样式类名。如果指定此项,那么这应该是与菜单主题关联的样式表中出现的类名。
enabled 布尔值 true 或 false 对所有类型而言均为可选。缺省值为 true。此成员指示菜单项在客户机端是否可单击。true 表示活动,false 表示不活动。
enableFn 字符串 对所有类型而言均为可选。如果指定此项,那么此成员是要在客户机上执行以确定此菜单项是否活动的 JavaScript™ 函数的名称。此菜单项的完整菜单订阅源 JSON 对象作为参数传递到此函数。

此函数的调用结果将覆盖“enabled”设置。

actionUrl 字符串 对于 StaticMenuitem,必须存在 actionUrlactionFn。如果存在 actionUrl,它可以是绝对 URL 或相对 URL,或者是追加到当前请求 URL 或标记值的查询字符串。对于 DynamicMenuitem 而言并非必需。对于任何其他类型而言,没有作用。

如果同时存在 actionFnactionUrl,那么将优先调用前者。

actionHttpMethod 值为正常 HTTP 值“GET”、“POST”、“PUT”和“DELETE”和其他值的字符串。 缺省值为“GET”。对所有对象类型而言均为可选。如果在 DynamicMenuitem 上存在,那么这将覆盖任何由操作框架插件提供的操作。
actionFn 字符串 对于 StaticMenuitem,必须存在 actionUrlactionFn。如果存在 actionFn,那么此成员是单击菜单项时要在客户机上执行的 JavaScript 函数的名称。此菜单项的完整菜单订阅源 JSON 对象作为参数传递到此函数。如果同时存在 actionFnactionUrl,那么将优先调用前者。
visibilityFn 字符串 对于所有类型可选(但对于 Separator 或 ModuleRef 而言并无作用)。对于 DynamicMenuitemStaticMenuitem,如果指定了 visibilityFn,那么此成员是要在客户机上执行以确定此菜单项是否活动的 JavaScript 函数的名称。此菜单项的完整菜单订阅源 JSON 对象作为参数传递到此函数。
metadata
针对 V8.0.0.1:嵌入对象,其成员可以为字符串值、布尔值、数字值或其他嵌入对象。没有强行限制嵌套级别。元数据上仅具有两个限制:
  • 无法在元数据中使用数组
  • 元数据对象中的成员无法具有名称“metadata”。这将导致 IllegalSyntaxException。

在 V8.0.0.1 之前,元数据仅限制为未嵌套嵌入项的字符串类型。

对所有类型而言均为可选。如果存在,那么其中的成员必须遵守针对相应版本列出的限制。如果在任何嵌套级别的元数据对象中的任何字符串值中存在替换变量,且格式为 ${variableName},那么这些值将根据接收到的菜单请求中的查询参数 (...&variableName=value...) 进行替换。元数据值字符串中可以包含多个替换变量,但是在字符串中只执行一次传递。如果找到的任何替换变量在请求的查询参数中未找到任何替代项,那么会在元数据中保持不变,此元数据作为 JSON 订阅源响应的一部分传递到客户机。

例如:
[
  {
    ....
    "metadata" : {
           "a" : "something",
           "b" : true,
           "c" : 123,
           "thisIsNestedMetadata" : {
                 "a" : "something nested",
                 "d" : "This is a substitution:  ${subVar}"
           }
      }
      ...
  }
]
moduleId 字符串 对于 Submenu 可选,但仅适用于 Submenu。如果不是在 Submenu 条目中显示标识,那么此成员为模块的名称(如 ModuleRefid),客户机在用户展开 Submenu 项的情况下在单独直接请求中访问此模块。此成员构建的菜单项的格式为 {"type":"Submenu", "id" : "moduleRef:", ... }。客户机代码使用此标识向服务器发出请求(此请求是普通菜单请求 ?uri=menu:moduleRef:" 的变体)。订阅源提供程序代码通过启动指定的模块来处理此请求,就好像存在包含具有该标识的 ModuleRef 的菜单订阅源定义文件一样。
moduleArgs 字符串,格式为 HTTP 请求查询参数集,但没有前导 & 符(菜单订阅源提供程序将在前面添加 & 符)。如果存在多个查询参数,那么将在第一个参数后的任何两个参数之间插入 & 符。示例:
foo=bar&foo2=bar2&... 
对于 Submenu 可选,但仅适用于 Submenu。如果与 moduleId 一起存在,那么菜单订阅源提供程序为订阅源中的 Submenu 条目构建的 URI 如下所示:{"type" : "Submenu", "id" : "moduleRef:moduleId_value&moduleArgs_value", ... }。如果与 Submenu 中的 ID 一起存在,那么该 URI 将与上一个示例类似,但标识和参数没有 moduleRef: 前缀。
markupId 任意字符串,用于在 html 标记中创建定义菜单项的标识。 可选。仅用于 DynamicMenuItemStaticMenuItem 菜单条目。该项支持变量替换,因此可以使用 ${windowID} 来创建各个 Portlet(呈示时)的标识的唯一实例。

为模块化的主题编写菜单定义文件

您可以使用服务器端订阅源提供程序和客户机端 JavaScript 为新主题编写新的菜单定义文件。如果复制随时可用的样本主题并将其更改为新的定制主题,那么可以根据需要使用或更改相同的 JSON 菜单文件。如果从菜单项中引用了任何新的客户机端函数,那么必须创建并引用这些新的 JavaScript 功能。

JSON 菜单订阅源提供程序在 WebDAV 中主题根下的 menuDefinitions 目录中查找 JSON 菜单定义文件。

使用现有菜单定义文件作为样本,并使用 jsonlint 之类的工具来预先验证 JSON 语法。

JSON 语法在用于菜单定义文件时受限(除此之外允许进行注释)。

进行调试时,请在服务器上使用跟踪字符串 com.ibm.wps.jsonmenu.*=all

动态扩展和动态构造的菜单

从 WebSphere Portal 8.0.0.1 开始,可以将菜单项动态添加到菜单定义文件。可以使用此功能来扩展现有“静态”菜单定义文件,例如在 WebDAV 的 menuDefinitions 文件夹中样本主题中存在的文件。还可以使用此功能来动态构造不具有静态菜单文件的菜单。

如前文所述,在菜单的请求 URI 中,查询参数以 "?uri=menu:menu name" 开头。此菜单名称被菜单框架视为文件名,我们在资源的“基本位置”中搜索该文件,针对这些资源,主题元数据属性 com.ibm.portal.theme.template.ref 指定了主题。在菜单定义中,"type":"ModuleRef" 条目可在 JSON 子添加项引用主题优化模块,此模块包含 menu 添加项。此 JSON 子添加项具有指向实际 JSON 代码的 URL。从该子添加项 URL 检索到的 JSON 会替换文件的菜单订阅源中 "type":"ModuleRef" 条目。

从 WebSphere Portal 8.0.0.1 开始,此 JSON 子添加项还使用 ref-id 限定符进行了标记。该限定符的值为字符串。菜单定义文件处理结束时,菜单框架会再添加一个步骤。它会在当前页面的概要文件的主题模块中发现的所有 JSON 子添加项中搜索,在菜单请求上为此页面指定了 navID 参数。如果这些子添加项的任一 ref-id 标记与正处理的请求菜单名称匹配,那么会动态将这些子添加项的 JSON 代码添加到菜单订阅源的末尾,好像具有专门指向该模块的 "type":"ModuleRef" 条目一样。

此菜单框架通过创建新主题模块(或至少创建新子添加项),以及更新概要文件(而不必更新菜单定义文件)以允许使用新菜单项动态扩展菜单内容。

此外,还可使用此功能来动态构造菜单,而无需创建菜单定义文件。如果传入菜单请求包含的菜单名称不存在,那么现在菜单框架会视为好像针对空数组“[ ]”仅使用最小菜单语法指向菜单定义文件。之后,仅使用当前页面的概要文件中指定的模块的 JSON 子添加项来创建菜单,在当前页面中,ref-id 限定符匹配请求的菜单名称,即使它指定的文件不存在。
要点: 不会以任何方式控制或保证动态添加到菜单的项顺序。尽管测试时服务器在相同系统上重新启动期间顺序一致,却发现跨不同操作系统时,顺序不一致。尤其是,在 IBM JVM 和不是由 IBM 提供的 JVM 之间。如果有必要严格控制菜单项的顺序,那么在菜单定义文件中使用显式 "type":"ModuleRef" 条目以显式插入菜单项,而不依赖于动态添加机制。

有关构造包含 JSON 子添加项的主题优化模块的更多信息,请参阅“使用模块添加菜单项”。

控制 JSON 菜单订阅源的高速缓存生存期

WebSphere Portal V8.0.0.1 及更高版本中,JSON 菜单框架接受一个或多个 "jsonmenu.cache.time.<menu name>" 形式的 WP ConfigService 属性,其中值是以秒为单位的高速缓存时间。此值必须大于或等于 0。值为 0 表示不对菜单进行高速缓存。

这些 WP ConfigService 设置中的 <menu name> 值将与传入菜单请求中的 "?uri=menu:<menu name>" 查询参数的值进行匹配。WP ConfigService 中的菜单名称与接收到的菜单请求的比较区分大小写。