OBIEE里我们习惯于在数据格式TAB的列公式里嵌入HTML代码,勾选上"Treat text as HTML",这样我们就可以看到定制的HTML效果。但是在其他的很多地方却无法这么做,比如我们想在表标题,仪表盘和回复力的报表标题,提示的标签里输入HTML却无法得到想要的效果。也许我们可以用OBIEE的CSS定制样式,但是这样会将其应用到部件的整个部分,无法选择性的应用局部。本文阐述了如何通过添加脚本控制,可以在任何地方编写丰富的HTML,做到writing everywhere!
1,为什么不能够在想要加HTML的地方具有想要的效果呢?
比如我们在报表的标题上编写如下的标题:
product <font color="red">latest</font>
希望显示时能够解析该HTML代码段,可惜我们在高级TAB页面看到的是将>和<进行了转义。
<saw:view xsi:type="saw:titleView" name="titleView!1" rptViewVers="200510010" includeName="false" startedDisplay="none">
<saw:subTitle>
<saw:caption fmt="text">
<saw:text>product <font color="red">latest</font></saw:text></saw:caption></saw:subTitle></saw:view>
<saw:view xsi:type="saw:tableView" name="tableView!1" rptViewVers="200510010"/>
由于在显示的时候会进行相关的转义,所以我们编写的HTML代码不会产生效果。
在列公式里OBIEE同样对"<"和">"进行了转义,只不过在遇到“treat text as html”时presentation service进行了相反过程的转义。由此想到的解决方案就是在客户端展示之前将其再次反转回来,即将转义的小于号和大于号让浏览器能够识别出来。
2,所以我们需要定义什么样的代码段需要进行转义,这样才不会影响其他的显示效果。
可以将需要转义的HTML代码段加上前缀和后缀,这样我们就可以识别出需要转义的代码段,而不会对所有代码进行转义。
比如如下定的代码增加了前缀和后缀:
-=#B#=-Products (<span style="font-size:10pt;
font-style:italic;color:red;">Reclassified</span>)-=#E#=-
-=#B#=-Material<br>Category-=#E#=-
-=#B#=-Products (<a href="http://mycompany.com/oldcatalog.htm">
Old Catalog</a>)-=#E#=-
现在我们需要做的就是,在解析网页时,利用定制的分隔符,识别出相应的代码段,移除掉分隔符,将HTML实体转换回他们的标准显示。
众所周知,javascript和HTML DOM可以对代码段进行操作,通过如下的脚本获得body。
v_old_body = document.getElementsByTagName( "body" )[0].innerHTML
通过js脚本对body里的元素进行操作,更新body里的元素。
document.getElementsByTagName( "body" )[0].innerHTML = v_new_body;
3,以下的代码可以执行相应的转换:
// =========================================================================== //
// CUSTOM JAVASCRIPT - REINSERT AFTER UPGRADES
// =========================================================================== //
// =========================================================================== //
// HTML EVERYWHERE
// =========================================================================== //
//
// This function selectively converts the HTML entities "&lt;" and "&gt;"
// to the characters "<" and ">" respectively, allowing HTML to be entered
// into any OBIEE screen component (such as a column header or a prompt label).
//
// HTML to be converted should be preceded by "-=#B#=-" and suceeded by
// "-=#E#=-" when entered into an OBIEE screen component.
//
// The HTML page has the following structure:
//
// <page> ::= [ <component> <end delimiter> | * ] <postfix>
// <component> ::= <prefix> <begin delimiter> <text to decode>
//
// This function should be called from the "onload()" event handler.
//
// =========================================================================== //
function cs_html_everywhere(){
// DECLARATIONS
var c_begin_del = "-=#B#=-";
var c_end_del = "-=#E#=-";
var v_old_body = "";
var v_new_body = "";
var v_body_components = new Array();
// GET OLD BODY
v_old_body = document.getElementsByTagName( "body" )[0].innerHTML;
// SPLIT OLD BODY INTO COMPONENTS PLUS POSTFIX
v_body_components = v_old_body.split( c_end_del );
// EXAMINE OLD BODY COMPONENTS [IGNORING POSTFIX]
for( i = 0; i < v_body_components.length - 1; i++ ){
// GET START OF TEXT AFTER PREFIX
v_start = v_body_components[i].indexOf( c_begin_del );
// APPEND PREFIX PLUS DECODED TEXT TO NEW BODY
v_new_body +=
v_body_components[i].substring( 0, v_start ) +
v_body_components[i].
substring( v_start + c_begin_del.length ).
replace( /&lt;/g, "<" ).replace( /&gt;/g, ">" );
}
// APPEND POSTFIX
v_new_body += v_body_components[ v_body_components.length - 1 ];
// REPLACE BODY
document.getElementsByTagName( "body" )[0].innerHTML = v_new_body;
}
4,现在的问题是,如何让这段代码作用于每一个仪表盘页呢?
通过查看页面源代码,发现在每个页面的开头会引入几个js文件:
<script language="JavaScript" src="res/b_mozilla/browserdom.js"></script>
<script language="JavaScript" src="res/b_mozilla/common.js"></script>
<script language="JavaScript" src="res/b_mozilla/xmluiframework.js"></script>
<script language="JavaScript" src="res/b_mozilla/menu.js"></script>
<script language="JavaScript" src="res/b_mozilla/common/ajax.js"></script>
<script language="JavaScript" src="res/b_mozilla/proxy.js"></script>
<script language="JavaScript" src="res/b_mozilla/columndef.js"></script>
其中common.js文件的位置为:
<OracleBI>\oc4j_bi\j2ee\home\applications\analytics\analytics\res\b_mozilla
所以我们只要将代码放到被引用文件中就可以保证每个页面都能够访问到我们的js脚本。
5,但是又如何保证我们的js功能在页面加载完成后就立即运行呢,同时不影响其他的OBIEE功能?
还是通过查看页面源代码:
function NQOnWindowLoadsaw_86_2() {
saw.dashboard.reload();}
</script>
<script language="javascript">
function NQOnLoadEvent()
{NQOnWindowLoadsaw_86_1();
NQOnWindowLoadsaw_86_2();
}; window.onload = NQOnLoadEvent;
</script>
</head>
发现“onload”时间处理器定义当页面加载完成后什么函数将被调用,在这里是NQOnloadEvent,在该函数内部还调用了另外两个函数,在NQOnWindowLoadsaw_86_2()还调用了saw.dashboard.reload();该函数在页面load或reload时会被调用。所以将我们的“cs_html_everywhere”放到该函数的结尾,就能确保OBIEE做好相应工作后,我们的代码也能被执行。
函数saw.dashboard.reload()可以portalscript.js文件中找到,该文件在如下的位置可以找到:
<OracleBI>\oc4j_bi\j2ee\home\applications\analytics\analytics\res\b_mozilla\dashboards
该函数的代码如下:
saw.dashboard.reload = function()
{
var sSearchingReports = saw.dashboard.getSearchingIds();
if (!sSearchingReports)
return;
var tEmbedFrameDiv = document.getElementById(sEmbedFrameDivID);
if (tEmbedFrameDiv)
{
var tForm = saw.createFormFromString(tEmbedFrameDiv.getAttribute("src"));
tForm.ViewState.value = document.getElementById("idViewStateDiv").getAttribute("stateID");
saw.addHiddenInput(tForm, "reloadTargets", sSearchingReports);
var req = new sawr.serverRequest(saw.dashboard.impl.getReloadCall(), saw.dashboard.transferToParent);
req.setCallerObj("reload");
req.setErrorHandler(saw.dashboard.showError);
var conn = new sawr.iFrameConnection(req);
if (conn.setVisible)
{
if (tEmbedFrameDiv.getAttribute("display") == "visible")
conn.setVisible(true);
conn.setID(sEmbedFrameID);
}
conn.postForm(tForm);
}
}
在讲自己的代码放到系统的文件中时,最好做出相应的标记,便于维护。
注意到该函数有两个出口,所以需要将我们的函数放到return前和函数的结尾两个位置。
修改后的代码如下:
saw.dashboard.reload = function()
{
var sSearchingReports = saw.dashboard.getSearchingIds();
if (!sSearchingReports){
//=========================
// CUSTOM SCRIPT
//=========================
cs_html_ererywhere();
//=========================
return;
}
var tEmbedFrameDiv = document.getElementById(sEmbedFrameDivID);
if (tEmbedFrameDiv)
{
var tForm = saw.createFormFromString(tEmbedFrameDiv.getAttribute("src"));
tForm.ViewState.value = document.getElementById("idViewStateDiv").getAttribute("stateID");
saw.addHiddenInput(tForm, "reloadTargets", sSearchingReports);
var req = new sawr.serverRequest(saw.dashboard.impl.getReloadCall(), saw.dashboard.transferToParent);
req.setCallerObj("reload");
req.setErrorHandler(saw.dashboard.showError);
var conn = new sawr.iFrameConnection(req);
if (conn.setVisible)
{
if (tEmbedFrameDiv.getAttribute("display") == "visible")
conn.setVisible(true);
conn.setID(sEmbedFrameID);
}
conn.postForm(tForm);
}
//=========================
// CUSTOM SCRIPT
//=========================
cs_html_ererywhere();
//=========================
}
通过以上的方法,我们至少可以满足自己的需求。但是不排除在OBIEE升级后导致函数调用改变。也有可能不会让每一个js文件都出现在页面里,这样就会导致js脚本不能全局访问到。还有可能是升级后函数定的名称可能改变。
本来这个功能应该在开发时就应该考虑到,至少可以在哪里配置,不过很遗憾,就是没找到。