This is just another attempt to unpack Siebel Audit Trail CLOB value through PL/SQL. This time it is with a "classic" select statement.
It is definitely not the most performant solution but useful if you want to quickly look through a handful of S_AUDIT_ITEM records without a need to open Siebel views or install SQL packages.
Might give you an idea of how to leverage a recursive SQL for parsing purposes.
with user_select as (
-- free form S_AUDIT_ITEM select should include at least ROW_ID, AUDIT_LOG, BUSCOMP_NAME, TBL_NAME columns
select *
from siebel.S_AUDIT_ITEM
where AUDIT_LOG is not null and rownum <= 5
order by row_id desc
),
-- split audit_log value into tokens
tokens (ROW_ID, LVL, AUDIT_LOG, BUSCOMP_NAME, TBL_NAME, REMAIN, TOKEN_LEN, TOKEN, NEW_POS) as (
-- maybe it is worth fetching first token here?
select ROW_ID, 0 LVL, AUDIT_LOG, BUSCOMP_NAME, TBL_NAME,
AUDIT_LOG REMAIN,
0 TOKEN_LEN,
null TOKEN,
1 NEW_POS
from user_select
union all
select i.ROW_ID, p.LVL + 1 LVL, i.AUDIT_LOG, i.BUSCOMP_NAME, i.TBL_NAME,
SUBSTR(p.AUDIT_LOG, p.NEW_POS) REMAIN, -- remaining AUDIT_LOG value after cutting processed tokens (for debugging)
TO_NUMBER(REGEXP_SUBSTR(i.AUDIT_LOG, '(\d*)\*', p.NEW_POS, 1, '', 1)) TOKEN_LEN, -- length of the following token (for debugging)
-- token lenght comes before * and then a comes a token of that lenght
REGEXP_SUBSTR(p.AUDIT_LOG, '\*(.{' || REGEXP_SUBSTR(i.AUDIT_LOG, '(\d*)\*', p.NEW_POS, 1, '', 1) || '})', p.NEW_POS, 1, '', 1) TOKEN,
-- position at which starts next token length (current position + token lenght + lenght of token lenght + 1(* sign))
p.NEW_POS + TO_NUMBER(REGEXP_SUBSTR(i.AUDIT_LOG, '(\d*)\*', p.NEW_POS, 1, '', 1)) + length(REGEXP_SUBSTR(i.AUDIT_LOG, '(\d*)\*', p.NEW_POS, 1, '', 1)) + 1 NEW_POS
from user_select i
join tokens p on p.ROW_ID = i.ROW_ID and p.NEW_POS < length(i.AUDIT_LOG) -- recursive join until we reach the end of AUDIT_LOG value
),
-- group tokens into C,N,O,J,L,K arrays and number rows
blocks (ROW_ID, LVL, AUDIT_LOG, BUSCOMP_NAME, TBL_NAME, TOKEN, TYPE, ROWCNT, NUM) as (
select ROW_ID, LVL, AUDIT_LOG, BUSCOMP_NAME, TBL_NAME, TOKEN,
TO_CHAR(SUBSTR(TOKEN, 1, 1)) TYPE, -- first token indicates the meaning of following tokens (C, N, O, J, L, K)
TO_NUMBER(SUBSTR(TOKEN, 2)) ROWCNT, -- and a count of following tokens
0 NUM -- mark first(technical) token to filter it out later
from tokens
where lvl = 1
union all
select t.ROW_ID, t.LVL, t.AUDIT_LOG, t.BUSCOMP_NAME, t.TBL_NAME, t.TOKEN,
CASE WHEN p.ROWCNT = p.NUM -- fetch next technical token after reached the end of token array
THEN TO_CHAR(SUBSTR(t.TOKEN, 1, 1))
ELSE p.TYPE
END TYPE,
CASE WHEN p.ROWCNT = p.NUM
THEN TO_NUMBER(SUBSTR(t.TOKEN, 2))
ELSE p.ROWCNT
END ROWCNT,
CASE WHEN p.ROWCNT = p.NUM -- also reset a row number
THEN 0
ELSE p.NUM + 1
END NUM
from tokens t
join blocks p on t.row_id = p.row_id and t.lvl = p.lvl + 1
),
-- combine arrays into records and map columns into fields
recs as (
-- J(oin) entries are recorded with field names
select j.*, to_char(j.token) FIELD_NAME, o.token OLD_VAL, n.token NEW_VAL
from blocks j
join blocks n on n.row_id = j.row_id and n.num = j.num and n.type = 'L'
join blocks o on o.row_id = j.row_id and o.num = j.num and o.type = 'K'
where j.num > 0 and j.type = 'J'
union all
-- C(olumn) entries need to be mapped to field names through [Administration - Audit Trail]
select c.*, NVL(a.field_name, to_char(c.token)) FIELD_NAME, o.token OLD_VAL, n.token NEW_VAL
from blocks c
join blocks n on n.row_id = c.row_id and n.num = c.num and n.type = 'N'
join blocks o on o.row_id = c.row_id and o.num = c.num and o.type = 'O'
left join (
select b.buscomp_name, f.tbl_name, f.col_name, f.field_name,
ROW_NUMBER() OVER (PARTITION BY b.buscomp_name, f.tbl_name, f.col_name ORDER BY f.created desc) row_num
from siebel.S_AUDIT_BUSCOMP b
join siebel.S_AUDIT_FIELD f on f.audit_bc_id = b.row_id
) a on a.col_name = to_char(c.token) and a.buscomp_name = c.buscomp_name and a.tbl_name = c.tbl_name and row_num = 1 -- fetching only last created field entry
where c.num > 0 and c.type = 'C'
)
select ROW_ID, NUM, FIELD_NAME, NEW_VAL, OLD_VAL
from recs
-- here you can join original S_AUDIT_LOG records by ROW_ID if you need more info
order by ROW_ID desc, TYPE desc, NUM asc;
And it is also on GitHub.
Great news, a freshly baked version of the About View bookmarklet has arrived.
Been working on it slowly for quite a long time. Started this version from scratch to get rid of EJS and experiment more with plain ES6 JS. Hope it deserves a separate post.
New features in version 2.0:
As I mentioned it is relatively fresh, so stay tuned for further upgrades and I'll greatly appreciate all feedback.
Recent version:
(() => {
if ("undefined" == typeof SiebelApp) {
alert("It works only in Siebel OUI session!");
return;
}
const Id = "XapuksAboutView2";
// read options localStorage
let options = {};
if (localStorage.getItem(Id)) {
options = JSON.parse(localStorage.getItem(Id));
} else {
resetOptions();
}
let $d = $(`.ui-dialog.${Id}`);
// event handling
const handlers = {
"None": (e) => {
return true;
},
"Expand / Special": (e) => {
id = $(e.target).attr("data-ul");
if (id) {
$d.find(`ul.ul_show:not(#${id}):not(:has(#${id})):not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
$d.find("#" + id).toggleClass(['ul_show', 'ul_hide']);
e.stopPropagation();
return false;
} else {
e.stopPropagation();
return true;
}
},
"Expand active context": (e) => {
const a = SiebelApp.S_App.GetActiveView().GetActiveApplet();
if (a) {
$d.find(`ul#${a.GetFullId()}, ul#${a.GetFullId()}_controls, ul#${a.GetActiveControl()?.GetInputName()}`).removeClass("ul_hide").addClass("ul_show");
}
e?.stopPropagation();
return false;
},
"Copy active applet": (e) => {
const a = SiebelApp.S_App.GetActiveView().GetActiveApplet();
if (a) {
handlers["Copy value"]({
target: $d.find(`a[data-ul=${a.GetFullId()}]`)[0]
});
}
},
"Collapse item": (e) => {
$d.find(`ul.ul_show:not(:has(.ul_show:not(.keep_open))):not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
e.stopPropagation();
return false;
},
"Collapse all": (e) => {
$d.find(`ul.ul_show:not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
e.stopPropagation();
return false;
},
"Close dialog": (e) => {
$d.dialog("close");
e.stopPropagation();
return false;
},
"Options": (e) => {
rOptions();
e.stopPropagation();
return false;
},
"Copy value and close": (e) => {
handlers["Copy value"](e);
handlers["Close dialog"](e);
e.stopPropagation?.call(this);
return false;
},
"Copy value": (e) => {
const scope = e.target;
// replacing link with intput and select the value
const val = $(scope).text();
$(scope).hide().after("<input id='" + Id + "i'>");
$d.find("#" + Id + "i").val(val).select();
// attempt to copy value
if (document.execCommand("copy", false, null)) {
// if copied, display a message for a second
$d.find("#" + Id + "i").attr("disabled", "disabled").css("color", "red").val("Copied!");
setTimeout(() => {
$d.find("#" + Id + "i").remove();
$(scope).show();
}, 700);
} else {
// if failed to copy, leave input until blur, so it can be copied manually
$d.find("#" + Id + "i").blur(() => {
$(this).remove();
$d.find("a").show();
});
}
e.stopPropagation?.call(this);
return false;
},
"Invoke applet method": (e) => {
var $target = $(e.target);
var applet = SiebelApp.S_App.GetActiveView().GetAppletMap()[$target.attr("data-applet")];
const method = $target.text();
applet.InvokeMethod(method);
e.stopPropagation?.call(this);
return false;
},
"Invoke control method": (e) => {
var $target = $(e.target);
var applet = SiebelApp.S_App.GetActiveView().GetAppletMap()[$target.attr("data-applet")];
var control = applet.GetControls()[$target.attr("data-control")];
const method = $target.text();
SiebelApp.S_App.uiStatus.Busy();
try {
applet.GetPModel().OnControlEvent(SiebelApp.Constants.get("PHYEVENT_INVOKE_CONTROL"), control.GetMethodName(), control.GetMethodPropSet(), {
async: true,
cb: () => SiebelApp.S_App.uiStatus.Free()
});
} catch (e) {
console.error(e.toString());
SiebelApp.S_App.uiStatus.Free();
}
e.stopPropagation?.call(this);
return false;
},
"Focus": (e) => {
var $target = $(e.target);
const $el = $($target.attr("data-focus"));
$d.dialog('close');
$el.focus();
e.stopPropagation?.call(this);
return false;
},
"Expand related": (e) => {
var $target = $(e.target);
var sel = $target.attr("data-selector");
$d.find(`ul.ul_show:not(.keep_open)`).removeClass("ul_show").addClass("ul_hide");
$d.find(sel).toggleClass(['ul_show', 'ul_hide']);
e.stopPropagation?.call(this);
return false;
}
};
// handle double click
if ($d.length) {
var o = options["bmk_dbl"];
handlers[o]();
return;
}
// render the dialog
let guid = 0;
const css = [`<style>`, ...[
`ul {margin-left:20px}`,
`a.x_active {text-decoration:underline}`,
`a.x_hidden {font-style:italic}`,
`select {display:block; margin-bottom:15px}`,
`.options {background-color:lightgray; padding:15px; margin:10px}`,
`.ul_hide {display:none}`,
`.ul_show {border-bottom: 1px solid; border-top: 1px solid; margin: 5px; padding: 5px; border-color: lightgray;}`,
options["focus_feature"] == "true" ? `ul:has(.ul_show:not(.keep_open)) :is(label,a) {color:darkgray!important}` : ``,
options["focus_feature"] == "true" ? `ul.ul_show:not(:has(.ul_show:not(.keep_open))) :is(label,a) {color:black!important}` : ``,
`a[data-ul] {font-weight:bold}`,
`a[data-ul]:before {content:"> "; opacity:0.5; font-style:normal}`,
`a[data-handler]:before, a[data-focus]:before {content:"<["; opacity:0.5; font-style:normal}`,
`a[data-handler]:after, a[data-focus]:after {content:"]>"; opacity:0.5; font-style:normal}`,
`label {font-size:1rem; margin:0px; font-weight:bold;}`,
`table {display:block; overflow-x:auto; whitespace: nowrap}`,
`td {border:solid 1px}`,
`.options select {width:250px}`,
].map((i) => i ? `.${Id} ${i}` : ``), `</style>`].join("\n");
$d = $(`<div class="container" title="About View 2.0">${rApplication()}${css}</div>`).dialog({
dragStop: () => $d.dialog({ height: 'auto' }),
classes: { "ui-dialog": Id },
modal: true,
width: options["width"],
close: () => $d.dialog('destroy').remove(),
buttons: [
{
text: 'Help',
click: () => window.open('http://xapuk.com/index.php?topic=145', '_blank')
}, {
text: 'Feedback',
click: () => window.location.href = "mailto:[email protected]?subject=AboutView2"
}, {
text: 'Settings',
click: rOptions,
}, {
text: 'Reset Settings',
click: resetOptions
}, {
text: 'Close (esc)',
click: () => $d.dialog('close')
}
]
});
function dispatchEvent(e, cb) {
var $target = $(e.target);
if (cb.name?.indexOf("Special") > 0) {
if ($target.attr("data-handler") == "applet method") {
return handlers["Invoke applet method"](e);
} else if ($target.attr("data-handler") == "control method") {
return handlers["Invoke control method"](e);
} else if ($target.attr("data-selector")) {
return handlers["Expand related"](e);
}
}
if ($target.attr("data-focus")) {
return handlers["Focus"](e);
}
return cb(e);
}
$d.contextmenu(handlers[options["ws_right"]]);
$d.click(handlers[options["ws_click"]]);
$d.find("a").off("click").click((e) => dispatchEvent(e, handlers[options["link_click"]]));
$d.find("a").off("contextmenu").contextmenu((e) => dispatchEvent(e, handlers[options["link_right"]]));
$(".ui-widget-overlay").click(handlers[options["out_click"]]);
$(".ui-widget-overlay").contextmenu(handlers[options["out_right"]]);
function resetOptions(e) {
options = {
"bmk_dbl": "Expand active context",
"ws_click": "None",
"ws_right": "Collapse item",
"link_click": "Copy value",
"link_right": "Expand / Special",
"out_click": "Close dialog",
"out_right": "Close dialog",
"adv": "true",
"width": "1000",
"ctrl_list_by": "name",
"applet_list": "applet / bc",
"applet_list_by": "name",
"focus_feature": "false"
};
localStorage.setItem(Id, JSON.stringify(options));
}
// render functions
function rDropdown(caption, field, list) {
const id = field;
const value = options[field];
return [`<li>`,
`<label for="${id}">${caption}</label>`,
`<select id="${id}">`,
list.map((i) => `<option value="${i}" ${i == value ? 'selected' : ''}>${i}</option>`),
`</select>`,
`<li>`].join("");
}
function rOptions() {
if ($d.find(".options").length) {
$d.find(".options").remove();
} else {
let html = [
`<div class="options">`,
`<h4>SETTINGS</h4>`,
rDropdown(`Advanced properties`, `adv`, [`false`, `true`]),
rDropdown(`Dialog width`, `width`, [`600`, `800`, `1000`]),
rDropdown(`Show in main list`, `applet_list`, [`applet`, `applet / bc`, `applet / bc / rowid`]),
rDropdown(`List applets by`, `applet_list_by`, [`name`, `title`]),
rDropdown(`List controls by`, `ctrl_list_by`, [`name`, `caption`]),
rDropdown(`Link click`, `link_click`, [`Copy value`, `Copy value and close`, `Expand / Special`, `None`]),
rDropdown(`Link right click`, `link_right`, [`Copy value`, `Copy value and close`, `Expand / Special`, `None`]),
rDropdown(`Bookmarklet double click`, `bmk_dbl`, [`Expand active context`, `Copy active applet`]),
rDropdown(`Whitespace click`, `ws_click`, [`None`, `Close dialog`, `Options`, `Expand active context`, `Collapse item`, `Collapse all`]),
rDropdown(`Whitespace right click`, `ws_right`, [`None`, `Close dialog`, `Options`, `Expand active context`, `Collapse item`, `Collapse all`]),
rDropdown(`Outside click`, `out_click`, [`Close dialog`, `Expand active context`, `Collapse item`, `Collapse all`]),
rDropdown(`Outside right click`, `out_right`, [`Close dialog`, `Expand active context`, `Collapse item`, `Collapse all`]),
rDropdown(`Focus feature`, `focus_feature`, [`false`, `true`]),
`<\div>`
].join("\n");
$d.append(html);
$d.find("select").change((e) => {
const c = e?.target;
if (c) {
options[c.id] = c.value;
localStorage.setItem(Id, JSON.stringify(options));
}
});
}
}
function rPS(prop) {
return rItem(`<a href='#'>${prop[0]}</a>`, prop[1]);
}
function rHierarchy(caption, value, advanced) {
var a = [];
while ("object" === typeof value && value?.constructor?.name?.length > 1) {
a.push(value.constructor.name);
value = value.constructor.superclass;
}
return rItem(caption, a, advanced);
}
function rItem(caption, value, advanced, attribs = {}) {
if (value && (!Array.isArray(value) || value.length) || "boolean" === typeof value) {
if (!advanced || options["adv"] === `true`) {
guid++;
let id = Id + guid;
let sAttr = Object.entries(attribs).map(([p, v]) => `${p}="${v}"`).join(" ");
return (Array.isArray(value) ? [
`<li>`,
`<label for="${id}_0">`, caption, `:</label> `,
value.map((e, i) => `<a href="#" id="${id}_${i}" ${sAttr}>${e}</a>`).join(" > "),
`</li>`
] : [
`<li>`,
`<label for="${id}">`, caption, `:</label> `,
`<a href="#" id="${id}" ${sAttr}>`, escapeHtml(value), `</a>`,
`</li>`
]).join("");
}
}
return "";
}
function rControl(control) {
const id = control.GetInputName();
const applet = control.GetApplet();
const pr = SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())?.GetPR();
const bc = applet.GetBusComp();
const ps = control.GetMethodPropSet();
const up = control.GetPMPropSet();
const con = SiebelApp.Constants;
let sel = `#${applet.GetFullId()} [name=${control.GetInputName()}]`;
if (con.get("SWE_CTRL_RTCEMBEDDED") === control.GetUIType()) {
sel = `#${applet.GetFullId()} #cke_${control.GetInputName()}`;
}
const cls = control === applet.GetActiveControl() ? 'x_active' : $(sel).length == 0 || $(sel).is(":visible") ? '' : 'x_hidden'
return [`<li>`,
`<a href="#" data-ul="${id}_c" class="${cls}">`,
options["ctrl_list_by"] == 'caption' && control.GetDisplayName() ? control.GetDisplayName() : control.GetName(),
`</a>`,
`<ul id="${id}_c" class="ul_hide">`,
rItem(control.GetControlType() == con.get("SWE_PST_COL") ? "List column" : control.GetControlType() == con.get("SWE_PST_CNTRL") ? "Control" : control.GetControlType(), control.GetName()),
rItem("Display name", control.GetDisplayName()),
rItem("Field", control.GetFieldName(), false, { "data-handler": "field", "data-selector": `ul#${applet.GetFullId()}_bc,ul#${applet.GetFullId()}_bc_${applet.GetBusComp().GetFieldMap()[control.GetFieldName()]?.index}` }),
rItem("Value", bc?.GetFieldValue(control.GetFieldName())),
rItem("Message", control.GetControlMsg()),
...(control.GetMessageVariableMap() && Object.keys(control.GetMessageVariableMap()).length ?
[`<li><a href="#" data-ul="${id}_var">Message variables (${Object.keys(control.GetMessageVariableMap()).length}):</a></li>`,
`<ul id="${id}_var" class="ul_hide">`,
Object.entries(control.GetMessageVariableMap()).map(rPS).join("\n"),
`</ul>`] : []),
rItem("Type", control.GetUIType()),
rItem("LOV", control.GetLovType()),
rItem("MVG", control.IsMultiValue()),
rItem("Method", control.GetMethodName(), false, { "data-handler": "control method", "data-applet": applet.GetName(), "data-control": control.GetName() }),
...(options["adv"] === `true` && ps && ps.propArrayLen ?
[`<li><a href="#" data-ul="${id}_method">Method properties (${ps.propArrayLen}):</a></li>`,
`<ul id="${id}_method" class="ul_hide">`,
Object.entries(ps.propArray).map(rPS).join("\n"),
`</ul>`] : []),
//rItem("Id", id),
rItem("Id", options["adv"] === `true` && $(sel).is(":focusable") ? [id, `<a href="#" data-focus='${sel}'>Focus</a>`] : id),
rItem("Immidiate post changes", control.GetPostChanges()),
rItem("Display format", control.GetDisplayFormat()),
rItem("HTML attribute", control.GetHTMLAttr(), true),
rItem("Display mode", control.GetDispMode()),
rItem("Popup", control.GetPopupType() && [control.GetPopupType(), control.GetPopupWidth(), control.GetPopupHeight()].join(" / ")),
rHierarchy("Plugin wrapper", SiebelApp.S_App.PluginBuilder.GetPwByControl(pr, control), true),
rItem("Length", control.GetFieldName() ? control.GetMaxSize() : ""),
...(options["adv"] === `true` && up && up.propArrayLen ?
[`<li><a href="#" data-ul="${id}_up">User properties (${up.propArrayLen}):</a></li>`,
`<ul id="${id}_up" class="ul_hide">`,
Object.entries(up.propArray).map(rPS).join("\n"),
`</ul>`] : []),
rItem("Object", `SiebelApp.S_App.GetActiveView().GetAppletMap()["${applet.GetName()}"].GetControls()["${control.GetName()}"]`, true),
$(sel).length > 0 ? rItem("Node", `$("${sel}")`, true) : ``,
`</ul>`, `</li>`].join("");
}
function rApplet(applet) {
const cm = Object.values(applet.GetControls());
const mm = applet.GetCanInvokeArray();
const id = applet.GetFullId();
return [`<ul id="${id}" class="ul_hide">`,
rItem("Applet", applet.GetName()),
rItem("BusComp", applet.GetBusComp()?.GetName(), false, { "data-handler": "buscomp", "data-selector": `ul#${applet.GetFullId()}_bc` }),
rItem("Title", SiebelApp.S_App.LookupStringCache(applet.GetTitle())),
rItem("Mode", applet.GetMode()),
rItem("Record counter", applet.GetPModel().GetStateUIMap().GetRowCounter, true),
rHierarchy("PModel", applet.GetPModel(), true),
rHierarchy("PRender", SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())?.GetPR(), true),
rItem("Object", `SiebelApp.S_App.GetActiveView().GetAppletMap()["${applet.GetName()}"]`, true),
rItem("Node", `$("#${applet.GetFullId()}")`, true),
`<li><a href="#" data-ul="${id}_methods">Methods (${mm.length}):</a></li>`,
`<ul id="${id}_methods" class="ul_hide">`,
mm.map(m => [`<li>`, `<a href="#" data-handler="applet method" data-applet="${applet.GetName()}">`, m, `</a>`, `</li>`].join("")).join("\n"),
`</ul>`,
`<li><a href="#" data-ul="${id}_controls">Controls (${cm.length}):</a></li>`,
`<ul id="${id}_controls" class="ul_show keep_open">`, //<ul id="${id}_controls" class="ul_show">
...cm.map(rControl),
`</ul>`,
`</ul>`].join("\n");
}
function rField(field, id) {
const bc = field.GetBusComp();
const name = SiebelApp.S_App.LookupStringCache(field.GetName());
return [`<li>`,
`<a href="#" data-ul="${id}">`, name, `</a>`,
`<ul id="${id}" class="ul_hide">`,
rItem("Field", name),
rItem("Value", field.GetBusComp().GetFieldValue(name)),
rItem("Type", field.GetDataType()),
rItem("Length", field.GetLength()),
rItem("Search spec", field.GetSearchSpec()),
rItem("Calculated", !!field.IsCalc()),
rItem("Bounded picklist", !!field.IsBoundedPick()),
rItem("Read only", !!field.IsReadOnly()),
rItem("Immediate post changes", !!field.IsPostChanges()),
rItem("Object", `SiebelApp.S_App.GetBusObj().GetBusCompByName("${bc.GetName()}").GetFieldMap()["${name}"]`, true),
`</ul>`, `</li>`].join("\n");
}
function rBC(a, id) {
var bc = a.GetBusComp();
const fields = Object.values(bc.GetFieldMap());
return [`<ul id="${id}" class="ul_hide">`,
rItem("BusComp", bc.GetName()),
rItem("Commit pending", !!bc.commitPending, true),
rItem("Can update", !!bc.canUpdate),
rItem("Search spec", bc.GetSearchSpec()),
rItem("Sort spec", bc.GetSortSpec()),
rItem("Current row id", bc.GetIdValue()),
rItem("Object", `SiebelApp.S_App.GetBusObj().GetBusComp("${a.GetBCId()}")`, true),
`<li><label><a href="#" data-ul="${id}_rec">Records: ${Math.abs(bc.GetCurRowNum())} of ${bc.GetNumRows()}${bc.IsNumRowsKnown() ? '' : '+'}</a></label></li>`,
`<ul id="${id}_rec" class="ul_hide">`,
`<table>`,
`<tr>`,
...Object.keys(bc.GetFieldMap()).map((i) => `<th>${i}</th>`),
`</tr>`,
...bc.GetRecordSet().map((r, i) => [
`<tr>`,
...Object.values(r).map(v => [
`<td><a href="#" ${bc.GetSelection() == i ? ` class="x_active"` : ``}>`,
v,
`</a></td>`
].join("")),
`</tr>`
].join("")),
`</table>`,
`</ul>`,
`<li><label><a href="#" data-ul="${id}_fields">Fields(${bc.GetFieldList()?.length}):</a></label></li>`,
`<ul id="${id}_fields" class="ul_show keep_open">`,
...fields.map((field, i) => rField(field, id + "_" + field.index)),
`</ul>`,
`</ul>`].join("\n");
}
function rApplication() {
const app = SiebelApp.S_App;
const view = app.GetActiveView();
const bo = app?.GetBusObj();
const bm = bo?.GetBCArray();
const scrPM = SiebelApp.S_App.NavCtrlMngr()?.GetscreenNavigationPM();
let am = Object.values(view?.GetAppletMap());
var ws = SiebelApp.S_App.GetWSInfo().split("_");
var wsver = ws.pop();
var amCache = {};
Object.assign(amCache, view?.GetAppletMap());
// Identifying a primary BC
var paa = Object.values(SiebelApp.S_App.GetActiveView().GetAppletMap()).filter((a) => !a.GetParentApplet() && (!a.GetBusComp() || !a.GetBusComp().GetParentBusComp()));
if (!paa.length) {
alert("Failed to identify a primary BusComp!")
}
return [`<ul>`,
rItem("Application", app.GetName()),
rItem("Screen", scrPM?.Get("GetTabInfo")[scrPM?.Get("GetSelectedTabKey")]?.screenName),
rItem("View", view.GetName()),
rItem("Task", view.GetActiveTask()),
rHierarchy("PModel", SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())?.GetPM(), true),
rHierarchy("PRender", SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())?.GetPR(), true),
rItem("BusObject", bo?.GetName()),
rItem("Workspace", [ws.join("_"), wsver]),
`<label>Applets (${am.length}) / BusComps (${bm.length}):</label>`,
`<ul>`,
hierBC(paa[0].GetBusComp(), 0, amCache),
...Object.values(amCache).map((a) => rAppletName(a, 0, amCache)),
`</ul></ul>`].join("\n");
}
// prints applet name
function rAppletName(a, l, amCache) {
delete amCache[a.GetName()];
return [`<li>`,
`<ul>`.repeat(l),
`<a href="#" data-ul="${a.GetFullId()}" class="${a === SiebelApp.S_App.GetActiveView().GetActiveApplet() ? 'x_active' : $(`#${a.GetFullId()}`).is(":visible") ? '' : 'x_hidden'}">`,
options['applet_list_by'] == 'title' && SiebelApp.S_App.LookupStringCache(a.GetTitle()) ? SiebelApp.S_App.LookupStringCache(a.GetTitle()) : a.GetName(),
`</a>`,
a.GetBusComp() && options["applet_list"].indexOf("bc") > -1 ? ` / <a href="#" data-ul="${a.GetFullId()}_bc">${a.GetBusComp().GetName()}</a>` : ``,
a.GetBusComp() && a.GetBusComp().GetIdValue() && options["applet_list"].indexOf("rowid") > -1 ? ` / <a href="#">${a.GetBusComp().GetIdValue()}</a>` : ``,
rApplet(a),
a.GetBusComp() && rBC(a, a.GetFullId() + "_bc"),
`</ul>`.repeat(l),
`</li>`].join("");
}
// prints applets based on bc or parent applet (rec)
function hierApplet(bc, pa, l, amCache) {
return Object.values(amCache).filter((a) => bc && a.GetBusComp() === bc || pa && a.GetParentApplet() === pa).map((a) => !(a.GetName() in amCache) ? "" : [
rAppletName(a, l, amCache),
hierApplet(null, a, l + 1, amCache) // look for child applets
].join("\n"));
}
// prints applets based on BC hierarchy (rec)
function hierBC(bc, l, amCache) {
return [
hierApplet(bc, null, l, amCache)?.join("\n"),
...SiebelApp.S_App.GetActiveBusObj().GetBCArray().filter((e) => e.GetParentBusComp() === bc).map((b) => hierBC(b, l + 1, amCache))
].join("\n");
}
// utilities
function escapeHtml(html) {
return html.toString()
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
})()
javascript:(function()%7B%2F*%0A%20%20%20%20%2B1.%20%5BClick%5D%20option%20should%20be%20against%20Type%3A%20Button%2C%20not%20method%0A%20%20%20%20%2B2.%20%5BInvoke%5D%20option%20should%20be%20available%20against%20every%20control%20with%20InvokeMethod%0A%09%2B3.%20Reorginize%20the%20code%2C%20so%20it%20is%20granular%20and%20can%20be%20easily%20reconfigured%20%0A%094.%20Fetch%20Applet%20and%20view%20UPs%20through%20PM%3A%0A%09%09var%20appletComponent%20%3D%20SiebelAppFacade.ComponentMgr.FindComponent(%7B%0A%09%09%09id%3A%20%5BopenAppletPopupJSON.AppletName%5D%5B0%5D%0A%09%09%7D)%3B%0A%09%09var%20popupAppletPM%20%3D%20appletComponent.GetPM()%3B%0A%20%20%20%20%2B5.%20Display%20a%20hierarchy%20of%20PM%2FPR%20classes%20for%20View%2FApplet%2FControl%20%0A%20%20%20%20%09-%20use%20Siebel%20facade%20hierarchy%20code%20snippet%0A%20%20%20%20%09-%20on%20click%20of%20PM%2FPR%20run%20inspect%20to%20open%20a%20source%20file%20when%20in%20Chrome%20DevTools%0A%20%20%20%20%2B6.%20BusComp%20object%20selector%20(make%20object%20selector%20hidden%20and%20coppy%20on%20double%20click%3F)%0A%20%20%20%207.%20make%20focus%20button%20a%20link%20and%20put%20it%20outside%20the%20spoiler%0A%20%20%20%20%2B8.%20another%20spoiler%20at%20the%20bottom%20with%20Options%3A%0A%20%20%20%20%09-%20left%2Fright%20click%20%3D%20copy%20%2B%20close%20%2B%20copy%20and%20close%20%2B%20expand%0A%20%20%20%20%09-%20advanced%20checkbox%20-%20if%20N%20-%20hide%20all%20OUI%20related%20properties%0A%20%20%20%20%09-%20store%20options%20in%20localStorage%0A%20%20%20%20%09-%20checkbox%20-%20always%20expand%20active%20applet%0A%20%20%20%20%09-%20checkbox%20-%20always%20expand%20active%20control%20(available%20only%20if%20prev%20option%20%3D%20Y)%0A%20%20%20%20-9.%20applet%20%2F%20control%20DOM%20selectors%20to%20have%20Inspect%20option%20-%20on%20click%20run%20inspect()%20-%20not%20possible%20to%20call%20inspect%20from%20JS%20%0A%20%20%20%20%2B10.%20IP19%20compatible%0A%20%20%20%20-11.%20Renders%20hidden%20content%20when%20expanded%2C%20not%20in%20advance%20-%20more%20reliable%20and%20fast%0A%20%20%20%2012.%20for%20applets_div%20and%20controls%20container%20print%20out%20event%20listeners%20(event%2C%20selector%2C%20inspect(function))%20-%20code%20snippet%20%3D%20Node%20event%20listeners%0A%20%20%20%20%2B13.%20display%20applets%2FBCs%20in%20a%20hierarchycal%20order%20(use%20space%20or%20a%20glyph%20to%20indent)%0A%20%20%20%2014.%20Display%20a%20tree%20of%20all%20pm.Get()%20properties%20for%20view%20and%20applet%20-%20in%20our%20case%20it%20is%20where%20all%20portal%20config%20is%20stored%0A%20%20%20%2015.%20also%20display%20all%20SiebelApp.Constants%3F%20How%20to%20get%20a%20list%3F%0A%20%20%20%20%2B16.%20Get%20value%20through%20GetControlMsg()%20if%20GetFieldName()%20doesn't%20exist%20in%20the%20BC%20recordset%0A%20%20%20%2017.%20dblclick%20to%20run%20special%20methods%20(configurable)%20-%20focus%2C%20inspect%2C%20invoke%2C%20force%20show%2C%20etc%2C%20dblclick%20on%20whitespace%20gives%20you%20advanced%20look%0A%20%20%20%2018.%20print%20standalone%20applet%20in%20collapsed%20section%3F%20Through%20SiebelAppFacade.%0A%20%20%20%20-19.%20applet%2C%20application%20UPs%20and%20other%20staff%20comming%20from%20the%20server%20through%20SiebelApp.S_App.GetAppPropertySet()%20-%20nope%2C%20not%20applet%20UPs%0A%20%20%20%20%2B21.%20cater%20for%20empty%20applets%20with%20no%20records%2C%20export%20and%20import%20applets%0A%20%20%20%2022.%20expose%20RO%20attributte%20for%20all%20controls%20and%20have%20an%20option%20to%20update%20a%20field%20right%20away%0A%20%20%20%2023.%20commands%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20bookmarklet%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20double%20click%20%3D%20expand%20active%20applet%20and%20control%2Fcopy%20active%20applet%2Fcopy%20active%20control%20name%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20tripple%20click%20%3D%20reset%20all%20options%20to%20default%0A%20%20%20%20%20%20%20%20%20%20%20%20whitespace%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20click%20%3D%20show%20more%20options%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20right%20click%20%3D%20close%20window%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20double%20click%20%3D%20expand%20all%20or%20collapse%20all%20if%20expanded%0A%20%20%20%20%20%20%20%20%20%20%20%20links(%3Ca%3E)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20left%20click%20%3D%20special%20command%20(expand%2C%20run%20method%2C%20inspect%2C%20etc)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20right%20click%20%3D%20copy%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20-%20doule%20click%20%3D%20%0A%20%20%20%20%20%20%20%20%20%20%20%20other%20option%20example%20-%20right%20mb%20down%20while%20left%20mb%20is%20down%0A%20%20%20%20%2B24.%20need%20another%20way%20of%20fetching%20PR%20class%2C%20through%20FindComponent%3F%0A%20%20%20%20%20%20%20%20%20%20%20%20SiebelAppFacade.ComponentMgr.FindComponent(SiebelApp.S_App.GetActiveView().GetAppletMap()%5B'FaCS%20UX2%20NQSC%20Applicant%20Portal%20Application%20Left%20Pane%20Menu%20Items%20Applet'%5D.GetName()).GetPR()%3B%0A%20%20%20%20%2B25.%20Double%20click%20BM%20to%20expand%20current%20applet%20%2B%20carrent%20control%2C%20third%20click%20will%20expand%20all%0A%20%20%20%2026.%20wrap%20render%20functions%20into%20try%2Fcatch%20and%20return%20error%20text%20istead%20of%20html%0A%20%20%20%20%2B27.%20Print%20the%20entire%20RecordSet%20and%20hightlight%20an%20active%20record%20instead%20of%20just%20active%20record%0A%20%20%20%2028.%20option%20to%20print%20jquery%20selector%20%24(%22%23123%22)%20or%20node%20selector%20%24(%22%23123%22)%5B0%5D%0A%20%20%20%20%2B29.%20expose%20view%20and%20applet%20title%20as%20applet%20attribute%20and%20as%20a%20value%20for%20AppletTitle%20control%0A%20%20%20%2030.%20option%20to%20toggle%20collapse%20all%20sections%20when%20expanding%20a%20neighbour%0A%20%20%20%20%2B31.%20toggle%20to%20show%20control%2Fapplet%20display%20name%20(GetDisplayName)%20or%20physical%20name%20(GetName)%20in%20the%20list%0A%20%20%20%20%2B32.%20get%20record%20count%20from%20SiebelApp.S_App.GetActiveView().GetAppletMap()%5B'FaCS%20UX2%20NQSC%20Application%20List%20Applet'%5D.GetPModel().GetStateUIMap().GetRowCounter%0A%20%20%20%20%2B34.%20escape%20field%20and%20message%20values%0A%09%2B34.%20invokable%20methods%2C%20swe%20properties%20of%20invokable%20control%0A%09%2B34.%20buscomp%20selector%20should%20be%20by%20id%0A%20%20%20%20%2B35.%20option%20to%20autoclose%20after%20copy%20%0A%20%20%20%20%2B36.%20Option%20to%20display%20a%20current%20rowid%20next%20to%20the%20applet%20%2F%20bc%0A%20%20%20%20%2B37.%20control%20UP%0A%20%20%20%20%2B38.%20link%20from%20the%20control%20to%20the%20field%20or%20just%20expand%20another%20level%20(call%20of%20the%20same%20renderer%20function)%20on%20right%20click%0A%20%20%20%20%2B39.%20fetch%20screen%20name%20using%20SiebelApp.S_App.NavCtrlMngr().GetscreenNavigationPM().Get(%22GetTabInfo%22)%2C%20active%20screen%20%3D%20SiebelApp.S_App.NavCtrlMngr().GetscreenNavigationPM().Get(%22GetSelectedTabKey%22)%0A%20%20%20%20%2B40.%20expandable%20items%20should%20have%20an%20indication%20like%20%22(%3E)%20(%5E)%20(%2B)%20collapsed%20item%22%20%22(V)%20(-)%20expanded%20item%22%0A%20%20%20%20%2B41.%20%5BReset%20options%5D%20button%20intead%20of%20event%20action%0A%20%20%20%20%2B42.%20Expand%20option%20should%20do%20applets%20and%20controls%0A%20%20%20%20%2B43.%20Keep%20Applet%20Controls%20list%20opened%20by%20default%0A%20%20%20%20%2B44.%20Special%20event%3A%0A%20%20%20%20%20%20%20%20-%20special%20attrib%20data-handler%20%3D%20%5Bfield%2C%20buscomp%2C%20applet%20method%2C%20control%20method%2C%20focus%5D%0A%20%20%20%20%20%20%20%20-%20special%20highlight%20that%20supports%20special%20event%20-%20%5Bvalue%5D%20(value)%20%3Cvalue%3E%0A%20%20%20%20%20%20%20%20-%20should%20be%20configurable%20for%20each%20instance%20or%20once%20for%20all%20SE%3F%0A%20%20%20%20%20%20%20%20-%20event%3F%20-%20%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20dbl%20click%20also%20triggers%20single%20click%0A%20%20%20%20%20%20%20%20%20%20%20%20-%20right%20click%20already%20has%20an%20event%20handler%20(expand)%0A%20%20%20%20%20%20%20%20%2B%20(field%2C%20buscomp)%20SE%20on%20control%20field%20is%20to%20open%20a%20field%0A%20%20%20%20%20%20%20%20%2B%20(field)%20SE%20on%20applet%20buscomp%20is%20to%20open%20a%20buscomp%20-%20check%20how%20it%20works%20when%20buscomp%20is%20hidden%0A%20%20%20%20%20%20%20%20%2B%20(applet)%20SE%20on%20available%20method%20is%20to%20call%20it%0A%20%20%20%20%20%20%20%20%2B%20(applet%2C%20control)%20SE%20on%20control%20method%20is%20to%20call%20it%20-%20for%20all%20controls%20with%20InvokeMethod%0A%20%20%20%20%20%20%20%20%2B%20(applet%2C%20control)%20SE%20on%20control%20or%20control%20node%20selector%20or%20separate%20link%20%3Cfocus%3E%20is%20to%20focus%20on%20it%20-%20only%20works%20on%20visible%20elements%0A%20%20%20%20%2B45.%20right%20click%20on%20regular%20links%20(values)%20triggers%20right%20click%20on%20whitespace%0A%20%20%20%20%2B46.%20proper%20indent%20for%20expanded%20section%20is%20to%20wrap%20it%20into%20ul%20insted%20of%20indent%20-%20noticable%20on%20grandchild%20applets%0A%20%20%20%20%2B47.%20translate%20control%20type%20through%20the%20constants%20-%20check%20the%20full%20list%20in%20pwinfra%20and%20other%20PW%20%0A%20%20%20%20%2B48.%20Fancy%20CSS%20to%20fade%20everything%20otside%20the%20deepest%20expanded%20section%20to%20put%20atention%20to%20the%20expanded%20one%0A%20%20%20%20%20%20%20%20-%20not%20possible%20without%20js%20since%20(%3Anot%20and%20%3Ahas)%20are%20not%20supported%0A%20%20%20%20%20%20%20%20-%20append%20a%20special%20class%20%0A%20%20%20%20%2B49.%20fix%20all%20event%20handlers%20to%20be%20configurable%0A%20%20%20%2050.%20code%20review%0A%20%20%20%20%20%20%20%20-%20reorganise%20as%20class%3F%0A%20%20%20%20%2B51.%20revisit%20advanced%2Fdefauklt%20options%20(a%3Arecord%20counter%2C%20c%3Auser%20props%2C%20method%20props%2C%20focus%2C%20b%3Acommit%20pending%2C%20)%0A%20%20%20%20%2B52.%20value%20link%20right%20click%20should%20have%20a%20default%20handler%20(open%20menu%3F)%0A%20%20%20%20%2B53.%20option%20for%20focus%20border%20feature%0A%20%20%20%20%2B54.%20whitespace%20right%20click%20to%20collapse%20last%20expanded%20item%0A%20%20%20%2055.%20Check%20if%20there%20something%20else%20to%20expose%0A%20%20%20%20%2B58.%20list%20applet%20column%20visibility%3F%0A%20%20%20%20%2B59.%20view%20PR%2FPM%20doesn't%20work%20in%20fins%0A%20%20%20%20%2B61.%20rtf%20field%20visibility%2Fselector%0A%09%2B62.%20Show%20control%20length%20only%20if%20there%20a%20field%0A%20%20%20%20%0AVersion%203.0%20features%3A%0A%20%20%20%201.%20New%20concept%20-%20separate%20renderer%20and%20ref-data%3A%20%0A%20%20%20%20%20%20%20%20-%20ref-data%20example%3A%20%7B%0A%20%20%20%20%20%20%20%20%09%22Applet%22%3A%7B%0A%20%20%20%20%20%20%20%20%09%09%22Name%22%3A%22GetName%22%2C%0A%20%20%20%20%20%20%20%20%09%09%22PModel%22%3A%22GetPModel%22%0A%20%20%20%20%20%20%20%20%09%7D%2C%0A%20%20%20%20%20%20%20%20%09%22Control%22%3A%7B%0A%20%20%20%20%20%20%20%20%09%09...%0A%20%20%20%20%20%20%20%20%09%7D%0A%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20-%20store%20the%20structure%20in%20localStorage%0A%20%20%20%20%20%20%20%20-%20on%20label%20rightclick%20-%20exclude%20the%20prop%20from%20the%20tree%2C%20so%20user%20won't%20see%20it%20anymore%0A%20%20%20%20%20%20%20%20-%20under%20options%20-%20have%20a%20list%20of%20properties%20included%20and%20excluded%0A%20%20%20%2060.%20Make%20label%20also%20a%20link%20and%20copy%20function%20path%20on%20click%3F%0A%20%20%20%2056.%20Container%20page%20name%20expandable%20with%20a%20list%20of%20page%20items%20under%20it%20SiebelApp.S_App.GetChildren()%5B3%5D%0A%20%20%20%2057.%20Application%20UP%20%3D%20SiebelApp.S_App.GetPMPropSet()%0A%20%20%20%2033.%20print%20real%20event%20listeners%20since%20chrome%20leads%20to%20the%20jquery%20only%3F%0A%0A*%2F%0A%0A(()%3D%3E%7B%0A%0A%09if%20(%22undefined%22%20%3D%3D%20typeof%20SiebelApp)%7B%0A%09%09alert(%22It%20works%20only%20in%20Siebel%20OUI%20session!%22)%3B%0A%09%09return%3B%0A%09%7D%0A%09%0A%09const%20Id%20%3D%20%22XapuksAboutView2%22%3B%0A%0A%09%2F%2F%20read%20options%20localStorage%0A%09let%20options%20%3D%20%7B%7D%3B%0A%09if%20(localStorage.getItem(Id))%20%7B%0A%09%09options%20%3D%20JSON.parse(localStorage.getItem(Id))%3B%0A%09%7D%20else%20%7B%0A%09%09resetOptions()%3B%0A%09%7D%0A%0A%09let%20%24d%20%3D%20%24(%60.ui-dialog.%24%7BId%7D%60)%3B%09%0A%0A%20%20%20%20%2F%2F%20event%20handling%0A%09const%20handlers%20%3D%20%7B%0A%09%09%22None%22%3A(e)%3D%3E%7B%0A%09%09%09return%20true%3B%0A%09%09%7D%2C%0A%09%09%22Expand%20%2F%20Special%22%3A(e)%3D%3E%7B%0A%09%09%09id%20%3D%20%24(e.target).attr(%22data-ul%22)%3B%0A%09%09%09if%20(id)%20%7B%0A%09%09%09%09%24d.find(%60ul.ul_show%3Anot(%23%24%7Bid%7D)%3Anot(%3Ahas(%23%24%7Bid%7D))%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09%09%24d.find(%22%23%22%20%2B%20id).toggleClass(%5B'ul_show'%2C'ul_hide'%5D)%3B%0A%09%09%09%20%20%20%20e.stopPropagation()%3B%0A%09%09%09%09return%20false%3B%0A%09%09%09%7D%20else%20%7B%0A%09%09%09%09e.stopPropagation()%3B%0A%09%09%09%09return%20true%3B%0A%09%09%09%7D%0A%09%09%7D%2C%0A%09%09%22Expand%20active%20context%22%3A(e)%3D%3E%7B%0A%09%09%09const%20a%20%3D%20SiebelApp.S_App.GetActiveView().GetActiveApplet()%3B%0A%09%09%09if%20(a)%20%7B%0A%09%09%09%09%24d.find(%60ul%23%24%7Ba.GetFullId()%7D%2C%20ul%23%24%7Ba.GetFullId()%7D_controls%2C%20ul%23%24%7Ba.GetActiveControl()%3F.GetInputName()%7D%60).removeClass(%22ul_hide%22).addClass(%22ul_show%22)%3B%0A%09%09%09%7D%0A%09%09%09e%3F.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Copy%20active%20applet%22%3A(e)%3D%3E%7B%0A%09%09%09const%20a%20%3D%20SiebelApp.S_App.GetActiveView().GetActiveApplet()%3B%0A%09%09%09if%20(a)%20%7B%0A%09%09%09%09handlers%5B%22Copy%20value%22%5D(%7B%0A%09%09%09%09%09target%3A%24d.find(%60a%5Bdata-ul%3D%24%7Ba.GetFullId()%7D%5D%60)%5B0%5D%0A%09%09%09%09%7D)%3B%0A%09%09%09%7D%0A%09%09%7D%2C%0A%09%09%22Collapse%20item%22%3A(e)%3D%3E%7B%0A%09%09%09%24d.find(%60ul.ul_show%3Anot(%3Ahas(.ul_show%3Anot(.keep_open)))%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Collapse%20all%22%3A(e)%3D%3E%7B%0A%09%09%09%24d.find(%60ul.ul_show%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Close%20dialog%22%3A(e)%3D%3E%7B%0A%09%09%09%24d.dialog(%22close%22)%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Options%22%3A(e)%3D%3E%7B%0A%09%09%09rOptions()%3B%0A%09%09%09e.stopPropagation()%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Copy%20value%20and%20close%22%3A(e)%3D%3E%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20handlers%5B%22Copy%20value%22%5D(e)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20handlers%5B%22Close%20dialog%22%5D(e)%3B%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Copy%20value%22%3A(e)%3D%3E%7B%0A%09%09%09const%20scope%20%3D%20e.target%3B%0A%09%09%09%2F%2F%20replacing%20link%20with%20intput%20and%20select%20the%20value%0A%09%09%09const%20val%20%3D%20%24(scope).text()%3B%0A%09%09%09%24(scope).hide().after(%22%3Cinput%20id%3D'%22%20%2B%20Id%20%2B%20%22i'%3E%22)%3B%0A%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).val(val).select()%3B%0A%09%09%09%2F%2F%20attempt%20to%20copy%20value%0A%09%09%09if%20(document.execCommand(%22copy%22%2C%20false%2C%20null))%7B%0A%09%09%09%09%2F%2F%20if%20copied%2C%20display%20a%20message%20for%20a%20second%0A%09%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).attr(%22disabled%22%2C%20%22disabled%22).css(%22color%22%2C%22red%22).val(%22Copied!%22)%3B%0A%09%09%09%09setTimeout(()%3D%3E%7B%0A%09%09%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).remove()%3B%0A%09%09%09%09%09%24(scope).show()%3B%0A%09%09%09%09%7D%2C%20700)%3B%0A%09%09%09%7D%20else%20%7B%0A%09%09%09%09%2F%2F%20if%20failed%20to%20copy%2C%20leave%20input%20until%20blur%2C%20so%20it%20can%20be%20copied%20manually%0A%09%09%09%09%24d.find(%22%23%22%20%2B%20Id%20%2B%20%22i%22).blur(()%3D%3E%7B%0A%09%09%09%09%09%24(this).remove()%3B%0A%09%09%09%09%09%24d.find(%22a%22).show()%3B%0A%09%09%09%09%7D)%3B%20%20%20%20%09%0A%09%09%09%7D%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Invoke%20applet%20method%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09var%20applet%20%3D%20SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%24target.attr(%22data-applet%22)%5D%3B%0A%09%09%09const%20method%20%3D%20%24target.text()%3B%0A%09%09%09applet.InvokeMethod(method)%3B%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Invoke%20control%20method%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09var%20applet%20%3D%20SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%24target.attr(%22data-applet%22)%5D%3B%0A%09%09%09var%20control%20%3D%20applet.GetControls()%5B%24target.attr(%22data-control%22)%5D%3B%0A%09%09%09const%20method%20%3D%20%24target.text()%3B%0A%09%09%09SiebelApp.S_App.uiStatus.Busy()%3B%0A%09%09%09try%20%7B%0A%09%09%09applet.GetPModel().OnControlEvent(SiebelApp.Constants.get(%22PHYEVENT_INVOKE_CONTROL%22)%2C%20control.GetMethodName()%2C%20control.GetMethodPropSet()%2C%20%7B%0A%09%09%09%09async%3A%20true%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cb%3A%20()%3D%3ESiebelApp.S_App.uiStatus.Free()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D)%3B%0A%09%09%09%7D%20catch(e)%20%7B%0A%09%09%09%09console.error(e.toString())%3B%0A%09%09%09%09SiebelApp.S_App.uiStatus.Free()%3B%0A%09%09%09%7D%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Focus%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09const%20%24el%20%3D%20%24(%24target.attr(%22data-focus%22))%3B%0A%09%09%09%24d.dialog('close')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24el.focus()%3B%0A%09%09%09e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%2C%0A%09%09%22Expand%20related%22%3A%20(e)%20%3D%3E%20%7B%0A%09%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09%09var%20sel%20%3D%20%24target.attr(%22data-selector%22)%3B%0A%09%09%09%24d.find(%60ul.ul_show%3Anot(.keep_open)%60).removeClass(%22ul_show%22).addClass(%22ul_hide%22)%3B%0A%09%09%09%24d.find(sel).toggleClass(%5B'ul_show'%2C'ul_hide'%5D)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20e.stopPropagation%3F.call(this)%3B%0A%20%20%20%20%09%20%20%20%20return%20false%3B%0A%09%09%7D%0A%0A%09%7D%3B%0A%0A%20%20%20%20%2F%2F%20handle%20double%20click%0A%09if%20(%24d.length)%20%7B%0A%20%20%20%20%20%20%20%20var%20o%20%3D%20options%5B%22bmk_dbl%22%5D%3B%0A%20%20%20%20%20%20%20%20handlers%5Bo%5D()%3B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%0A%09%2F%2F%20render%20the%20dialog%0A%09let%20guid%20%3D%200%3B%0A%09const%20css%20%3D%20%5B%60%3Cstyle%3E%60%2C%20...%5B%0A%09%09%60ul%20%7Bmargin-left%3A20px%7D%60%2C%0A%09%09%60a.x_active%20%7Btext-decoration%3Aunderline%7D%60%2C%0A%09%09%60a.x_hidden%20%7Bfont-style%3Aitalic%7D%60%2C%0A%09%09%60select%20%7Bdisplay%3Ablock%3B%20margin-bottom%3A15px%7D%60%2C%0A%09%09%60.options%20%7Bbackground-color%3Alightgray%3B%20padding%3A15px%3B%20margin%3A10px%7D%60%2C%0A%09%09%60.ul_hide%20%7Bdisplay%3Anone%7D%60%2C%0A%09%09%60.ul_show%20%7Bborder-bottom%3A%201px%20solid%3B%20border-top%3A%201px%20solid%3B%20margin%3A%205px%3B%20padding%3A%205px%3B%20border-color%3A%20lightgray%3B%7D%60%2C%0A%09%09options%5B%22focus_feature%22%5D%20%3D%3D%20%22true%22%20%3F%20%60ul%3Ahas(.ul_show%3Anot(.keep_open))%20%3Ais(label%2Ca)%20%7Bcolor%3Adarkgray!important%7D%60%3A%60%60%2C%0A%20%20%20%09%09options%5B%22focus_feature%22%5D%20%3D%3D%20%22true%22%20%3F%20%60ul.ul_show%3Anot(%3Ahas(.ul_show%3Anot(.keep_open)))%20%3Ais(label%2Ca)%20%7Bcolor%3Ablack!important%7D%60%3A%60%60%2C%0A%09%09%60a%5Bdata-ul%5D%20%7Bfont-weight%3Abold%7D%60%2C%0A%09%09%60a%5Bdata-ul%5D%3Abefore%20%7Bcontent%3A%22%3E%20%22%3B%20opacity%3A0.5%3B%20font-style%3Anormal%7D%60%2C%0A%09%09%60a%5Bdata-handler%5D%3Abefore%2C%20a%5Bdata-focus%5D%3Abefore%20%7Bcontent%3A%22%3C%5B%22%3B%20opacity%3A0.5%3B%20font-style%3Anormal%7D%60%2C%0A%09%09%60a%5Bdata-handler%5D%3Aafter%2C%20a%5Bdata-focus%5D%3Aafter%20%7Bcontent%3A%22%5D%3E%22%3B%20opacity%3A0.5%3B%20font-style%3Anormal%7D%60%2C%0A%09%09%60label%20%7Bfont-size%3A1rem%3B%20margin%3A0px%3B%20font-weight%3Abold%3B%7D%60%2C%0A%09%09%60table%20%7Bdisplay%3Ablock%3B%20overflow-x%3Aauto%3B%20whitespace%3A%20nowrap%7D%60%2C%0A%09%09%60td%20%7Bborder%3Asolid%201px%7D%60%2C%0A%09%09%60.options%20select%20%7Bwidth%3A250px%7D%60%2C%0A%09%5D.map((i)%3D%3Ei%20%3F%20%60.%24%7BId%7D%20%24%7Bi%7D%60%3A%60%60)%2C%60%3C%2Fstyle%3E%60%5D.join(%22%5Cn%22)%3B%0A%0A%09%24d%20%3D%20%24(%60%3Cdiv%20class%3D%22container%22%20title%3D%22About%20View%202.0%22%3E%24%7BrApplication()%7D%24%7Bcss%7D%3C%2Fdiv%3E%60).dialog(%7B%0A%09%09dragStop%3A%20()%20%3D%3E%20%24d.dialog(%7Bheight%3A'auto'%7D)%2C%20%0A%09%09classes%3A%20%7B%22ui-dialog%22%3A%20Id%7D%2C%0A%09%09modal%3A%20true%2C%0A%09%09width%3A%20options%5B%22width%22%5D%2C%0A%09%09close%3A%20()%20%3D%3E%20%24d.dialog('destroy').remove()%2C%0A%09%09buttons%3A%20%5B%0A%09%09%09%7B%0A%09%09%09%20%20%20text%3A'Help'%2C%0A%09%09%09%20%20%20click%3A%20()%20%3D%3E%20window.open('http%3A%2F%2Fxapuk.com%2Findex.php%3Ftopic%3D145'%2C'_blank')%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Feedback'%2C%0A%09%09%09%20%20%20click%3A%20()%20%3D%3E%20window.location.href%3D%22mailto%3Avbabkin%40gmail.com%3Fsubject%3DAboutView2%22%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Settings'%2C%0A%09%09%09%20%20%20click%3A%20rOptions%2C%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Reset%20Settings'%2C%0A%09%09%09%20%20%20click%3A%20resetOptions%0A%09%09%09%7D%2C%7B%0A%09%09%09%20%20%20text%3A'Close%20(esc)'%2C%0A%09%09%09%20%20%20click%3A%20()%20%3D%3E%20%24d.dialog('close')%0A%09%09%09%7D%0A%09%09%5D%0A%09%7D)%3B%0A%09%0A%09function%20dispatchEvent(e%2C%20cb)%20%7B%0A%09%09var%20%24target%20%3D%20%24(e.target)%3B%0A%09%09if%20(cb.name%3F.indexOf(%22Special%22)%20%3E%200)%20%7B%0A%09%09%09if%20(%24target.attr(%22data-handler%22)%20%3D%3D%20%22applet%20method%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Invoke%20applet%20method%22%5D(e)%3B%0A%09%09%09%7D%20else%20if%20(%24target.attr(%22data-handler%22)%20%3D%3D%20%22control%20method%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Invoke%20control%20method%22%5D(e)%3B%0A%09%09%09%7D%20else%20if%20(%24target.attr(%22data-selector%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Expand%20related%22%5D(e)%3B%0A%09%09%09%7D%0A%09%09%7D%0A%09%09if%20(%24target.attr(%22data-focus%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20handlers%5B%22Focus%22%5D(e)%3B%0A%09%09%7D%0A%20%20%20%20%20%20%20%20return%20cb(e)%3B%0A%09%7D%0A%0A%20%20%20%20%24d.contextmenu(handlers%5Boptions%5B%22ws_right%22%5D%5D)%3B%0A%20%20%20%20%24d.click(handlers%5Boptions%5B%22ws_click%22%5D%5D)%3B%0A%20%20%20%20%24d.find(%22a%22).off(%22click%22).click((e)%3D%3EdispatchEvent(e%2C%20handlers%5Boptions%5B%22link_click%22%5D%5D))%3B%0A%20%20%20%20%24d.find(%22a%22).off(%22contextmenu%22).contextmenu((e)%3D%3EdispatchEvent(e%2C%20handlers%5Boptions%5B%22link_right%22%5D%5D))%3B%0A%20%20%20%20%24(%22.ui-widget-overlay%22).click(handlers%5Boptions%5B%22out_click%22%5D%5D)%3B%0A%20%20%20%20%24(%22.ui-widget-overlay%22).contextmenu(handlers%5Boptions%5B%22out_right%22%5D%5D)%3B%0A%0A%20%20%20%20function%20resetOptions(e)%20%7B%0A%09%09options%20%3D%20%7B%0A%09%09%09%22bmk_dbl%22%3A%20%22Expand%20active%20context%22%2C%0A%09%09%09%22ws_click%22%3A%20%22None%22%2C%0A%09%09%09%22ws_right%22%3A%20%22Collapse%20item%22%2C%0A%09%09%09%22link_click%22%3A%20%22Copy%20value%22%2C%0A%09%09%09%22link_right%22%3A%20%22Expand%20%2F%20Special%22%2C%0A%09%09%09%22out_click%22%3A%20%22Close%20dialog%22%2C%0A%09%09%09%22out_right%22%3A%20%22Close%20dialog%22%2C%0A%09%09%09%22adv%22%3A%20%22true%22%2C%0A%09%09%09%22width%22%3A%20%221000%22%2C%0A%09%09%09%22ctrl_list_by%22%3A%20%22name%22%2C%0A%09%09%09%22applet_list%22%3A%20%22applet%20%2F%20bc%22%2C%0A%09%09%09%22applet_list_by%22%3A%20%22name%22%2C%0A%09%09%09%22focus_feature%22%3A%22false%22%0A%09%09%7D%3B%0A%09%09localStorage.setItem(Id%2C%20JSON.stringify(options))%3B%0A%09%7D%0A%0A%20%20%20%20%2F%2F%20render%20functions%0A%20%20%20%20function%20rDropdown(caption%2C%20field%2C%20list)%20%7B%0A%20%20%20%20%09const%20id%20%3D%20field%3B%0A%20%20%20%20%09const%20value%20%3D%20options%5Bfield%5D%3B%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cli%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Clabel%20for%3D%22%24%7Bid%7D%22%3E%24%7Bcaption%7D%3C%2Flabel%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cselect%20id%3D%22%24%7Bid%7D%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20list.map((i)%3D%3E%60%3Coption%20value%3D%22%24%7Bi%7D%22%20%24%7Bi%3D%3Dvalue%3F'selected'%3A''%7D%3E%24%7Bi%7D%3C%2Foption%3E%60)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Fselect%3E%60%2C%0A%20%20%20%20%20%20%20%20%60%3Cli%3E%60%5D.join(%22%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rOptions()%20%7B%0A%20%20%20%20%20%20%20%20if%20(%24d.find(%22.options%22).length)%20%7B%0A%20%20%20%20%20%20%20%20%09%24d.find(%22.options%22).remove()%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%09let%20html%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cdiv%20class%3D%22options%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ch4%3ESETTINGS%3C%2Fh4%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Advanced%20properties%60%2C%20%60adv%60%2C%20%5B%60false%60%2C%20%60true%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Dialog%20width%60%2C%20%60width%60%2C%20%5B%60600%60%2C%20%60800%60%2C%20%601000%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Show%20in%20main%20list%60%2C%20%60applet_list%60%2C%20%5B%60applet%60%2C%20%60applet%20%2F%20bc%60%2C%20%60applet%20%2F%20bc%20%2F%20rowid%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60List%20applets%20by%60%2C%20%60applet_list_by%60%2C%20%5B%60name%60%2C%20%60title%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60List%20controls%20by%60%2C%20%60ctrl_list_by%60%2C%20%5B%60name%60%2C%20%60caption%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Link%20click%60%2C%20%60link_click%60%2C%20%5B%60Copy%20value%60%2C%20%60Copy%20value%20and%20close%60%2C%20%60Expand%20%2F%20Special%60%2C%20%60None%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Link%20right%20click%60%2C%20%60link_right%60%2C%20%5B%60Copy%20value%60%2C%20%60Copy%20value%20and%20close%60%2C%20%60Expand%20%2F%20Special%60%2C%20%60None%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Bookmarklet%20double%20click%60%2C%20%60bmk_dbl%60%2C%20%5B%60Expand%20active%20context%60%2C%20%60Copy%20active%20applet%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Whitespace%20click%60%2C%20%60ws_click%60%2C%20%5B%60None%60%2C%20%60Close%20dialog%60%2C%20%60Options%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Whitespace%20right%20click%60%2C%20%60ws_right%60%2C%20%5B%60None%60%2C%20%60Close%20dialog%60%2C%20%60Options%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Outside%20click%60%2C%20%60out_click%60%2C%20%5B%60Close%20dialog%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Outside%20right%20click%60%2C%20%60out_right%60%2C%20%5B%60Close%20dialog%60%2C%20%60Expand%20active%20context%60%2C%20%60Collapse%20item%60%2C%20%60Collapse%20all%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rDropdown(%60Focus%20feature%60%2C%20%60focus_feature%60%2C%20%5B%60false%60%2C%20%60true%60%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%5Cdiv%3E%60%0A%20%20%20%20%20%20%20%20%09%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%20%20%20%20%09%24d.append(html)%3B%0A%20%20%20%20%20%20%20%20%09%24d.find(%22select%22).change((e)%3D%3E%7B%0A%09%09%09%09const%20c%20%3D%20e%3F.target%3B%0A%09%09%09%09if%20(c)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20options%5Bc.id%5D%20%3D%20c.value%3B%0A%09%09%09%09%20%20%20%20localStorage.setItem(Id%2C%20JSON.stringify(options))%3B%0A%09%09%09%09%7D%0A%09%09%09%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rPS(prop)%20%7B%0A%20%20%20%20%09return%20rItem(%60%3Ca%20href%3D'%23'%3E%24%7Bprop%5B0%5D%7D%3C%2Fa%3E%60%2C%20prop%5B1%5D)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rHierarchy(caption%2C%20value%2C%20advanced)%20%7B%0A%20%20%20%20%09var%20a%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20%20%20while%20(%22object%22%20%3D%3D%3D%20typeof%20value%20%26%26%20value%3F.constructor%3F.name%3F.length%20%3E%201)%20%7B%0A%20%20%20%20%20%20%20%20%09a.push(value.constructor.name)%3B%0A%20%20%20%20%20%20%20%20%09value%20%3D%20value.constructor.superclass%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%09return%20rItem(caption%2C%20a%2C%20advanced)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rItem(caption%2C%20value%2C%20advanced%2C%20attribs%3D%7B%7D)%20%7B%0A%20%20%20%20%20%20%20%20if%20(value%20%26%26%20(!Array.isArray(value)%20%7C%7C%20value.length)%20%7C%7C%20%22boolean%22%20%3D%3D%3D%20typeof%20value)%20%7B%0A%20%20%20%20%20%20%20%20%09if%20(!advanced%20%7C%7C%20options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60)%20%7B%0A%09%09%09%09guid%2B%2B%3B%0A%09%09%09%09let%20id%20%3D%20Id%20%2B%20guid%3B%0A%09%09%09%09let%20sAttr%20%3D%20Object.entries(attribs).map((%5Bp%2Cv%5D)%3D%3E%60%24%7Bp%7D%3D%22%24%7Bv%7D%22%60).join(%22%20%22)%3B%0A%09%09%09%09return%20(Array.isArray(value)%20%3F%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cli%3E%60%2C%0A%09%09%09%09%09%60%3Clabel%20for%3D%22%24%7Bid%7D_0%22%3E%60%2Ccaption%2C%60%3A%3C%2Flabel%3E%20%60%2C%0A%09%09%09%09%09value.map((e%2C%20i)%20%3D%3E%60%3Ca%20href%3D%22%23%22%20id%3D%22%24%7Bid%7D_%24%7Bi%7D%22%20%24%7BsAttr%7D%3E%24%7Be%7D%3C%2Fa%3E%60).join(%22%20%3E%20%22)%2C%0A%09%09%09%09%09%60%3C%2Fli%3E%60%0A%09%09%09%09%5D%20%3A%20%5B%0A%09%09%09%09%09%60%3Cli%3E%60%2C%0A%09%09%09%09%09%60%3Clabel%20for%3D%22%24%7Bid%7D%22%3E%60%2Ccaption%2C%60%3A%3C%2Flabel%3E%20%60%2C%0A%09%09%09%09%09%60%3Ca%20href%3D%22%23%22%20id%3D%22%24%7Bid%7D%22%20%24%7BsAttr%7D%3E%60%2CescapeHtml(value)%2C%60%3C%2Fa%3E%60%2C%0A%09%09%09%09%09%60%3C%2Fli%3E%60%0A%09%09%09%09%5D).join(%22%22)%3B%0A%20%20%20%20%20%20%20%20%09%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%22%22%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rControl(control)%20%7B%0A%20%20%20%20%09const%20id%20%3D%20control.GetInputName()%3B%0A%20%20%20%20%09const%20applet%20%3D%20control.GetApplet()%3B%0A%20%20%20%20%09const%20pr%20%3D%20SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())%3F.GetPR()%3B%0A%20%20%20%20%09const%20bc%20%3D%20applet.GetBusComp()%3B%0A%20%20%20%20%09const%20ps%20%3D%20control.GetMethodPropSet()%3B%0A%20%20%20%20%09const%20up%20%3D%20control.GetPMPropSet()%3B%0A%20%20%20%20%09const%20con%20%3D%20SiebelApp.Constants%3B%0A%20%20%20%20%09let%20sel%20%3D%20%60%23%24%7Bapplet.GetFullId()%7D%20%5Bname%3D%24%7Bcontrol.GetInputName()%7D%5D%60%3B%0A%20%20%20%20%09if%20(con.get(%22SWE_CTRL_RTCEMBEDDED%22)%20%3D%3D%3D%20control.GetUIType())%20%7B%0A%20%20%20%20%09%09sel%20%3D%20%60%23%24%7Bapplet.GetFullId()%7D%20%23cke_%24%7Bcontrol.GetInputName()%7D%60%3B%0A%20%20%20%20%09%7D%0A%20%20%20%20%09const%20cls%20%3D%20control%20%3D%3D%3D%20applet.GetActiveControl()%20%3F%20'x_active'%20%3A%20%24(sel).length%20%3D%3D%200%20%7C%7C%20%24(sel).is(%22%3Avisible%22)%20%3F%20''%20%3A%20'x_hidden'%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cli%3E%60%2C%0A%09%09%20%20%20%20%60%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_c%22%20class%3D%22%24%7Bcls%7D%22%3E%60%2C%20%0A%09%09%09%20%20%20%20options%5B%22ctrl_list_by%22%5D%20%3D%3D%20'caption'%20%26%26%20control.GetDisplayName()%20%3F%20control.GetDisplayName()%20%3A%20control.GetName()%20%2C%20%0A%09%09%09%60%3C%2Fa%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_c%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09rItem(control.GetControlType()%20%3D%3D%20con.get(%22SWE_PST_COL%22)%3F%22List%20column%22%3Acontrol.GetControlType()%20%3D%3D%20con.get(%22SWE_PST_CNTRL%22)%3F%22Control%22%3Acontrol.GetControlType()%2C%20control.GetName())%2C%0A%09%09%09rItem(%22Display%20name%22%2C%20control.GetDisplayName())%2C%0A%09%09%09rItem(%22Field%22%2C%20control.GetFieldName()%2C%20false%2C%20%7B%22data-handler%22%3A%22field%22%2C%22data-selector%22%3A%60ul%23%24%7Bapplet.GetFullId()%7D_bc%2Cul%23%24%7Bapplet.GetFullId()%7D_bc_%24%7Bapplet.GetBusComp().GetFieldMap()%5Bcontrol.GetFieldName()%5D%3F.index%7D%60%7D)%2C%0A%09%09%09rItem(%22Value%22%2C%20bc%3F.GetFieldValue(control.GetFieldName()))%2C%0A%09%09%09rItem(%22Message%22%2C%20control.GetControlMsg())%2C%0A%09%09%09...(control.GetMessageVariableMap()%20%26%26%20Object.keys(control.GetMessageVariableMap()).length%20%3F%20%0A%09%09%09%09%5B%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_var%22%3EMessage%20variables%20(%24%7BObject.keys(control.GetMessageVariableMap()).length%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_var%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09%09Object.entries(control.GetMessageVariableMap()).map(rPS).join(%22%5Cn%22)%2C%0A%09%09%09%09%60%3C%2Ful%3E%60%5D%20%3A%20%5B%5D)%2C%09%0A%09%09%09rItem(%22Type%22%2C%20control.GetUIType())%2C%0A%09%09%09rItem(%22LOV%22%2C%20control.GetLovType())%2C%0A%09%09%09rItem(%22MVG%22%2C%20control.IsMultiValue())%2C%0A%09%09%09rItem(%22Method%22%2C%20control.GetMethodName()%2C%20false%2C%20%7B%22data-handler%22%3A%22control%20method%22%2C%20%22data-applet%22%3Aapplet.GetName()%2C%22data-control%22%3Acontrol.GetName()%7D)%2C%0A%09%09%09...(options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60%20%26%26%20ps%20%26%26%20ps.propArrayLen%20%3F%20%0A%09%09%09%09%5B%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_method%22%3EMethod%20properties%20(%24%7Bps.propArrayLen%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_method%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09%09Object.entries(ps.propArray).map(rPS).join(%22%5Cn%22)%2C%0A%09%09%09%09%60%3C%2Ful%3E%60%5D%20%3A%20%5B%5D)%2C%0A%09%09%09%2F%2FrItem(%22Id%22%2C%20id)%2C%0A%09%09%09rItem(%22Id%22%2C%20options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60%20%26%26%20%24(sel).is(%22%3Afocusable%22)%20%3F%20%5Bid%2C%20%60%3Ca%20href%3D%22%23%22%20data-focus%3D'%24%7Bsel%7D'%3EFocus%3C%2Fa%3E%60%5D%3Aid)%2C%09%0A%09%09%09rItem(%22Immidiate%20post%20changes%22%2C%20control.GetPostChanges())%2C%0A%09%09%09rItem(%22Display%20format%22%2C%20control.GetDisplayFormat())%2C%0A%09%09%09rItem(%22HTML%20attribute%22%2C%20control.GetHTMLAttr()%2C%20true)%2C%0A%09%09%09rItem(%22Display%20mode%22%2C%20control.GetDispMode())%2C%0A%09%09%09rItem(%22Popup%22%2C%20control.GetPopupType()%20%26%26%20%5Bcontrol.GetPopupType()%2C%20control.GetPopupWidth()%2C%20control.GetPopupHeight()%5D.join(%22%20%2F%20%22))%2C%0A%09%09%09rHierarchy(%22Plugin%20wrapper%22%2C%20SiebelApp.S_App.PluginBuilder.GetPwByControl(pr%2C%20control)%2C%20true)%2C%0A%09%09%09rItem(%22Length%22%2C%20control.GetFieldName()%20%3F%20control.GetMaxSize()%20%3A%20%22%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...(options%5B%22adv%22%5D%20%3D%3D%3D%20%60true%60%20%26%26%20up%20%26%26%20up.propArrayLen%20%3F%20%0A%09%09%09%09%5B%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_up%22%3EUser%20properties%20(%24%7Bup.propArrayLen%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_up%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09%09Object.entries(up.propArray).map(rPS).join(%22%5Cn%22)%2C%0A%09%09%09%09%60%3C%2Ful%3E%60%5D%20%3A%20%5B%5D)%2C%09%0A%09%09%09rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%22%24%7Bapplet.GetName()%7D%22%5D.GetControls()%5B%22%24%7Bcontrol.GetName()%7D%22%5D%60%2C%20true)%2C%0A%09%09%09%24(sel).length%20%3E%200%20%3F%20rItem(%22Node%22%2C%20%60%24(%22%24%7Bsel%7D%22)%60%2C%20true)%20%3A%20%60%60%2C%0A%09%09%60%3C%2Ful%3E%60%2C%60%3C%2Fli%3E%60%5D.join(%22%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rApplet(applet)%20%7B%0A%20%20%20%20%09const%20cm%20%3D%20Object.values(applet.GetControls())%3B%0A%20%20%20%20%09const%20mm%20%3D%20applet.GetCanInvokeArray()%3B%0A%20%20%20%20%09const%20id%20%3D%20applet.GetFullId()%3B%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cul%20id%3D%22%24%7Bid%7D%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09rItem(%22Applet%22%2C%20applet.GetName())%2C%0A%09%09%09rItem(%22BusComp%22%2C%20applet.GetBusComp()%3F.GetName()%2C%20false%2C%20%7B%22data-handler%22%3A%22buscomp%22%2C%20%22data-selector%22%3A%60ul%23%24%7Bapplet.GetFullId()%7D_bc%60%7D)%2C%0A%09%09%09rItem(%22Title%22%2C%20SiebelApp.S_App.LookupStringCache(applet.GetTitle()))%2C%0A%09%09%09rItem(%22Mode%22%2C%20applet.GetMode())%2C%0A%09%09%09rItem(%22Record%20counter%22%2C%20applet.GetPModel().GetStateUIMap().GetRowCounter%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PModel%22%2C%20applet.GetPModel()%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PRender%22%2C%20SiebelAppFacade.ComponentMgr.FindComponent(applet.GetName())%3F.GetPR()%2C%20true)%2C%0A%09%09%09rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetActiveView().GetAppletMap()%5B%22%24%7Bapplet.GetName()%7D%22%5D%60%2C%20true)%2C%0A%09%09%09rItem(%22Node%22%2C%20%60%24(%22%23%24%7Bapplet.GetFullId()%7D%22)%60%2C%20true)%2C%0A%09%09%09%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_methods%22%3EMethods%20(%24%7Bmm.length%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_methods%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09mm.map(m%3D%3E%5B%60%3Cli%3E%60%2C%20%60%3Ca%20href%3D%22%23%22%20data-handler%3D%22applet%20method%22%20data-applet%3D%22%24%7Bapplet.GetName()%7D%22%3E%60%2C%20m%2C%20%60%3C%2Fa%3E%60%2C%20%60%3C%2Fli%3E%60%5D.join(%22%22)).join(%22%5Cn%22)%2C%0A%09%09%09%60%3C%2Ful%3E%60%2C%0A%09%09%09%60%3Cli%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_controls%22%3EControls%20(%24%7Bcm.length%7D)%3A%3C%2Fa%3E%3C%2Fli%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D_controls%22%20class%3D%22ul_show%20keep_open%22%3E%60%2C%20%2F%2F%3Cul%20id%3D%22%24%7Bid%7D_controls%22%20class%3D%22ul_show%22%3E%0A%09%09%09...cm.map(rControl)%2C%0A%09%09%09%60%3C%2Ful%3E%60%2C%0A%09%09%60%3C%2Ful%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rField(field%2C%20id)%20%7B%0A%20%20%20%20%09const%20bc%20%3D%20field.GetBusComp()%3B%0A%20%20%20%20%09const%20name%20%3D%20SiebelApp.S_App.LookupStringCache(field.GetName())%3B%0A%20%20%20%20%09return%20%5B%60%3Cli%3E%60%2C%0A%09%09%09%60%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D%22%3E%60%2C%20name%2C%20%60%3C%2Fa%3E%60%2C%0A%09%09%09%60%3Cul%20id%3D%22%24%7Bid%7D%22%20class%3D%22ul_hide%22%3E%60%2C%0A%09%09%09rItem(%22Field%22%2C%20name)%2C%0A%09%09%09rItem(%22Value%22%2C%20field.GetBusComp().GetFieldValue(name))%2C%0A%09%09%09rItem(%22Type%22%2C%20field.GetDataType())%2C%0A%09%09%09rItem(%22Length%22%2C%20field.GetLength())%2C%0A%09%09%09rItem(%22Search%20spec%22%2C%20field.GetSearchSpec())%2C%0A%09%09%09rItem(%22Calculated%22%2C%20!!field.IsCalc())%2C%0A%09%09%09rItem(%22Bounded%20picklist%22%2C%20!!field.IsBoundedPick())%2C%0A%09%09%09rItem(%22Read%20only%22%2C%20!!field.IsReadOnly())%2C%0A%09%09%09rItem(%22Immediate%20post%20changes%22%2C%20!!field.IsPostChanges())%2C%0A%09%09%09rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetBusObj().GetBusCompByName(%22%24%7Bbc.GetName()%7D%22).GetFieldMap()%5B%22%24%7Bname%7D%22%5D%60%2C%20true)%2C%0A%09%09%60%3C%2Ful%3E%60%2C%20%60%3C%2Fli%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rBC(a%2C%20id)%20%7B%0A%20%20%20%20%09var%20bc%20%3D%20a.GetBusComp()%3B%0A%20%20%20%20%09const%20fields%20%3D%20Object.values(bc.GetFieldMap())%3B%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cul%20id%3D%22%24%7Bid%7D%22%20class%3D%22ul_hide%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22BusComp%22%2C%20bc.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Commit%20pending%22%2C%20!!bc.commitPending%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Can%20update%22%2C%20!!bc.canUpdate)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Search%20spec%22%2C%20bc.GetSearchSpec())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Sort%20spec%22%2C%20bc.GetSortSpec())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Current%20row%20id%22%2C%20bc.GetIdValue())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Object%22%2C%20%60SiebelApp.S_App.GetBusObj().GetBusComp(%22%24%7Ba.GetBCId()%7D%22)%60%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cli%3E%3Clabel%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_rec%22%3ERecords%3A%20%24%7BMath.abs(bc.GetCurRowNum())%7D%20of%20%24%7Bbc.GetNumRows()%7D%24%7Bbc.IsNumRowsKnown()%3F''%3A'%2B'%7D%3C%2Fa%3E%3C%2Flabel%3E%3C%2Fli%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cul%20id%3D%22%24%7Bid%7D_rec%22%20class%3D%22ul_hide%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctable%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctr%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...Object.keys(bc.GetFieldMap()).map((i)%3D%3E%60%3Cth%3E%24%7Bi%7D%3C%2Fth%3E%60)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ftr%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...bc.GetRecordSet().map((r%2C%20i)%3D%3E%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctr%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20...Object.values(r).map(v%3D%3E%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3Ctd%3E%3Ca%20href%3D%22%23%22%20%24%7Bbc.GetSelection()%20%3D%3D%20i%20%3F%20%60%20class%3D%22x_active%22%60%20%3A%20%60%60%7D%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20v%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Fa%3E%3C%2Ftd%3E%60%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D.join(%22%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ftr%3E%60%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D.join(%22%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ftable%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cli%3E%3Clabel%3E%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Bid%7D_fields%22%3EFields(%24%7Bbc.GetFieldList()%3F.length%7D)%3A%3C%2Fa%3E%3C%2Flabel%3E%3C%2Fli%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cul%20id%3D%22%24%7Bid%7D_fields%22%20class%3D%22ul_show%20keep_open%22%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20...fields.map((field%2C%20i)%20%3D%3E%20rField(field%2C%20id%20%2B%20%22_%22%20%2B%20field.index))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%60%2C%0A%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20rApplication()%20%7B%0A%20%20%20%20%20%20%20%20const%20app%20%3D%20SiebelApp.S_App%3B%0A%20%20%20%20%20%20%20%20const%20view%20%3D%20app.GetActiveView()%3B%0A%20%20%20%20%20%20%20%20const%20bo%20%3D%20app%3F.GetBusObj()%3B%0A%20%20%20%20%20%20%20%20const%20bm%20%3D%20bo%3F.GetBCArray()%3B%0A%20%20%20%20%20%20%20%20const%20scrPM%20%3D%20SiebelApp.S_App.NavCtrlMngr()%3F.GetscreenNavigationPM()%3B%0A%20%20%20%20%20%20%20%20let%20am%20%3D%20Object.values(view%3F.GetAppletMap())%3B%0A%20%20%20%20%20%20%20%20var%20ws%20%3D%20SiebelApp.S_App.GetWSInfo().split(%22_%22)%3B%0A%20%20%20%20%20%20%20%20var%20wsver%20%3D%20ws.pop()%3B%0A%0A%20%20%20%20%20%20%20%20var%20amCache%20%3D%20%7B%7D%3B%0A%20%20%20%20%20%20%20%20Object.assign(amCache%2C%20view%3F.GetAppletMap())%3B%0A%0A%09%09%2F%2F%20Identifying%20a%20primary%20BC%0A%09%09var%20paa%20%3D%20Object.values(SiebelApp.S_App.GetActiveView().GetAppletMap()).filter((a)%20%3D%3E%20!a.GetParentApplet()%20%26%26%20(!a.GetBusComp()%20%7C%7C%20!a.GetBusComp().GetParentBusComp()))%3B%0A%09%09if%20(!paa.length)%20%7B%0A%09%09%09alert(%22Failed%20to%20identify%20a%20primary%20BusComp!%22)%0A%09%09%7D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20return%20%5B%60%3Cul%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Application%22%2C%20app.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Screen%22%2C%20scrPM%3F.Get(%22GetTabInfo%22)%5BscrPM%3F.Get(%22GetSelectedTabKey%22)%5D%3F.screenName)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22View%22%2C%20view.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Task%22%2C%20view.GetActiveTask())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PModel%22%2C%20SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())%3F.GetPM()%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rHierarchy(%22PRender%22%2C%20SiebelAppFacade.ComponentMgr.FindComponent(view.GetName())%3F.GetPR()%2C%20true)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22BusObject%22%2C%20bo%3F.GetName())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20rItem(%22Workspace%22%2C%20%5Bws.join(%22_%22)%2C%20wsver%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Clabel%3EApplets%20(%24%7Bam.length%7D)%20%2F%20BusComps%20(%24%7Bbm.length%7D)%3A%3C%2Flabel%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%60%3Cul%3E%60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20hierBC(paa%5B0%5D.GetBusComp()%2C%200%2C%20amCache)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20...Object.values(amCache).map((a)%3D%3ErAppletName(a%2C%200%2C%20amCache))%2C%0A%20%20%20%20%20%20%20%20%60%3C%2Ful%3E%3C%2Ful%3E%60%5D.join(%22%5Cn%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20prints%20applet%20name%0A%20%20%20%20function%20rAppletName(a%2C%20l%2C%20amCache)%20%7B%0A%20%20%20%20%09delete%20amCache%5Ba.GetName()%5D%3B%0A%20%20%20%20%09return%20%5B%60%3Cli%3E%60%2C%0A%09%09%09%09%20%20%20%20%60%3Cul%3E%60.repeat(l)%2C%0A%09%09%09%09%09%60%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Ba.GetFullId()%7D%22%20class%3D%22%24%7Ba%20%3D%3D%3D%20SiebelApp.S_App.GetActiveView().GetActiveApplet()%20%3F%20'x_active'%20%3A%20%24(%60%23%24%7Ba.GetFullId()%7D%60).is(%22%3Avisible%22)%20%3F%20''%20%3A%20'x_hidden'%7D%22%3E%60%2C%0A%09%09%09%09%09%09options%5B'applet_list_by'%5D%20%3D%3D%20'title'%20%26%26%20SiebelApp.S_App.LookupStringCache(a.GetTitle())%20%3F%20SiebelApp.S_App.LookupStringCache(a.GetTitle())%20%3A%20a.GetName()%2C%0A%09%09%09%09%09%60%3C%2Fa%3E%60%2C%0A%09%09%09%09%09a.GetBusComp()%20%26%26%20options%5B%22applet_list%22%5D.indexOf(%22bc%22)%20%3E%20-1%20%3F%20%60%20%2F%20%3Ca%20href%3D%22%23%22%20data-ul%3D%22%24%7Ba.GetFullId()%7D_bc%22%3E%24%7Ba.GetBusComp().GetName()%7D%3C%2Fa%3E%60%20%3A%20%60%60%2C%0A%09%09%09%09%09a.GetBusComp()%20%26%26%20a.GetBusComp().GetIdValue()%20%26%26%20options%5B%22applet_list%22%5D.indexOf(%22rowid%22)%20%3E%20-1%20%3F%20%60%20%2F%20%3Ca%20href%3D%22%23%22%3E%24%7Ba.GetBusComp().GetIdValue()%7D%3C%2Fa%3E%60%20%3A%20%60%60%2C%0A%09%09%09%09%09rApplet(a)%2C%0A%09%09%09%09%09a.GetBusComp()%20%26%26%20rBC(a%2C%20a.GetFullId()%20%2B%20%22_bc%22)%2C%0A%09%09%09%09%09%60%3C%2Ful%3E%60.repeat(l)%2C%0A%09%09%09%09%60%3C%2Fli%3E%60%5D.join(%22%22)%3B%0A%20%20%20%20%7D%0A%0A%09%2F%2F%20prints%20applets%20based%20on%20bc%20or%20parent%20applet%20(rec)%0A%09function%20hierApplet(bc%2C%20pa%2C%20l%2C%20amCache)%20%7B%0A%09%09return%20Object.values(amCache).filter((a)%20%3D%3E%20bc%20%26%26%20a.GetBusComp()%20%3D%3D%3D%20bc%20%7C%7C%20pa%20%26%26%20a.GetParentApplet()%20%3D%3D%3D%20pa).map((a)%20%3D%3E%20!(a.GetName()%20in%20amCache)%20%3F%20%22%22%20%3A%20%5B%0A%09%09%09rAppletName(a%2C%20l%2C%20amCache)%2C%0A%09%09%09hierApplet(null%2C%20a%2C%20l%20%2B%201%2C%20amCache)%20%2F%2F%20look%20for%20child%20applets%0A%09%09%5D.join(%22%5Cn%22))%3B%0A%09%7D%0A%0A%09%2F%2F%20prints%20applets%20based%20on%20BC%20hierarchy%20(rec)%0A%09function%20hierBC(bc%2C%20l%2C%20amCache)%20%7B%0A%09%09return%20%5B%0A%09%09%09hierApplet(bc%2C%20null%2C%20l%2C%20amCache)%3F.join(%22%5Cn%22)%2C%0A%09%09%09...SiebelApp.S_App.GetActiveBusObj().GetBCArray().filter((e)%20%3D%3E%20e.GetParentBusComp()%20%3D%3D%3D%20bc).map((b)%20%3D%3E%20hierBC(b%2C%20l%20%2B%201%2C%20amCache))%0A%09%09%5D.join(%22%5Cn%22)%3B%0A%09%7D%0A%0A%09%2F%2F%20utilities%0A%09function%20escapeHtml(html)%20%7B%0A%09%09return%20html.toString()%0A%09%09%20%20%20%20.replace(%2F%26%2Fg%2C%20%22%26amp%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F%3C%2Fg%2C%20%22%26lt%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F%3E%2Fg%2C%20%22%26gt%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F%22%2Fg%2C%20%22%26quot%3B%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.replace(%2F'%2Fg%2C%20%22%26%23039%3B%22)%3B%0A%09%7D%0A%7D)()%7D)()%3B
I used Caio's tool to convert source code into a bookmarklet.
Today I want to share a couple of SQLs which helps me to leverage workspaced repository tables.
The first one is a simple flat query of workspace affected objects. Same data you see when clicking workspace in Siebel Tools.Filter by the object name/type to get an idea of who is modifying/modified the object.
select w.name WS_NAME, w.status_cd WS_STATUS, v.VERSION_NUM WS_VER, pw.name BASE_WS_NAME,
u.fst_name WS_AUTHOR, o.OBJ_TYPE , o.OBJ_NAME, o.operation_cd, o.last_upd
from siebel.S_REPOSITORY r -- repository
join siebel.S_WORKSPACE pw on r.ROW_ID = pw.REPOSITORY_ID -- baseline workspace
join siebel.S_WORKSPACE w on w.PAR_WS_ID = pw.ROW_ID -- child / dev workspace with changes
join siebel.S_WS_VERSION v on v.ws_id = w.row_id -- versions of the workspace
join siebel.S_CONTACT u on u.ROW_ID = w.CREATED_BY -- ws author
join siebel.S_RTC_MOD_OBJ o on o.WS_VER_ID = v.ROW_ID and r.ROW_ID = o.REPOSITORY_ID -- modified objects
where r.name = 'Siebel Repository'
and w.NAME = 'dev_vbabkin_d412_1' -- filter by dev branch name
--and pw.NAME = 'int_dev' -- or baseline/parent branch name
--and o.obj_type = 'Business Component' and o.obj_name = 'Account' -- or by affected object
order by o.last_upd desc;
Keep in mind that SQL only lists top-level objects (Applet, BusComp, Picklist etc) without any specific details.
The second query gives full information about object modifications in delivered workspaces including each attribute across all sub-objects.When you deliver a workspace Siebel logs the history of all modifications done to the objects in your workspace. Below SQL just puts all the logs tables together.
select v.VERSION_NUM WS_VER, dw.name WS_NAME, u.fst_name AUTHOR, m.last_upd, w.name BASE_WS_NAME,
c.TOP_PARENT_TYPE TOP_OBJ_TYPE, c.TOP_PARENT_NAME TOP_OBJ_NAME,
c.obj_type, c.object_name,
a.attribute, NVL(a.old_cust_value, a.old_std_value) OLD_VAL, a.new_std_value NEW_VAL,
c.OBJ_NAME, c.STATUS
from siebel.S_REPOSITORY r -- repository
join siebel.S_WORKSPACE w on r.ROW_ID = w.REPOSITORY_ID -- baseline workspace
join siebel.S_WS_VERSION v on w.ROW_ID = v.WS_ID and r.ROW_ID = v.REPOSITORY_ID -- versions of baseline workspace
join siebel.S_WS_MERGE_LOG m on m.RESULT_WS_VER_ID = v.ROW_ID -- workspace merge info
join siebel.S_WS_VERSION dv on dv.ROW_ID = m.FROM_WS_VER_ID -- versions of delivered workspace
join siebel.S_WORKSPACE dw on dw.ROW_ID = dv.WS_ID -- delivered workspace (child branches)
join siebel.S_CONTACT u on u.ROW_ID = dw.CREATED_BY -- ws author
left join siebel.S_WS_MG_LOGOBJ c on c.WS_MERGE_LOG_ID = m.row_id -- modified child objects3445rg
left join siebel.S_WS_MG_LOGATTR a on a.ws_merge_log_id = m.row_id and a.ws_mg_logobj_id = c.row_id -- modified attributes
where r.name = 'Siebel Repository'
and w.NAME = 'int_dev' --filter by parent/destination/baseline workspace name
--and dw.name like 'dev_vbabkin_d412_1' -- by delivered workspace name
and c.TOP_PARENT_TYPE = 'Business Component' and c.TOP_PARENT_NAME = 'Account' -- by top level object
--and c.obj_type='Business Component User Prop' and c.object_name like 'Named Method%' -- by child object (could be same as top if you are looking for attrib changes)
--and a.attribute = 'Inactive' and a.new_std_value = 'Y' -- by attribute
order by v.VERSION_NUM desc, c.TOP_PARENT_NAME, c.object_name;
Unlike the first SQL it doesn't give you info about in-progress workspaces.
Finally, as a bonus below is a SQL to check if there is a workspace delivery in progress. Run it before delivering your workspace to avoid annoying "Another workspace delivery/rebase is in-progress. Please try after sometime." error with a failed workspace to be resubmitted.
DECLARE
wsname varchar(30) := 'int_dev'; -- baseline/parent workspace where you delivering to
BEGIN
SELECT name into wsname FROM siebel.S_WORKSPACE w WHERE w.name = wsname FOR UPDATE NOWAIT;
ROLLBACK;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -54 THEN
raise_application_error(-20001, 'Another delivery in progress');
ELSE
ROLLBACK;
END IF;
END;
To be continued...
In this article, we'll do a scriptless PreSetFieldValue validation as an alternative solution to field-level validation. Clearly, PreSetFieldValue is not always the best fit for user input validations. But when you've been given no choice, here is a declarative and scalable solution to consider.
We'll be using a well-known stack: RunTime Events (RTE) + Data Validation Manager (DVM). And I'll also show you some tricks to work around the limitations.
The first challenge is to fetch a new field value. As you know at the PreSetFieldValue event BusComp holds an original value. Here is a couple of Siebel Support articles explaining the problem:
The solution is in the implicit parameter that runtime event passes by to a business service. And this parameter is [PreSetFieldValue]. Separate thanks to Jason for the hint!
Parameter | Description |
---|---|
Object Name | The name of the object experiencing the event |
Event Type | The type of object (BusComp, Applet, Application) |
ActionSet | The name of the action set |
EventId | The ROW_ID of the event |
Sub Event | The content of the Sub Event field (method, field, view name) |
Action | The name of the action |
Event Name | The name of the event such as PreWriteRecord, InvokeMethod etc |
Context | The content of the Business Service Context field. Alex has a great article on how to take an advantage of it. |
Business Component Name | An active BucComp name. Only available when [Object Type] = "Applet" or "BusComp" |
PreSetFieldValue | A new field value. Only available when [Event] = "PreSetFieldValue" |
There are probably other event-specific parameters out there |
Next problem is that the DVM business service can't handle implicit RTE parameters. I presume since DVM business service handles the Context parameter natively, it probably replaces the implicit inputs PropertySet with the new one constructed from the Context parameter.
Anyway, it shouldn't stop us. We can call another BS that accepts RTE parameters and converts them into profile attributes(PA) and then we can use the PA in the DVM rule. Feel free to create your generic BS which converts input parameter(s) into profile attribute(s) or use [User Registration] business service.
Imagine you've been asked to ensure [Account.Close Reason] is provided when you set [Account.Status] = 'Closed' and presumably locking the record.
So, let's start with creating a Runtime ActionSet "Account Close Reason Validation" with two actions as below:
Sequence | Action Type | Business Service Name | Business Service Method | Business Service Context |
---|---|---|---|---|
1 | BusService | User Registration | SetProfileAttr | |
2 | BusService | Data Validation Manager | Validate | RuleSet,Account Close Reason Validation |
Now we can create a Runtime Event:
Object Type | Object Name | Event | Subevent | Action Set Name |
---|---|---|---|---|
BusComp | Account | PreSetFieldValue | Status | Account Close Reason Validation |
And finally a DVM RuleSet with one rule:
Name | Stop On Error | Immediate Display | Rule Expression |
---|---|---|---|
Account Close Reason Validation | Y | Y | GetProfileAttr("PreSetFieldValue") <> "Closed" OR [Close Reason] IS NOT NULL |
Done! Another scriptless solution in your toolbox!
P.S.: Beware that GetPofileAttr function always returns string. To retrieve other data types, please use type specific functions.
If you are not quite satisfied with out-of-the-box [Inspect Workspace UI], in this article I offer you to build your own UI or to check out my version.
As usual, the front-end going to be based on a JQuery dialog and compiled into a bookmarklet while the back-end is another method in the [FWK Runtime] business service (BS).
The idea is simple: to find a workspace record in [Repository Workspace] BusComp by name and run OpenWS and PreviewWS methods.
Here is how your BS method might look like:
function InspectWS(Inputs, Outputs) {
var name = Inputs.GetProperty("Name");
var bo = TheApplication().GetBusObject("Workspace");
var bc = bo.GetBusComp("Repository Workspace");
try {
bc.SetSearchExpr('[Name] = "' + name + '"');
bc.SetViewMode(AllView);
bc.ExecuteQuery(ForwardBackward);
if (bc.FirstRecord()) {
bc.InvokeMethod("OpenWS");
bc.InvokeMethod("PreviewWS");
} else {
throw "Workspace name not found: " + name;
}
} catch (e) {
throw e;
} finally {
bc = null;
bo = null;
}
}
Don't forget to publish your BS through [ClientBusinessService] application user property and probably make it a client-side business service, so you won't have a problem with the upstream migration.
Here is a simplified dialog with a text field where you paste a workspace name and a button to run a BS method:
(() => {
if ("undefined" === typeof SiebelApp) {
alert("It works only in Siebel OUI session!");
return;
}
const func = "SiebelInspectWS";
const html = `<div title="Inspect Workspace"><input type="text" id = "${func}" style="width:100%"></div>`;
const $d = $(html).dialog({
modal: true,
width: 640,
buttons: [{
text: 'Inspect',
click: () => {
const service = SiebelApp.S_App.GetService("FWK Runtime");
let ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Name", $('#' + func).val());
let config = {
async: false,
cb: function (methodName, inputSet, outputSet) {
if (outputSet.GetProperty("Status") == "Error") {
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}
alert(sRes || "Done!");
}
};
service.InvokeMethod("InspectWS", ps, config);
}
}, {
text: 'Close (Esc)',
click: () => $(this).dialog('close')
}]
});
})();
To open the UI just run the above snippet from the browser console, snippet, file or compile it into a bookmarklet. Make sure you transform/compile it before using in old browsers like IE.
I've advanced the initial version of Inspect Workspace UI with quite a few features, so check out my latest implementation below.
Features:
One of the reasons this approach is so quick, is because it doesn't clear all cache every time. So, remember to clear the cache separately if required. For example, if you created a new view in the workspace, clear the responsibility cache after inspecting it.
Be careful when re-inspecting a workspace from the same view where changes are, it might crash the application. It helps if you step out of the test view before re-inspecting.
Source code (v1.5):
/*
@desc Inspect Workspace UI
@author VB(xapuk.com)
@version 1.5 2020/11/08
@requires "FWK Runtime" business service to be published (Application ClientBusinessService usep property)
@features
+elements: help text hidden by default, input field with the history, message bar, 3 buttons
+don't accept value shorter then 3 chars excluding *
+async call with busy overlay
+highlight search pattern in all found results
+shows a text if number of results = limit
+cut history to 5 items and don't store empty requests
+insect ws on click of <a>
+close on right-click of whitespace
+change a default ws filter to only filter out Delivered WSs
+copy ws name on right-click of link
+make a ws name in a sucess message a link
+put a timestamp in the message
+fix contextmenu on text input
+before opening a dialog check if it exists, and if so run auto inspect
+clicking a ws name inspects the first in the list
+dialog should have a unique selector itself so we don't mess with other dialogs
+print a message before server call, like inspecting ws blabla, or searching for workspace blabla
+use a function to print the message, keep a history of 3 messages
+close when click outside
+make it work and test in IE/Edge/Firefox
+ES6 => Babel => ES5 => Bookmarklet
@Fixed in 1.2:
+print placeholder text on empty call
+don't highlight search specs
+clear results before next search
+fix char limit error
+fix hightlight
+print user name instead of "my"
@Fixed in 1.3:
+placeholder text color
+<> in placeholder text
+when searching for exact ws name, shouldn't highlight it
+link click doesn't work if clicked on highlighted part (<b>)
+don't close on whitespace click
@Fixed in 1.4
+change to the layout
+fixed empty call problem
+instruction changes
@fixed in 1.5
+remove "workspace" from messages so inspecting latest workspace wording make sence
+search results shouldn't be empty - when inspecting should spool a ws name, while search/inspect in progress put ...
+more instructions
*/
(() => {
if ("undefined" === typeof SiebelApp) {
alert("It works only in Siebel OUI session!");
return;
}
// snippet id
const func = "SiebelInspectWS";
// selector preffix
const id = "#" + func;
const cls = "." + func;
// max number of output records
const iLimit = 10;
// history of recent calls
let aHistory = JSON.parse(window.localStorage[func] || "[]");
// messages
let aMsg = [];
// double click of bookmarklet
if ($("." + func).length) {
$("." + func).find(id + "IBtn").click();
return;
}
const placeholder = `${SiebelApp.S_App.GetUserName()||"my"} recent undelivered workspace`;
const help = `<i><p>Welcome to Inspect Workspace UI</p>
Text field accepts several formats:<br>
<ul><li> - an exact workspace name: vbabkin_20200924_d419_1</li>
<li> - a search pattern of workspace name: *d419*</li>
<li> - an exact search spec for Repository Workspace BC: [Parent Name] = "Release 21" AND [Created By] = LoginId()</li>
<li> - leave it empty to search / inspect most recent undelivered workspaces created by active user</li></ul>
<p>Hit Enter to search for 10 most recent workspaces matching the provided name/pattern/spec and then click one of the workspaces in the list to inspect it.</p>
<p>Hit Ctrl+Enter to inspect the most recent workspaces matching the provided name/pattern/spec.</p>
<p>If you want to inspect/re-inspect your recent undelivered workspace, just hit Ctrl+Enter upon opening a dialog or double click a bookmark link.</p>
<p>Right click on workspace name to copy it.</p>
<p>Click anywhere outside of the dialog to close it.</p>
<p>Check out <a href="http://xapuk.com/index.php?topic=125" target="_blank">http://xapuk.com/</a> for details.</p></i>`;
const html = `<div title="Inspect workspace">
<span id = "${func}Help" style = "display:none">${help}</span>
<input placeholder = "<${placeholder}>" type="text" id = "${func}" list="${func}History" autocomplete="off">
<ul id="${func}List">Provide a search criteria above and run [Search] to see a list of available workspaces<br>and/or run [Inspect] directly to inspect the most recent workspace matching the criteria</ul>
<p id = "${func}Msg"></p>
<datalist id = "${func}History"></datalist>
<style>
.${func} input {
width: 100%!Important;
margin-bottom: 10px;
}
#${func}::placeholder {
color: lightslategrey;
}
#${func}List {
margin-left: 15px;
}
#${func}Help i {
font-size: 0.9rem;
}
.${func} li {
list-style-type: disc;
margin-left: 30px;
}
#${func}Msg {
border-top: 1px solid lightslategrey;
padding-top: 5px;
}
</style>
</div>`;
const $d = $(html).dialog({
modal: true,
width: 640,
classes: {
"ui-dialog": func
},
buttons: [{
text: 'Search (Enter)',
click: () => Run(false)
}, {
id: func + "IBtn",
text: 'Inspect (Ctrl+Enter)',
click: () => Run(true)
}, {
text: 'Help',
click: () => $d.find(id + "Help").toggle()
}, {
text: 'Close (Esc)',
click: () => $d.dialog('close')
}],
open: function () {
const $this = $(this);
// autofocus
$this.find('#' + func).focus();
// key bindings
$this.parent(".ui-dialog").contextmenu(function (e) {
const scope = e.target;
if (scope.nodeName === "A") {
// copy value on right-click of link
e.stopPropagation();
e.preventDefault();
// replace link with an input
$(scope).hide().after(`<input id='${func}Copy'>`);
$d.find(id + "Copy").val($(scope).text()).select();
// attempt to copy value
if (document.execCommand("copy", false, null)) {
// if copied, display a message for a second
$d.find(id + "Copy").attr("disabled", "disabled").css("color", "red").val("Copied!");
setTimeout(() => {
$d.find(id + "Copy").remove();
$(scope).show();
}, 700);
} else {
// if failed to copy, keep input element until blur, so it can be copied manually
$d.find(id + "Copy").blur(() => {
$(this).remove();
$d.find("a").show();
});
}
}
}).click((e) => {
var a = $(e.target).closest("a");
if (a.length && a.closest(id + "List").length) {
Run(true, a.text());
}
}).find(id).keydown((event) => {
if (event.keyCode === 13) {
Run(event.ctrlKey);
}
});
// close dialog when click outside
$('.ui-widget-overlay').click(() => $d.dialog("close"));
// render history
aHistory.forEach((i) => $this.find(id + "History").append(`<option>${i}</option>`));
},
close: () => {
$d.dialog('destroy').remove();
}
});
function Run(bInspect, inpname) {
const name = inpname ? inpname : $('#' + func).val();
// don't accept specs shorter then 3 chars
if (name && name.replace(/\*/gm, "").length < 3) {
printMsg(`Value should be longer then 3 characters! ${name}`);
return;
}
//clean up results before search
if (!bInspect) {
$d.find(id + "List").empty();
}
// save last query
if (name) {
if (aHistory.indexOf(name) > -1) {
aHistory.splice(name, 1);
}
aHistory.unshift(name);
// limit history stack volume to 5
if (aHistory.length > 5) {
aHistory.pop();
}
window.localStorage[func] = JSON.stringify(aHistory);
}
// invoke BS
const service = SiebelApp.S_App.GetService("FWK Runtime");
let ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Name", name);
ps.SetProperty("Inspect", bInspect ? "Y" : "N");
ps.SetProperty("Limit", iLimit);
let config = {
async: true,
scope: this,
mask: true,
cb: function (methodName, inputSet, outputSet) {
if (outputSet.GetProperty("Status") == "Error") {
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
} else {
let psRS = outputSet.GetChildByType("ResultSet");
if (psRS) {
sRes = psRS.GetProperty("Result");
sWorkspaces = psRS.GetProperty("Workspaces");
if (!sRes) {
if (bInspect) {
sRes = `Workspace <b><a href='#'>${sWorkspaces||"?"}</a></b> inspected successfully!`;
} else {
if (sWorkspaces) {
// print a list of workspaces
$d.find(id + "List").empty();
let aWorkspaces = sWorkspaces.split(",");
aWorkspaces.forEach((w) => $d.find(id + "List").append(`<li><a href='#'>${highlightText(name, w)}</a></li>`));
if (aWorkspaces.length == iLimit) {
$d.find(id + "List").append(`<p><i>${iLimit} most recent workspaces are shown.</i></p>`);
}
}
}
} else if (sRes.indexOf("No workspace found") > -1) {
$d.find(id + "List").html(`Workspace not found, please provide a valid search criteria and run [Search] again...`);
}
}
}
if (sRes) {
printMsg(sRes);
}
}
};
printMsg(`${bInspect?'Inspecting':'Searching for'} ${name||placeholder}`);
service.InvokeMethod("InspectWS", ps, config);
}
function highlightText(pattern, value) {
if (pattern && value && !pattern.match(/\[.*\]/gm) && pattern.replace(/\*/gm, "").length < value.length) {
const patterns = pattern.split("*");
let i, lastIndex = -1;
value = patterns.reduce((res, p) => {
let i = res.indexOf(p, lastIndex);
if (p && i > -1) {
res = `${res.substr(0, i)}<b>${p}</b>${res.substr(i + p.length)}`;
lastIndex = i;
}
return res;
}, value);
}
return value;
}
function printMsg(txt) {
txt = (new Date).toLocaleTimeString() + ' >> ' + txt;
// limit a message stack to 3 items
aMsg.push(txt);
if (aMsg.length > 3) {
aMsg.shift();
}
$d.find(id + "Msg").html(aMsg.join("<br>"));
}
})();
javascript:(function(){function e(e,a){var c=a?a:$("#"+n).val();if(c&&c.replace(/\*/gm,"").length<3)return void i("Value should be longer then 3 characters! "+c);e||d.find(r+"List").empty(),c&&(s.indexOf(c)>-1&&s.splice(c,1),s.unshift(c),s.length>5&&s.pop(),window.localStorage[n]=JSON.stringify(s));var l=SiebelApp.S_App.GetService("FWK Runtime"),u=SiebelApp.S_App.NewPropertySet();u.SetProperty("Name",c),u.SetProperty("Inspect",e?"Y":"N"),u.SetProperty("Limit",o);var f={async:!0,scope:this,mask:!0,cb:function(n,s,a){if("Error"==a.GetProperty("Status"))sRes=a.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");else{var p=a.GetChildByType("ResultSet");if(p)if(sRes=p.GetProperty("Result"),sWorkspaces=p.GetProperty("Workspaces"),sRes)sRes.indexOf("No workspace found")>-1&&d.find(r+"List").html("Workspace not found, please provide a valid search criteria and run [Search] again...");else if(e)sRes="Workspace <b><a href='#'>"+(sWorkspaces||"?")+"</a></b> inspected successfully!";else if(sWorkspaces){d.find(r+"List").empty();var l=sWorkspaces.split(",");l.forEach(function(e){return d.find(r+"List").append("<li><a href='#'>"+t(c,e)+"</a></li>")}),l.length==o&&d.find(r+"List").append("<p><i>"+o+" most recent workspaces are shown.</i></p>")}}sRes&&i(sRes)}};i((e?"Inspecting":"Searching for")+" "+(c||p)),l.InvokeMethod("InspectWS",u,f)}function t(e,t){return e&&t&&!e.match(/\[.*\]/gm)&&e.replace(/\*/gm,"").length<t.length&&function(){var i=e.split("*"),n=-1;t=i.reduce(function(e,t){var i=e.indexOf(t,n);return t&&i>-1&&(e=e.substr(0,i)+"<b>"+t+"</b>"+e.substr(i+t.length),n=i),e},t)}(),t}function i(e){e=(new Date).toLocaleTimeString()+" >> "+e,a.push(e),a.length>3&&a.shift(),d.find(r+"Msg").html(a.join("<br>"))}if("undefined"==typeof SiebelApp)return void alert("It works only in Siebel OUI session!");var n="SiebelInspectWS",r="#"+n,o=10,s=JSON.parse(window.localStorage[n]||"[]"),a=[];if($("."+n).length)return void $("."+n).find(r+"IBtn").click();var p=(SiebelApp.S_App.GetUserName()||"my")+" recent undelivered workspace",c='<i><p>Welcome to Inspect Workspace UI</p>Text field accepts several formats:<br><ul><li> - an exact workspace name: vbabkin_20200924_d419_1</li><li> - a search pattern of workspace name: *d419*</li><li> - an exact search spec for Repository Workspace BC: [Parent Name] = "Release 21" AND [Created By] = LoginId()</li><li> - leave it empty to search / inspect most recent undelivered workspaces created by active user</li></ul><p>Hit Enter to search for 10 most recent workspaces matching the provided name/pattern/spec and then click one of the workspaces in the list to inspect it.</p><p>Hit Ctrl+Enter to inspect the most recent workspaces matching the provided name/pattern/spec.</p><p>If you want to inspect/re-inspect your recent undelivered workspace, just hit Ctrl+Enter upon opening a dialog or double click a bookmark link.</p><p>Right click on workspace name to copy it.</p><p>Click anywhere outside of the dialog to close it.</p><p>Check out <a href="http://xapuk.com/index.php?topic=125" target="_blank">http://xapuk.com/</a> for details.</p></i>',l='<div title="Inspect workspace"><span id = "'+n+'Help" style = "display:none">'+c+'</span><input placeholder = "<'+p+'>" type="text" id = "'+n+'" list="'+n+'History" autocomplete="off"><ul id="'+n+'List">Provide a search criteria above and run [Search] to see a list of available workspaces<br>and/or run [Inspect] directly to inspect the most recent workspace matching the criteria</ul><p id = "'+n+'Msg"></p><datalist id = "'+n+'History"></datalist><style>.'+n+" input {width: 100%!Important;margin-bottom: 10px;}#"+n+"::placeholder {color: lightslategrey;}#"+n+"List {margin-left: 15px;}#"+n+"Help i {font-size: 0.9rem;}."+n+" li {list-style-type: disc;margin-left: 30px;}#"+n+"Msg {border-top: 1px solid lightslategrey;padding-top: 5px;}</style></div>",d=$(l).dialog({modal:!0,width:640,classes:{"ui-dialog":n},buttons:[{text:"Search (Enter)",click:function(){return e(!1)}},{id:n+"IBtn",text:"Inspect (Ctrl+Enter)",click:function(){return e(!0)}},{text:"Help",click:function(){return d.find(r+"Help").toggle()}},{text:"Close (Esc)",click:function(){return d.dialog("close")}}],open:function(){var t=$(this);t.find("#"+n).focus(),t.parent(".ui-dialog").contextmenu(function(e){var t=this,i=e.target;"A"===i.nodeName&&(e.stopPropagation(),e.preventDefault(),$(i).hide().after("<input id='"+n+"Copy'>"),d.find(r+"Copy").val($(i).text()).select(),document.execCommand("copy",!1,null)?(d.find(r+"Copy").attr("disabled","disabled").css("color","red").val("Copied!"),setTimeout(function(){d.find(r+"Copy").remove(),$(i).show()},700)):d.find(r+"Copy").blur(function(){$(t).remove(),d.find("a").show()}))}).click(function(t){var i=$(t.target).closest("a");i.length&&i.closest(r+"List").length&&e(!0,i.text())}).find(r).keydown(function(t){13===t.keyCode&&e(t.ctrlKey)}),$(".ui-widget-overlay").click(function(){return d.dialog("close")}),s.forEach(function(e){return t.find(r+"History").append("<option>"+e+"</option>")})},close:function(){d.dialog("destroy").remove()}})})();
Thanks to Manan for his contribution!
You never know when you might need a list of NamedSearches Siebel uses internally.
So, I thought to put it here along with some info where I've found it and some tips on how I usually use it. As you might've already guessed the main use of the list is to check you are not using these names with SetNamedSearch method in the production environment.
Name | Description |
---|---|
System Search | BusComp search specification |
Applet Search Spec Named Search | Applet search specification |
VisibilitySearch | View mode |
Link Search Spec Named Search | Link search specification |
PickListSearch | PickList search specification |
Drill Down By Id Named Search | After you drilled down into a view |
Task UI Search Spec Named Search | When using a [Task Step Context] tab in the Task UI steps |
Content Targeting Named Search | Personalization rules |
Sort Search Optimization | Sometimes sort spec performance can be optimised with search spec |
Private Filter Search | From BusComp User Prop = Private Activity Search Spec |
Active Field Flag Search | If you are using active flag property on BusComp |
MVG Type Field Named Search | When using Type field and value filter on MVG |
Bookmark Id Named Search | When you get to the view through a bookmark |
Auxiliary Id Named Search | ? |
Snapshot | ? |
Override Filter Search | ? |
Link Spec Substitute | ? |
This comes handy when using eScript playground. For example, this is how you test an applet search spec without a compilation:
var bo = TheApplication().ActiveBusObject();
var bc = bo.GetBusComp("Account");
bc.SetNamedSearch("Applet Search Spec Named Search", '[Updated] > Today()-10');
bc.ExecuteQuery(ForwardBackward);
And below eScript snippet tells you all filters applied to an active BusComp if you don't feel like reading logs:
var bo = TheApplication().ActiveBusObject();
var bc = bo.GetBusComp("Account");
var aSpecs = [
"System Search",
"Applet Search Spec Named Search",
"VisibilitySearch",
"Link Search Spec Named Search",
"PickListSearch",
"Drill Down By Id Named Search",
"Task UI Search Spec Named Search",
"Content Targeting Named Search",
"Sort Search Optimization",
"Active Field Flag Search",
"MVG Type Field Named Search",
"Auxiliary Id Named Search",
"Snapshot",
"Bookmark Id Named Search"
];
if (bc.GetSearchExpr()) {
log("User Search Spec", bc.GetSearchExpr());
}
for (var i in aSpecs) {
var name = aSpecs[i];
var spec = bc.GetNamedSearch(name);
if (spec) {
log(name, spec);
}
}
Stay tuned and take care, folks!
In this one, I want to share a script that helps me to deliver Siebel workspaces. This time I'll start with the source code and then I'll tell you how to use it.
Const sODBC = "SBL DEV Server DSN" ' ODBC32 name should be using OraClient. When using "Siebel Oracle" driver there'll be an annoying copyright message.
Const sUsername = "approver" ' DB user name
Const sPassword = "pass123" ' DB password
Const sTools = """C:\Siebel\Tools\BIN\siebdev.exe"" /c ""C:\Siebel\Tools\bin\enu\tools_dev.cfg"" /d ServerDataSrc" 'Siebel tools path along with some params, be careful with quote escaping
Const sLog = "C:\Siebel\Tools\LOG" ' Logs directory if you want it to be opened delivery failed
Const sDefWS = "dev_vbabkin_*" ' Default workspace name prefix to ease typing
Const sWSComment = "Delivered by VB" ' Delivery comment - usually your name/username if using a common system user for delivery
<job id = "WSDelivery">
<script language="VBScript" src="Parameters.vbs"/>
<script language="VBScript">
'@desc Siebel workspaces delivery "automation"
'@version 1.7 (20190914)
'@author VB (http://xapuk.com)
'@example For the sake of using x32 ODBC drivers, should be ran through x32 bit version of VBS engine ...windows\sysWOW64\cscript
Dim sSQL1, sSQL2, sSQL3
Dim sConnection, dbConnection, snpData
Dim objShell
Dim sWS
Dim sId
Dim sMsg
Sub Deliver
Set objShell = WScript.CreateObject("WScript.Shell")
' create objects first, if failed , there is no point in moving forward
Set snpData = CreateObject("ADODB.Recordset")
Set dbConnection = CreateObject("ADODB.Connection")
Set objShell = WScript.CreateObject("WScript.Shell")
' establish DB connection
sConnection = "Data Source=" & sODBC & "; User ID=" & sUsername & "; Password=" & sPassword & ";"
Err.Clear
On Error Resume Next
dbConnection.ConnectionString = sConnection
dbConnection.Open
' to handle DB connection errors
If Err.Number <> 0 Then
sMsg = Err.Description & chr(10) & chr(10) & sConnection & chr(10) & chr(10) & "Please open Parameters.vbs file to change connection parameters."
MsgBox sMsg, 0, "Siebel workspace delivery"
Else
On Error GoTo 0
' Input WS name
sWS = LCase(InputBox("Please enter a WS name", "Siebel workspace delivery", sDefWS))
If sWS > "" Then
' if WS name has * search with like and only Submitted For Delivery
If InStr(1, sWS, "*", 1) > 0 Then
sSQL2 = "select name, row_id from siebel.S_WORKSPACE where name like '" & Replace(sWS, "*", "%") & "' and status_cd = 'Submitted for Delivery'"
snpData.Open sSQL2, dbConnection
If Not(snpData.EOF) Then
sId = snpData("row_id")
sWS = snpData("name") ' Get a full name of the workspace
sSQL1 = "select row_id, status_cd from siebel.S_WORKSPACE where name = '" & sWS & "'"
snpData.MoveNext() ' Check if there are more than one match
If Not(snpData.EOF) Then
MsgBox "Multiple records found. Please specify the query.", 0, sWS
snpData.Close
Exit Sub
End If
End If
snpData.Close
Else ' otherwise look for exact name match
sSQL1 = "select row_id, status_cd from siebel.S_WORKSPACE where name = '" & sWS & "'"
snpData.Open sSQL1, dbConnection
' verify the WS
If Not(snpData.EOF) Then
sStatus = snpData("status_cd")
sId = snpData("row_id")
snpData.Close
If sStatus <> "Submitted for Delivery" Then
MsgBox "Wrong status (" + sStatus + ")", 0, sWS
Exit Sub
End If
End If
End If
' if WS is found, delivering
If sId > "" Then
' this is a basic SQL with a list of objects in WS. Feel free to enchance it with information of importance
'sSQL3 = "select v.VERSION_NUM, o.OBJ_TYPE, o.OBJ_NAME from siebel.S_REPOSITORY r join siebel.S_WORKSPACE w on r.ROW_ID = w.REPOSITORY_ID join siebel.S_WS_VERSION v on w.ROW_ID = v.WS_ID and r.ROW_ID = v.REPOSITORY_ID join siebel.S_RTC_MOD_OBJ o on o.WS_VER_ID = v.ROW_ID and r.ROW_ID = o.REPOSITORY_ID where r.name = 'Siebel Repository' and w.row_id = '" & sId & "' order by 1,2,3;"
' I'm only curious about scripts :)
sSQL3 = "select v.VERSION_NUM, o.OBJ_TYPE, o.OBJ_NAME, GREATEST(nvl(max(s1.name),'-'), nvl(max(s2.name),'-'), nvl(max(s3.name),'-')) SCRIPT from siebel.S_REPOSITORY r join siebel.S_WORKSPACE w on r.ROW_ID = w.REPOSITORY_ID join siebel.S_WS_VERSION v on w.ROW_ID = v.WS_ID and r.ROW_ID = v.REPOSITORY_ID join siebel.S_RTC_MOD_OBJ o on o.WS_VER_ID = v.ROW_ID and r.ROW_ID = o.REPOSITORY_ID left join siebel.S_APPLICATION o1 on o1.name = o.OBJ_NAME and r.row_id = o1.REPOSITORY_ID and o.OBJ_TYPE = 'Application' left join siebel.S_APPL_SCRIPT s1 on s1.APPLICATION_ID = o1.ROW_ID and s1.WS_ID = w.row_id left join siebel.S_APPLET o2 on o2.name = o.OBJ_NAME and r.row_id = o2.REPOSITORY_ID and o.OBJ_TYPE = 'Applet' left join siebel.S_APPL_WEBSCRPT s2 on s2.APPLET_ID = o2.row_id and s2.WS_ID = w.row_id left join siebel.S_BUSCOMP o3 on o3.name = o.OBJ_NAME and r.row_id = o3.REPOSITORY_ID and o.OBJ_TYPE = 'Business Component' left join siebel.S_BUSCOMP_SCRIPT s3 on s3.BUSCOMP_ID = o3.row_id and s3.WS_ID = w.row_id where r.name = 'Siebel Repository' and w.row_id = '" & sId & "' group by v.VERSION_NUM, o.OBJ_TYPE, o.OBJ_NAME order by 1, 2, 3;"
snpData.Open sSQL3, dbConnection
'fetching workspace content
sMsg = "Object modified in the WS:"
Do While Not(snpData.EOF)
sMsg = sMsg & chr(10) & snpData("VERSION_NUM") & " / " & snpData("OBJ_TYPE") & " = " & snpData("OBJ_NAME")
If snpData("SCRIPT") > "-" Then ' special alarm if there is a new applet, application or BS script
sMsg = sMsg & " / <<<<<[SCRIPT]>>>>>"
End If
snpData.MoveNext
Loop
snpData.Close
' Are you sure?
sMsg = sMsg + chr(10)+ chr(10) + "Are you sure you want to deliver the WS?"
If MsgBox(sMsg, 1, sWS) = 1 Then
' DELIVER
sMsg = sTools + " /u " & sUsername & " /p " & sPassword & " /Deliverworkspace " & sWS & " """ & sWSComment & ""
objShell.Run sMsg, 0, true
' check if delivery was successful
snpData.Open sSQL1, dbConnection
If Not(snpData.EOF) Then
sStatus = snpData("status_cd")
If sStatus = "Delivered" Then
MsgBox "WS is delivered!", 0, sWS
Else
' If failed open log folder
If sLog > "" Then
sMsg = """C:\Windows\explorer.exe"" """ & sLog & """"
objShell.Run sMsg, 8, true
End If
MsgBox "Not delivered with status = " & sStatus & chr(10) & "Please, check a log file.", 0, sWS
End If
Else
MsgBox "WS not found", 0, sWS
End If
End If
Else
MsgBox "WS not found", 0, sWS
End If
End If
dbConnection.Close
End If
Set snpData = Nothing
Set dbConnection = Nothing
Set objShell = Nothing
End Sub
</script>
<script language="VBScript">
Call Deliver
</script>
</job>
ECHO OFF
REM The only purpose of that shell script is to run VBScript in 32bit mode, same as ODBC driver
REM If you are using ODBC64 data source, simply run the .vbs file itself
time /t
C:\windows\sysWOW64\cscript WSDelivery.wsf
time /t
timeout /t 5
Download all files and put them into the same folder. Open "Parameters.vbs" and change each parameter according to your environment.
If you are using 32-bit ODBC driver on 64-bit OS run the script through "WSDelivery.bat", otherwise just run "WSDelivery.wsf" directly.
Step 1.
Here you put the name of the workspace you want to be delivered.
There are 3 scenarios I've tuned the script for:
If the script finds more then one match or if the WS is in the wrong status, it will tell you that.
Step 2.
At this stage, you will be able to check the details of the workspace.
First, make sure the name in the title is correct.
Then, go through the list of changed objects. If objects require a detailed look, you can always Cancel.
Step 3.
Script will run Siebel Tools in WSDelivery mode and will wait for it to complete.
Once delivery is completed it will tell you if it was success or failure.
I've tried my best to make the script straight forward. Feel free to adjust it to your needs.
I hope you'll find the script or at least the idea useful. In my case, it saves me a couple of weeks annually.
If "Siebel Workflow Process" (WF) is your primary tool for business process automation as they are for me, it might be a useful topic for you.
Familiar with the error "The value entered in field Process Business Object of buscomp Workflow Process Deployment does not match any value in the bounded pick list FOW SRF Business Object." (SBL-DAT-00225) when activating a custom BO-based workflow process in Siebel Tools? The usual way to solve it is to activate a process through [Administration - Business Process] screen. It is still annoying, as you will lose your current context, and extremely annoying when you don't have BP screen or access to it in the test application. In this topic, I offer you another, effortless way to activate a WF process. On the same view/session where you are testing it, and with just a couple clicks.
Ok, let's get down to implementation.
1. Publishing the service.
As you might have guessed, we will be using the "Workflow Admin Service". Out of many ways to run the BS, I'll cover OUI scripts.
So, let's make the BS accessible from a browser script. Here, I recommend using a service runner - straight forward concept when you publish one to run any other service in the system. If you are not comfortable with granting access to all services in production, use a client-side business service for service runner. So, it wouldn't be automatically migrated to higher environments along with a repository.
Here is how your service runner method can look like:
function InvokeServiceMethod(Inputs, Outputs) {
var bs;
var sBS = Inputs.GetProperty("Service");
Inputs.RemoveProperty("Service");
var sMethod = Inputs.GetProperty("Method");
Inputs.RemoveProperty("Method");
try {
bs = TheApplication().GetService(sBS);
bs.InvokeMethod(sMethod, Inputs, Outputs);
} catch(e) {
throw e;
} finally {
bs = null;
}
}
Here is a full version of my client-side Business Service - FWK Runtime. Simply, download and import it on Administration - Business Service screen.
And finally, granting access to run a service runner from browser scripts:
2. JS code with UI and service call.
We will need a UI to enter a WF process name and a button to run the BS. We will be using JQuery dialog with just a couple of elements. It is going to look like this:
Source code and installation links are below.
/*
@desc bookmarklet UI to activate Siebel Workflow Process
@author VB (xapuk.com)
@version 1
*/
if ("undefined" == typeof SiebelApp) {
alert("It works only in Siebel OUI session!");
} else {
// snippet id
var id = "SiebelWFDeploy";
// localStorage to store the history
var aHist = window.localStorage[id]?JSON.parse(window.localStorage[id]):[];
// just in case (experimental)
$("#" + id).parent().remove();
// constructing dialog content
var s = '<div title="Activate workflow">';
s += '<input id = "' + id + '" type="text" list="' + id + 'List" style="width:100%" value="' + (aHist.length?aHist[0]:"") + '">'; // most recent
s += '<label class="pt-3">Recent workflows:</label><ul>';
for (var i =0; i < aHist.length && i < 5; i++){ // five recent values as links
s += '<li><a href="#">' + aHist[i] + '</a></li>';
}
s += '</ul></div>';
// open dialog
var $d = $(s).dialog({
modal: true,
width: 640,
open: function() {
$('#' + id).autocomplete({source: aHist});
$('#' + id).focus().select(); // autofocus
},
close: function() {
$(this).dialog('destroy').remove();
},
buttons: [{
text: 'Activate (Enter/Click)',
click: function(){
go($d.find('#' + id).val());
}
}, {
text: 'Close (Esc)',
click: function() {
$(this).dialog('close');
}
}]
});
// key bindings
$d.keyup(function(event) {
// enter
if (event.keyCode === 13) {
go($d.find('#' + id).val());
}
});
// running function on anchor link click
$d.find("a").click(function(event) {
go($(this).html());
});
}
// Activate
function go(name) {
if (name){
// moving recent view to the top
if (aHist.indexOf(name) > -1){
aHist.splice(aHist.indexOf(name),1);
}
aHist.unshift(name);
window.localStorage[id] = JSON.stringify(aHist);
$d.dialog('close');
// invoke BS
var service = SiebelApp.S_App.GetService("FWK Runtime");
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Service", "Workflow Admin Service");
ps.SetProperty("Method", "Activate");
ps.SetProperty("FlowSearchSpec", "[Process Name] = '" + name + "'");
var outputSet = service.InvokeMethod("InvokeServiceMethod", ps);
if (console){
console.log(outputSet);
}
if (outputSet.GetProperty("Status") == "Error"){
alert(outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"));
}else{
if (outputSet.GetChildByType("ResultSet").GetProperty("NumFlowActivated") === "0"){
alert("Process definition [" + name + "] not found");
}else{
alert("Done!");
}
}
}
}
javascript:function go(e){if(e){aHist.indexOf(e)>-1&&aHist.splice(aHist.indexOf(e),1),aHist.unshift(e),window.localStorage[id]=JSON.stringify(aHist),$d.dialog("close");var i=SiebelApp.S_App.GetService("FWK Runtime"),t=SiebelApp.S_App.NewPropertySet();t.SetProperty("Service","Workflow Admin Service"),t.SetProperty("Method","Activate"),t.SetProperty("FlowSearchSpec","[Process Name] = '"+e+"'");var o=i.InvokeMethod("InvokeServiceMethod",t);console&&console.log(o),alert("Error"==o.GetProperty("Status")?o.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):"0"===o.GetChildByType("ResultSet").GetProperty("NumFlowActivated")?"Process definition ["+e+"] not found":"Done!")}}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var id="SiebelWFDeploy",aHist=window.localStorage[id]?JSON.parse(window.localStorage[id]):[];$("#"+id).parent().remove();var s='<div title="Activate workflow">';s+='<input id = "'+id+'" type="text" list="'+id+'List" style="width:100%" value="'+(aHist.length?aHist[0]:"")+'">',s+='<label class="pt-3">Recent workflows:</label><ul>';for(var i=0;i<aHist.length&&5>i;i++)s+='<li><a href="#">'+aHist[i]+"</a></li>";s+="</ul></div>";var $d=$(s).dialog({modal:!0,width:640,open:function(){$("#"+id).autocomplete({source:aHist}),$("#"+id).focus().select()},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Activate (Enter/Click)",click:function(){go($d.find("#"+id).val())}},{text:"Close (Esc)",click:function(){$(this).dialog("close")}}]});$d.keyup(function(e){13===e.keyCode&&go($d.find("#"+id).val())}),$d.find("a").click(function(e){go($(this).html())})}
3. Disable cache.
Workflow Process cache is controlled by VerCheckTime (Workflow Version Checking Interval) server parameter .
If the parameter is on, a new WF version wouldn't take in action in existing session right away after activating it. It makes sense to have it on production, but I recommend to turn it off on DEV environment.
Also, turn it off on your dedicated client in .cfg file:
...
[Workflow]
VerCheckTime = -1
Bonus
Same story with TBUI. Simply run [Task Activation Automation] service from JavaScript. Service runner is paying off already, by the way.
javascript:function go(e){if(e){aHist.indexOf(e)>-1&&aHist.splice(aHist.indexOf(e),1),aHist.unshift(e),window.localStorage[id]=JSON.stringify(aHist),$d.dialog("close");var t=SiebelApp.S_App.GetService("FWK Runtime"),i=SiebelApp.S_App.NewPropertySet();i.SetProperty("Service","Task Activation Automation"),i.SetProperty("Method","Activate Task"),i.SetProperty("TaskName",e);var o=t.InvokeMethod("InvokeServiceMethod",i);console&&console.log(o),alert("Error"==o.GetProperty("Status")?o.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):"0"===o.GetChildByType("ResultSet").GetProperty("NumFlowActivated")?"Process definition ["+e+"] not found":"Done!")}}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var id="SiebelTaskDeploy",aHist=window.localStorage[id]?JSON.parse(window.localStorage[id]):[];$("#"+id).parent().remove();var s='<div title="Activate task">';s+='<input id = "'+id+'" type="text" list="'+id+'List" style="width:100%" value="'+(aHist.length?aHist[0]:"")+'">',s+='<label class="pt-3">Recent tasks:</label><ul>';for(var i=0;i<aHist.length&&5>i;i++)s+='<li><a href="#">'+aHist[i]+"</a></li>";s+="</ul></div>";var $d=$(s).dialog({modal:!0,width:640,open:function(){$("#"+id).autocomplete({source:aHist}),$("#"+id).focus().select()},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Activate (Enter/Click)",click:function(){go($d.find("#"+id).val())}},{text:"Close (Esc)",click:function(){$(this).dialog("close")}}]});$d.keyup(function(e){13===e.keyCode&&go($d.find("#"+id).val())}),$d.find("a").click(function(e){go($(this).html())})}
This is one of my favourite repository SQL statements. It connects all layers of Siebel application into one flat structure:
Application | Screen/View |
Applet | Control/List column |
Business Component | Field |
Table | Column |
Here are just a few of the questions it can answer:
I'll share the most full version of the SQL, but feel free to tune it up to your purpose.
SELECT distinct
t.NAME "TABLE", col.NAME "COLUMN",
b.NAME "BUSCOMP", f.NAME "FIELD",
nvl(ci.CAPTION, li.DISPLAY_NAME) "CAPTION", nvl(c.NAME, lc.NAME) "CONTROL", a.NAME "APPLET",
s.NAME "SCREEN", v.NAME "VIEW", ap.NAME "APPLICATION"
FROM siebel.S_REPOSITORY r
-- workspace
join siebel.S_WORKSPACE ws on ws.REPOSITORY_ID = r.ROW_ID
-- application
join siebel.S_APPLICATION ap on ap.REPOSITORY_ID = r.ROW_ID and ws.ROW_ID = ap.WS_ID
join siebel.S_PAGE_TAB aps on aps.APPLICATION_ID = ap.WS_SRC_ID and aps.INACTIVE_FLG = 'N' and ws.ROW_ID = aps.WS_ID
-- screen
join siebel.S_SCREEN s on s.name = aps.SCREEN_NAME and s.REPOSITORY_ID = r.ROW_ID AND s.INACTIVE_FLG = 'N' and ws.ROW_ID = s.WS_ID
join siebel.S_SCREEN_VIEW sv on sv.screen_id = s.WS_SRC_ID and sv.INACTIVE_FLG = 'N' and ws.ROW_ID = sv.WS_ID
-- view
join siebel.S_VIEW v on v.REPOSITORY_ID = r.ROW_ID and sv.VIEW_NAME = v.name and ws.ROW_ID = v.WS_ID
join siebel.S_VIEW_WEB_TMPL vt on vt.VIEW_ID = v.WS_SRC_ID and vt.INACTIVE_FLG = 'N' and ws.ROW_ID = vt.WS_ID
join siebel.S_VIEW_WTMPL_IT vti on vti.VIEW_WEB_TMPL_ID = vt.WS_SRC_ID and vti.INACTIVE_FLG = 'N' and ws.ROW_ID = vti.WS_ID
-- applet (only form, list applets)
join siebel.S_APPLET a on a.REPOSITORY_ID = r.ROW_ID and a.name = vti.APPLET_NAME and ws.ROW_ID = a.WS_ID
join siebel.S_APPL_WEB_TMPL w on w.applet_id = a.WS_SRC_ID and w.TYPE = vti.APPLET_MODE_CD and w.INACTIVE_FLG ='N' and ws.ROW_ID = w.WS_ID
join siebel.S_APPL_WTMPL_IT wi on wi.APPL_WEB_TMPL_ID = w.WS_SRC_ID and wi.INACTIVE_FLG = 'N' and ws.ROW_ID = wi.WS_ID
-- control
left join siebel.S_CONTROL c on c.APPLET_ID = a.WS_SRC_ID and wi.CTRL_NAME = c.name and c.INACTIVE_FLG = 'N' and ws.ROW_ID = c.WS_ID
left join siebel.S_LIST l on l.APPLET_ID = a.WS_SRC_ID and ws.ROW_ID = l.WS_ID
left join siebel.S_LIST_COLUMN lc on lc.LIST_ID = l.WS_SRC_ID and wi.CTRL_NAME = lc.name and ws.ROW_ID = lc.WS_ID
-- control caption (only overrides!)
left join siebel.S_CONTROL_INTL ci on ci.CONTROL_ID = c.WS_SRC_ID and ws.ROW_ID = ci.WS_ID
left join siebel.S_LIST_COL_INTL li on li.LIST_COLUMN_ID = lc.WS_SRC_ID and ws.ROW_ID = li.WS_ID
-- buscomp
join SIEBEL.S_BUSCOMP b on b.name = a.BUSCOMP_NAME and b.REPOSITORY_ID = r.ROW_ID and ws.ROW_ID = b.WS_ID
-- fields exposed either through control or list column
join siebel.S_FIELD f on f.BUSCOMP_ID = b.WS_SRC_ID
and (lc.FIELD_NAME = f.name or c.FIELD_NAME = f.name) and ws.ROW_ID = f.WS_ID
-- join
left join siebel.S_JOIN j on j.name = f.JOIN_NAME and j.BUSCOMP_ID = f.BUSCOMP_ID and ws.ROW_ID = j.WS_ID
-- table
join siebel.S_TABLE t on t.REPOSITORY_ID = r.ROW_ID
and (t.name = b.TABLE_NAME and f.join_name is null -- base table
or t.name = j.DEST_TBL_NAME and j.row_id is not null -- explicit joins
or t.name = f.JOIN_NAME and j.row_id is null) -- implicit joins
-- column
join siebel.S_COLUMN col on col.TBL_ID = t.ROW_ID and f.COL_NAME = col.name and ws.ROW_ID = c.WS_ID
WHERE r.name = 'Siebel Repository'
and ws.name = 'MAIN'
and ap.name LIKE 'Siebel Financial Services'
ORDER BY 1, 2, 3;
Keep in mind that the SQL:
Here is also a version for old Siebel repositories (non-workspace): Snippet
SELECT distinct
t.NAME "TABLE", col.NAME "COLUMN",
b.NAME "BUSCOMP", f.NAME "FIELD",
nvl(ci.CAPTION, li.DISPLAY_NAME) "CAPTION", nvl(c.NAME, lc.NAME) "CONTROL", a.NAME "APPLET",
s.NAME "SCREEN", v.NAME "VIEW", ap.NAME "APPLICATION"
FROM siebel.S_REPOSITORY r
-- application
join siebel.S_APPLICATION ap on ap.REPOSITORY_ID = r.ROW_ID
join siebel.S_PAGE_TAB aps on aps.APPLICATION_ID = ap.ROW_ID and aps.INACTIVE_FLG = 'N'
-- screen
join siebel.S_SCREEN s on s.name = aps.SCREEN_NAME and s.REPOSITORY_ID = r.ROW_ID AND s.INACTIVE_FLG = 'N'
join siebel.S_SCREEN_VIEW sv on sv.screen_id = s.ROW_ID and sv.INACTIVE_FLG = 'N'
-- view
join siebel.S_VIEW v on v.REPOSITORY_ID = r.ROW_ID and sv.VIEW_NAME = v.name
join siebel.S_VIEW_WEB_TMPL vt on vt.VIEW_ID = v.ROW_ID and vt.INACTIVE_FLG = 'N'
join siebel.S_VIEW_WTMPL_IT vti on vti.VIEW_WEB_TMPL_ID = vt.ROW_ID and vti.INACTIVE_FLG = 'N'
-- applet (only form, list applets)
join siebel.S_APPLET a on a.REPOSITORY_ID = r.ROW_ID and a.name = vti.APPLET_NAME
join siebel.S_APPL_WEB_TMPL w on w.applet_id = a.ROW_ID and w.TYPE = vti.APPLET_MODE_CD and w.INACTIVE_FLG ='N'
join siebel.S_APPL_WTMPL_IT wi on wi.APPL_WEB_TMPL_ID = w.ROW_ID and wi.INACTIVE_FLG = 'N'
-- control
left join siebel.S_CONTROL c on c.APPLET_ID = a.ROW_ID and wi.CTRL_NAME = c.name and c.INACTIVE_FLG = 'N'
left join siebel.S_LIST l on l.APPLET_ID = a.ROW_ID
left join siebel.S_LIST_COLUMN lc on lc.LIST_ID = l.ROW_ID and wi.CTRL_NAME = lc.name
-- control caption (only overrides!)
left join siebel.S_CONTROL_INTL ci on ci.CONTROL_ID = c.ROW_ID
left join siebel.S_LIST_COL_INTL li on li.LIST_COLUMN_ID = lc.ROW_ID
-- buscomp
join SIEBEL.S_BUSCOMP b on b.name = a.BUSCOMP_NAME and b.REPOSITORY_ID = r.ROW_ID
-- fields exposed either through control or list column
join siebel.S_FIELD f on f.BUSCOMP_ID = b.ROW_ID
and (lc.FIELD_NAME = f.name or c.FIELD_NAME = f.name)
-- join
left join siebel.S_JOIN j on j.name = f.JOIN_NAME and j.BUSCOMP_ID = f.BUSCOMP_ID
-- table
join siebel.S_TABLE t on t.REPOSITORY_ID = r.ROW_ID
and (t.name = b.TABLE_NAME and f.join_name is null -- base table
or t.name = j.DEST_TBL_NAME and j.row_id is not null -- explicit joins
or t.name = f.JOIN_NAME and j.row_id is null) -- implicit joins
-- column
join siebel.S_COLUMN col on col.TBL_ID = t.ROW_ID and f.COL_NAME = col.name
WHERE r.name = 'Siebel Repository'
and ap.name LIKE 'Siebel Financial Services'
ORDER BY 1, 2, 3;
Starting a series "Runtime objects cache". And the first runtime object I'll be covering is Runtime Events (RTE).
Background:
If you are working with RTE you probably know about "Reload Runtime Events" menu command, which allows you to clear RTE cache in your current session. But, what if you are working with multiple sessions? For example, doing your configuration under one user / application and testing it under another, where you don't have access to RTE screen. It could also be a case where you don't want to lose a context. And finally, you might be creating RTE indirectly, from workflows start connector. It all leads you to restarting a Siebel client or losing a context.
Solution:
It turns out "Reload Runtime Events" command is an implicit BC method supported by generic CSSBusComp class. It means we can run that command from any applet and let it propagate to BC. Thanks to Siebel OUI we can run any applet method right from the browser console. As simple as that:
SiebelApp.S_App.GetActiveView().GetActiveApplet().InvokeMethod("ClearCTEventCache");
Here I've made a bookmarklet out of it, so you can run Reload RTE command from browsers bookmark toolbar in any application/view/context without any pre-configuration:
/*
@desc Reloads Runtime Events
@author VB(xapuk.com)
@version 1.1 2019/04/20
*/
if("undefined" === typeof SiebelApp){
alert("Please, log into Siebel application first!");
}else{
var v = SiebelApp.S_App.GetActiveView();
var ap = v.GetActiveApplet();
if ("undefined" === typeof ap) {
ap = v.GetAppletMap()[Object.keys(v.GetAppletMap())[0]];
}
if ("undefined" === typeof ap) {
alert("No applet found!");
} else {
ap.InvokeMethod("ClearCTEventCache", null, {
"cb": function(e) {
alert("Runtime Events were reloaded!");
},
"errcb": function(e) {
console.log("Err", e);
alert(e.toString());
}
});
}
}
javascript:void function(){if("undefined"==typeof SiebelApp)alert("Please, log into Siebel application first!");else{var e=SiebelApp.S_App.GetActiveView(),t=e.GetActiveApplet();"undefined"==typeof t&&(t=e.GetAppletMap()[Object.keys(e.GetAppletMap())[0]]),"undefined"==typeof t?alert("No applet found!"):t.InvokeMethod("ClearCTEventCache",null,{cb:function(e){alert("Runtime Events were reloaded!")},errcb:function(e){console.log("Err",e),alert(e.toString())}})}}();
Bonus:
You can use the same approach to reset List of Values cache:
SiebelApp.S_App.GetActiveView().GetActiveApplet().InvokeMethod("ClearLOVCache");
and View/Responsibility cache:
SiebelApp.S_App.GetActiveView().GetActiveApplet().InvokeMethod("ClearResponsibilityCache");
You know how calc expressions can become a nightmare when growing in size. Nested IIFs, long chains of logical operators, service calls and dozen of brackets. And all these in a single line. It can take minutes before one can understand what is going on. Sometimes I copy the expression into a text editor and manually format it - with tabs and newlines, the same way modern IDE formats the code. Once done, it is much easier to read a logic from the rule.
Inspired by the idea to automate the routine, I did a research and then spent some time on a "grammar playground". Check out the result below or in a new window.
Current formatting logic is to simply print function parameters (when more than 2) on new lines as well as logical expressions enclosed in parenthesis. I plan to make it a bit smarter in future.
Source code
function trav(o, t, f) {
var r = "";
if ("object" === typeof o) {
var p = o.par;
var n = o.not;
if (o.type === "bin") {
r = trav(o.left, t) + " " + o.operator + " " + trav(o.right, t);
} else if (o.type === "log") {
if(p) { // format logical operators eclosed in brackets
tt = t + "\t";
r = "(\n";
r += tt + trav(o.left, tt, true);
r += "\n" + tt + o.operator + " " + trav(o.right, tt, true);
r += "\n" + t + ")";
p = false;
} else {
if(f) {
r = trav(o.left, t, true);
r += "\n" + t + o.operator + " " + trav(o.right, t, true);
} else {
r = trav(o.left, t) + " " + o.operator + " " + trav(o.right, t);
}
}
} else if (o.type === "func") {
var f = o.arguments.length > 2; // split params when more then 2
var s = (f ? "\n" + t : "");
var st = (f ? s + "\t" : "");
r = o.name + "(";
for (var i in o.arguments) {
r += st + trav(o.arguments[i], t + "\t") + (i < o.arguments.length - 1 ? ", " : "");
}
r += s + ")";
} else if (o.type === "field") {
r = "[" + o.field + "]";
} else if (o.type === "param") {
r = "[&" + o.param + "]";
} else if (o.type === "num") {
r = o.value;
} else if (o.type === "str") {
r = o.quote + o.value + o.quote;
}
if (p) {
r = "(" + r + ")";
}
if (n) {
r = "NOT " + r;
}
} else {
r = o.toString();
}
return r;
}
$(document).ready(function(){
$("#ExpParser").on("click", function() {
var s = $("#ExpParserInput").val();
console.log(s);
if (s) {
try {
var o = SiebelQueryLang.parse(s);
s = trav(o.expression, "");
} catch(e) {
s = e.toString();
}
} else {
s = "Please, insert a Siebel expression first";
}
$("#ExpParserOutput").val(s);
}).click();
})
The main outcome of the effort is, of course, to make it a part of Expression Playground. Check out an updated version here.
Another playground for you Guys. This time it is to evaluate a "calc field" expressions. The official name of the syntax is Siebel Query Language and it is used widely through Siebel apps, for example:
A playground is very handy when it comes to prototyping or debugging expressions or simply if you need to get a value of an active field or profile attribute.
Less words more code, let's get down to the implementation.
This time we will be invoking a built-in BusComp method EvalExpr. So let's create another method in runtime business service:
function EvalExpr (Inputs, Outputs) {
var bc:BusComp;
try {
bc = TheApplication().ActiveBusObject().GetBusComp(Inputs.GetProperty("BC"));
Outputs.SetProperty("Result", bc.InvokeMethod("EvalExpr", Inputs.GetProperty("Expr")));
} catch(e) {
Outputs.SetProperty("Result", e.toString());
} finally {
bc = null;
}
}
Here is a full version of my client-side Business Service.
For UI we will be using a bookmarklet with a dialog which contains a drop-down with active BusComps, two text areas for input expression and a result, and a button to run the service.
/*
@desc UI allowing to evaluate expressions (EvalExpr) on active BCs
@author VB(xapuk.com)
@version 1.2 2018/07/23
@requires BS "FWK Runtime" to be published
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
}else{
var func = "SiebelEvalExpr";
$("#" + func).parent().remove();
var a = LoadBCs();
if (a.length === 0){
alert("No BusComps/Records available!");
}else{
var s = '<div title="Runtime calculations">' +
'<label for="' + func + 'List">Business Component:</label>' +
'<select id = "' + func + 'List" style="display:block"></select>' +
'<label for="' + func + '">Expression:</label>' +
'<textarea id = "' + func + '" rows="3"></textarea>' +
'<label for="' + func + 'Out">Results:</label>' +
'<textarea id = "' + func + 'Out" disabled rows="2"></textarea>' +
'<style>select,textarea{width:100%!Important}</style>' +
'</div>';
var d = $(s).dialog({
modal: true,
width: 1024,
heigth: 640,
open: function(){
$('#'+func).focus();
// key bindings
$("#"+func+"Out").parent().keydown(function(event) {
if (event.ctrlKey && event.keyCode === 13) { // ctrl + Enter
EvalExpr();
}
});
// list of BCs
$("#" + func + "List").append("<option>" + a.join("</option><option>") + "</option>");
$("#" + func + "List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName());
// recent expression
$("#" + func).val(JSON.parse(window.localStorage[func]));
//style
$(this).append('<style type="text/css">textarea, select { height:auto; width:100% }</style>');
},
close: function(){
$(this).dialog('destroy').remove();
},
buttons: [
{
text:'Run (Ctrl+Enter)',
click: EvalExpr
},
{
text:'Close (Esc)',
click: function() {
$(this).dialog('destroy').remove();
}
}
]
});
// bind and trigger auto-adjust
$(d).find("#" + func).keyup(function(){
TextAdjust(this, 3);
}).keyup();
}
}
function EvalExpr(){
var sExpr = $('#'+func).val();
var sRes = "";
// save last query
window.localStorage[func] = JSON.stringify(sExpr);
// if there is a selection
var ele = document.getElementById(func);
if(ele.selectionStart !== undefined && ele.selectionStart != ele.selectionEnd){// Normal browsers
sExpr = ele.value.substring(ele.selectionStart, ele.selectionEnd);
}else if(document.selection !== undefined){// IE
ele.focus();
var sel = document.selection.createRange();
sExpr = sel.text;
}
// invoke BS
var service = SiebelApp.S_App.GetService("FWK Runtime");
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", sExpr);
ps.SetProperty("BC", $("#" + func + "List").val());
var outputSet = service.InvokeMethod("EvalExpr", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
console.log(outputSet);
}
TextAdjust($('#'+func + "Out").show().text(sRes)[0]);
}
// auto-ajust textarea height
function TextAdjust(scope, minrows, maxrows) {
maxrows = maxrows>0?maxrows:10;
minrows = minrows>0?minrows:2;
var txt = scope.value;
var cols = scope.cols;
var arraytxt = txt.split('\n');
var rows = arraytxt.length;
if (rows > maxrows) {
scope.rows = maxrows;
} else if (rows < minrows) {
scope.rows = minrows;
} else {
scope.rows = rows;
}
}
// gather the list of active BCs
function LoadBCs(){
var a = [];
for(var i in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){
var bc = SiebelApp.S_App.GetActiveBusObj().GetBCMap()[i];
if (a.indexOf(bc.GetName()) == -1 && bc.GetNumRows() > 0){
a.push(bc.GetName());
}
}
return a;
}
javascript:void function(){function e(){var e=$("#"+i).val(),o="";window.localStorage[i]=JSON.stringify(e);var l=document.getElementById(i);if(void 0!==l.selectionStart&&l.selectionStart!=l.selectionEnd)e=l.value.substring(l.selectionStart,l.selectionEnd);else if(void 0!==document.selection){l.focus();var r=document.selection.createRange();e=r.text}var s=SiebelApp.S_App.GetService("FWK Runtime"),a=SiebelApp.S_App.NewPropertySet();a.SetProperty("Expr",e),a.SetProperty("BC",$("#"+i+"List").val());var n=s.InvokeMethod("EvalExpr",a);"Error"==n.GetProperty("Status")?o=n.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):(o=n.GetChildByType("ResultSet").GetProperty("Result"),console.log(n)),t($("#"+i+"Out").show().text(o)[0])}function t(e,t,o){o=o>0?o:10,t=t>0?t:2;var i=e.value,l=(e.cols,i.split("\n")),r=l.length;r>o?e.rows=o:t>r?e.rows=t:e.rows=r}function o(){var e=[];for(var t in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){var o=SiebelApp.S_App.GetActiveBusObj().GetBCMap()[t];-1==e.indexOf(o.GetName())&&o.GetNumRows()>0&&e.push(o.GetName())}return e}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var i="SiebelEvalExpr";$("#"+i).parent().remove();var l=o();if(0===l.length)alert("No BusComps/Records available!");else{var r='<div title="Runtime calculations"><label for="'+i+'List">Business Component:</label><select id = "'+i+'List" style="display:block"></select><label for="'+i+'">Expression:</label><textarea id = "'+i+'" rows="3"></textarea><label for="'+i+'Out">Results:</label><textarea id = "'+i+'Out" disabled rows="2"></textarea><style>select,textarea{width:100%!Important}</style></div>',s=$(r).dialog({modal:!0,width:1024,heigth:640,open:function(){$("#"+i).focus(),$("#"+i+"Out").parent().keydown(function(t){t.ctrlKey&&13===t.keyCode&&e()}),$("#"+i+"List").append("<option>"+l.join("</option><option>")+"</option>"),$("#"+i+"List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName()),$("#"+i).val(JSON.parse(window.localStorage[i])),$(this).append('<style type="text/css">textarea, select { height:auto; width:100% }</style>')},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Run (Ctrl+Enter)",click:e},{text:"Close (Esc)",click:function(){$(this).dialog("destroy").remove()}}]});$(s).find("#"+i).keyup(function(){t(this,3)}).keyup()}}}();
Here is a new version with a built-in beautifier from another topic:
/*
@desc UI allowing to evaluate expressions (EvalExpr) on active BCs
@author VB(xapuk.com)
@version 1.3 2019/03/10
@requires BS "FWK Runtime" to be compiled and published
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
} else {
var func = "SiebelEvalExpr";
$("#" + func).parent().remove();
var bBeauty = false;
var a = LoadBCs();
if (a.length === 0){
alert("No BusComps/Records available!");
}else{
var s = '<div title="Runtime calculations">' +
'<label for="' + func + 'List">Business Component:</label>' +
'<select id = "' + func + 'List" style="display:block"></select>' +
'<label for="' + func + '">Expression:</label>' +
'<textarea id = "' + func + '" rows="5" nowrap></textarea>' +
'<label for="' + func + 'Out">Results:</label>' +
'<textarea id = "' + func + 'Out" disabled rows="2"></textarea>' +
'<style type="text/css">.ui-dialog textarea, .ui-dialog select {height:auto; width:100%; margin-bottom:10px} .ui-dialog label{margin-top:20px!}</style>'
'</div>';
var d = $(s).dialog({
modal: true,
width: 1024,
heigth: 640,
open: function(){
$('#'+func).focus();
// key bindings
$("#"+func+"Out").parent().keydown(function(event) {
if (event.ctrlKey && event.keyCode === 13) { // ctrl + Enter
EvalExpr();
}
});
// list of BCs
$("#" + func + "List").append("<option>" + a.join("</option><option>") + "</option>");
$("#" + func + "List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName());
// recent expression
$("#" + func).val(JSON.parse(window.localStorage[func]));
},
close: function(){
$(this).dialog('destroy').remove();
},
buttons: [
{
text:'Format/Linarise',
click: Beauty,
id: 'BeautyBtn'
},{
text:'Run (Ctrl+Enter)',
click: EvalExpr
},{
text:'Close (Esc)',
click: function() {
$(this).dialog('destroy').remove();
}
}
]
});
// bind and trigger auto-adjust
$(d).find("#" + func).keyup(function(){
TextAdjust(this, 5);
}).keyup();
// bind a beautifier
$(".ui-dialog #BeautyBtn").hide();
require(["3rdParty/SiebelQueryLang"], function(e){
console.log("Beautifier loaded!");
$(".ui-dialog #BeautyBtn").show();
});
}
}
function Beauty(){
var s = $('#'+func).val();
if (s){
if (bBeauty){
// linarise
s = s.replace(/\n(\t|\s)*/gm,"");
$('#'+func).val(s).attr("wrap", "on");
bBeauty = false;
} else {
// beautify
try {
var o = SiebelQueryLang.parse(s);
s = trav(o.expression, "");
$('#'+func).val(s).attr("wrap", "off");
bBeauty = true;
} catch(e) {
// silence the error
console.log(e);
}
}
TextAdjust($('#'+func)[0]);
}
}
function trav(o, t, f) {
var r = "";
if ("object" === typeof o) {
var p = o.par;
var n = o.not;
if (o.type === "bin") {
r = trav(o.left, t) + " " + o.operator + " " + trav(o.right, t);
} else if (o.type === "log") {
if(p) { // format logical operators eclosed in brackets
tt = t + "\t";
r = "(\n";
r += tt + trav(o.left, tt, true);
r += "\n" + tt + o.operator + " " + trav(o.right, tt, true);
r += "\n" + t + ")";
p = false;
} else {
if(f) {
r = trav(o.left, t, true);
r += "\n" + t + o.operator + " " + trav(o.right, t, true);
} else {
r = trav(o.left, t) + " " + o.operator + " " + trav(o.right, t);
}
}
} else if (o.type === "func") {
var l = o.arguments.length;
var f = l > 2; // split params when more then 2
var s = (f ? "\n" + t : "");
var st = (f ? s + "\t" : "");
r = o.name + "(";
o.arguments.forEach(function(a, i) {
r += st + trav(a, t + "\t") + (i < l - 1 ? ", " : "");
});
r += s + ")";
} else if (o.type === "field") {
r = "[" + o.field + "]";
} else if (o.type === "param") {
r = "[&" + o.param + "]";
} else if (o.type === "num") {
r = o.value;
} else if (o.type === "str") {
r = '"' + o.value +'"';
}
if (p) {
r = "(" + r + ")";
}
if (n) {
r = "NOT " + r;
}
} else {
r = o;
}
return r;
}
function EvalExpr(){
var sExpr = $('#'+func).val();
var sRes = "";
// save last query
window.localStorage[func] = JSON.stringify(sExpr);
// if there is a selection
var ele = document.getElementById(func);
if(ele.selectionStart !== undefined && ele.selectionStart != ele.selectionEnd){// Normal browsers
sExpr = ele.value.substring(ele.selectionStart, ele.selectionEnd);
}else if(document.selection !== undefined){// IE
ele.focus();
var sel = document.selection.createRange();
sExpr = sel.text;
}
// invoke BS
var service = SiebelApp.S_App.GetService("FWK Runtime");
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", sExpr);
ps.SetProperty("BC", $("#" + func + "List").val());
var outputSet = service.InvokeMethod("EvalExpr", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
console.log(outputSet);
}
TextAdjust($('#'+func + "Out").show().text(sRes)[0]);
}
// auto-ajust textarea height
function TextAdjust(scope, minrows, maxrows) {
maxrows = maxrows>0?maxrows:30;
minrows = minrows>0?minrows:5;
var txt = scope.value;
var cols = scope.cols;
var arraytxt = txt.split('\n');
var rows = arraytxt.length;
if (rows > maxrows) {
scope.rows = maxrows;
} else if (rows < minrows) {
scope.rows = minrows;
} else {
scope.rows = rows;
}
}
// put active BC in the list with active applet's bc as selected
function LoadBCs(){
var a = [];
for(var i in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){
var bc = SiebelApp.S_App.GetActiveBusObj().GetBCMap()[i];
if (a.indexOf(bc.GetName()) == -1 && bc.GetNumRows() > 0){
a.push(bc.GetName());
}
}
return a;
}
javascript:void function(){function e(){var e=$("#"+a).val();if(e){if(l)e=e.replace(/\n(\t|\s)*/gm,""),$("#"+a).val(e).attr("wrap","on"),l=!1;else try{var o=SiebelQueryLang.parse(e);e=t(o.expression,""),$("#"+a).val(e).attr("wrap","off"),l=!0}catch(r){console.log(r)}i($("#"+a)[0])}}function t(e,o,i){var r="";if("object"==typeof e){var a=e.par,l=e.not;if("bin"===e.type)r=t(e.left,o)+" "+e.operator+" "+t(e.right,o);else if("log"===e.type)a?(tt=o+" ",r="(\n",r+=tt+t(e.left,tt,!0),r+="\n"+tt+e.operator+" "+t(e.right,tt,!0),r+="\n"+o+")",a=!1):i?(r=t(e.left,o,!0),r+="\n"+o+e.operator+" "+t(e.right,o,!0)):r=t(e.left,o)+" "+e.operator+" "+t(e.right,o);else if("func"===e.type){var n=e.arguments.length,i=n>2,s=i?"\n"+o:"",p=i?s+" ":"";r=e.name+"(",e.arguments.forEach(function(e,i){r+=p+t(e,o+" ")+(n-1>i?", ":"")}),r+=s+")"}else"field"===e.type?r="["+e.field+"]":"param"===e.type?r="[&"+e.param+"]":"num"===e.type?r=e.value:"str"===e.type&&(r='"'+e.value+'"');a&&(r="("+r+")"),l&&(r="NOT "+r)}else r=e;return r}function o(){var e=$("#"+a).val(),t="";window.localStorage[a]=JSON.stringify(e);var o=document.getElementById(a);if(void 0!==o.selectionStart&&o.selectionStart!=o.selectionEnd)e=o.value.substring(o.selectionStart,o.selectionEnd);else if(void 0!==document.selection){o.focus();var r=document.selection.createRange();e=r.text}var l=SiebelApp.S_App.GetService("FWK Runtime"),n=SiebelApp.S_App.NewPropertySet();n.SetProperty("Expr",e),n.SetProperty("BC",$("#"+a+"List").val());var s=l.InvokeMethod("EvalExpr",n);"Error"==s.GetProperty("Status")?t=s.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):(t=s.GetChildByType("ResultSet").GetProperty("Result"),console.log(s)),i($("#"+a+"Out").show().text(t)[0])}function i(e,t,o){o=o>0?o:30,t=t>0?t:5;var i=e.value,r=(e.cols,i.split("\n")),a=r.length;a>o?e.rows=o:t>a?e.rows=t:e.rows=a}function r(){var e=[];for(var t in SiebelApp.S_App.GetActiveBusObj().GetBCMap()){var o=SiebelApp.S_App.GetActiveBusObj().GetBCMap()[t];-1==e.indexOf(o.GetName())&&o.GetNumRows()>0&&e.push(o.GetName())}return e}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var a="SiebelEvalExpr";$("#"+a).parent().remove();var l=!1,n=r();if(0===n.length)alert("No BusComps/Records available!");else{var s='<div title="Runtime calculations"><label for="'+a+'List">Business Component:</label><select id = "'+a+'List" style="display:block"></select><label for="'+a+'">Expression:</label><textarea id = "'+a+'" rows="5" nowrap></textarea><label for="'+a+'Out">Results:</label><textarea id = "'+a+'Out" disabled rows="2"></textarea><style type="text/css">.ui-dialog textarea, .ui-dialog select {height:auto; width:100%; margin-bottom:10px} .ui-dialog label{margin-top:20px!}</style>',p=$(s).dialog({modal:!0,width:1024,heigth:640,open:function(){$("#"+a).focus(),$("#"+a+"Out").parent().keydown(function(e){e.ctrlKey&&13===e.keyCode&&o()}),$("#"+a+"List").append("<option>"+n.join("</option><option>")+"</option>"),$("#"+a+"List").val(SiebelApp.S_App.GetActiveView().GetActiveApplet().GetBusComp().GetName()),$("#"+a).val(JSON.parse(window.localStorage[a]))},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Format/Linarise",click:e,id:"BeautyBtn"},{text:"Run (Ctrl+Enter)",click:o},{text:"Close (Esc)",click:function(){$(this).dialog("destroy").remove()}}]});$(p).find("#"+a).keyup(function(){i(this,5)}).keyup(),$(".ui-dialog #BeautyBtn").hide(),require(["3rdParty/SiebelQueryLang"],function(e){console.log("Beautifier loaded!"),$(".ui-dialog #BeautyBtn").show()})}}}();
If you ever had a requirement to constrain an assocoation applet, you might be familiar with one of the methods:
To be honest, I always felt guilty using above methods and kept looking for a more declarative way. And finally found it.
Let's say you need to constrain a [Contact Assoc Applet] with Opportunity's primary organisation => so you'll assoc only contacts which are from the same org as your opportunity.
First thing you'll need to do is to change an applet class to be CSSSWEFrameSIAAssocList.
Now create a couple of applet user properties:
User Property | Value | Description |
---|---|---|
BC Field Search LHS | [Primary Organization Id] = | Left (static) part of spec. A field mentioned here is from assoc applet's BC. |
Parent BC Constraint Field | Primary Organization Id | Right (dynamic) part of spec is a field name from your list applet's parent BC (Opportunity) |
Applet is ready to be compiled and tested!
As a result Siebel will construct a named search spec as shown in a log below:
... Named search [Associate Constraint Search]: [Primary Organization Id] ='2-9EZ5U1'
Afterword
As I already mentioned, these UserProps are part of a specific class, which means you can't use it together with other useful UserProprs from different classes (for example, Override Visibility)
Also keep in mind that you are now bond to parent BC field name. Association applet will fail, if used in the context, where parent BC doesn't have a referenced field.
What if you can run eScript code without even opening Siebel Tools. Right from the Siebel client.
Need to code a new service? Click your favourite bookmarklet button and prototype it immediately, no need of recompilations/restarts.
Need to hook into a current BO context and manipulate active BCs? Easy!
Before I start, I have to say this idea haven't visited my head first. If you need a full immersion you should ask my masterminds - Jason or Roy.
So, here is how you build a lightweight eScript interactive playground.
As you may have guessed, it is all around eval() function. First step is to make it invokable from the browser:
1. Create a Business Service, as simple as this one:
function Service_PreInvokeMethod (MethodName, Inputs, Outputs) {
if (MethodName == “EvalScript”){
try {
Outputs.SetProperty(“Result”, eval(Inputs.GetProperty(“Expr”)));
} catch(e) {
Outputs.SetProperty(“Result”, e.toString());
}
}
return (CancelOperation);
}
A little trick here is to create the BS as a client-side business service, so it wouldn't be a part of repository => neither part of regular migrations => no security bleach on production.
2. Publish the service, so it can be accessible from a browser:
You will need a dialog with input and output text areas and a button to run the BS. Here is how your JS will look like:
// dialog html
var s = '<div title="eScript">'
+ '<textarea id = "SiebelEvalScript" style="height:150px"></textarea>'
+ '<textarea id = "SiebelEvalScriptOut" rows="4" disabled></textarea>'
+ '<style>textarea{width:100%!Important}</style>'
+ '</div>';
// display dialog
$(s).dialog({
modal: true,
width: 1024,
buttons: [{text:'Run', click: Eval}]
});
// run Business Service
function Eval(){
var sRes = "";
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", $('#SiebelEvalScript').val());
var outputSet = SiebelApp.S_App.GetService("FWK Runtime").InvokeMethod("EvalScript", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
}
$('#SiebelEvalScriptOut').text(sRes);
}
Try it in browser console or compile into a bookmarklet.
Don't have time building your own playground? Try mine, it is free and has some advanced features you might enjoy:
Front: Source code, Bookmarklet code, Bookmarklet
/*
@desc Framework allowing to run/evaluate eScript code
@author VB(xapuk.com)
@version 1.3 2018/12/05
@requires BS=FWK Runtime to be published
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
}else{
var editor; // AceJS editor object
var func = "SiebelEvalScript"; // function identifier
var snip; // an array of saved snippets
var last; // current snippet name
// dialog html
var s = '<div title="eScript">'
+ '<select id = "' + func + 'List" style="display:block"><option value="*">New...</option></select>'
+ '<textarea id = "' + func + '" placeholder="eSciript code..." style="height:150px"></textarea>'
+ '<label id = "' + func + '_lbl" for="' + func + '">Initialised</label>'
+ '<textarea id = "' + func + 'Out" rows="4" disabled></textarea>'
+ '<style>select,textarea{width:100%!Important}.ui-dialog-content{padding:0.5em 1em}</style>'
+ '</div>';
// hard-remove dialog object from DOM, just in case
$("#"+func + "List").parent().remove();
var d = $(s).dialog({
modal: true,
width: 1024,
open: function(){
$('#'+func).focus();
// load acejs plugin
if (typeof(ace) == "undefined"){
// injecting a script tag, also you can use require() function instead
var jsCode = document.createElement('script');
jsCode.setAttribute('src', "https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.2/ace.js");
jsCode.setAttribute('async', true);
jsCode.onload = attachACE;
document.body.appendChild(jsCode);
}else{
attachACE();
}
// List onchange
$("#"+func+"List").change(function(event) {
var n = $(this).val();
if (n != "*" && n > ""){
if (editor){
editor.setValue(snip[n]);
}else{
$("#"+func).text(snip[n]);
}
window.localStorage[func+"Last"] = n;
}
});
// key bindings
$("#"+func+"Out").parent().keydown(function(event) {
if (event.ctrlKey && event.keyCode === 13) { // ctrl + Enter
Eval();
return false;
}else if (event.ctrlKey && event.keyCode === 83) { // ctrl + S
Save();
return false;
}
});
Load(); // load presaved params
},
close: function(){
$(this).dialog('destroy').remove();
},
buttons: [
{
text:'Run (Ctrl+Enter)',
click: Eval
},
{
text:'Save (Ctrl + S)',
click: Save
},
{
text:'Remove',
click: Delete
},
{
text:'Close (Esc)',
click: function() {
$(this).dialog('close');
}
}
]
});
}
function Eval(){
var sExpr = GetCode();
var sRes = "";
var dTS = new Date();
var isChrome = !!window.chrome;
var isFirefox = typeof InstallTrigger !== 'undefined';
// execution timestamp
$('#'+func + "_lbl").text("Last executed at " + dTS.toISOString().replace("T", " ").replace("Z", " "));
Save(); // save snippets every time you run it
// invoke BS
var service = SiebelApp.S_App.GetService("FWK Runtime");
var ps = SiebelApp.S_App.NewPropertySet();
ps.SetProperty("Expr", sExpr);
var outputSet = service.InvokeMethod("EvalScript", ps);
if (outputSet.GetProperty("Status") == "Error"){
sRes = outputSet.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg");
}else{
sRes = outputSet.GetChildByType("ResultSet").GetProperty("Result");
}
$('#'+func + "Out").text(sRes);
// show results in browser console
if (console) {
var a = sRes.split(String.fromCharCode(13));
for(var i = 0; i < a.length; i++) {
// split into 3 parts for styling
var a2 = a[i].split('\t|\t');
var s1 = "", s2 = "", s3= "";
if (a2.length > 1){
if (a2.length > 2){
s1 = a2[0];
s2 = a2[1];
for(var j = 2; j < a2.length; j++) {
s3 += "\t" + a2[j];
}
} else {
s1 = a2[0];
s3 = a2[1];
}
} else {
s3 = a[i];
}
// collapse miltiline results
if (s3.indexOf("\n") > -1) {
if (isFirefox || isChrome ) {
console.groupCollapsed("%c" + s1 + " \t%c" + s2, "color:DarkCyan;", "color:Maroon;font-weight:bold");
} else {
console.groupCollapsed(s1 + " \t" + s2);
}
console.log(s3);
console.groupEnd();
} else {
if (isFirefox || isChrome ) {
console.log("%c" + s1 + " \t%c" + s2 + " \t%c" + s3, "color:DarkCyan;", "color:Maroon;font-weight:bold", "color:black;font-weight:normal");
} else {
console.log(s1 + " \t" + s2 + " \t" + s3);
}
}
}
}
}
// attach acejs plugin
function attachACE(){
editor = ace.edit(func);
editor.session.setMode("ace/mode/javascript");
$(".ace_editor").css("height","300");
}
// save button
function Save(){
var n = $('#' + func + "List").val();
if (n == "*" || n == null){ // new
n = prompt("Snippet name");
if(n){
if (n.match(/.{2,}/)){
snip[n] = GetCode(true);
window.localStorage[func] = JSON.stringify(snip);
$('#' + func + "List").append('<option value="' + n + '">' + n +'</option>');
$('#' + func + "List").val(n).change();
}else{
alert("Invalid snippet name!");
}
}
}else{ // existing
snip[n] = GetCode(true);
window.localStorage[func] = JSON.stringify(snip);
}
}
// Remove button
function Delete(){
var n = $('#' + func + "List").val();
if (confirm("Are you sure you want to delete a snippet: " + n)){
if (n && n != "*"){
delete snip[n]; // remove item
window.localStorage[func] = JSON.stringify(snip);
delete window.localStorage[func + "Last"];
Load(); // reload list
}
}
}
// loads preserved code snippets
function Load() {
var s = window.localStorage[func];
// remove all dropdown items
$("#" + func + "List option").remove();
//clear editor
if (editor){
editor.setValue("");
}else{
$("#"+func).text("");
}
// retrieve code snippets saved in local storage
var li = '';
if (s){
snip = JSON.parse(s);
for (k in snip){
li += '<option value="' + k + '">' + k + '</option>';
}
}else{
snip={};
}
$("#" + func + "List").append(li);
//last snippet
last = window.localStorage[func+"Last"];
if(last){
$('#' + func + "List").val(last).change();
}
}
// returns either selected peace of code or full value from text area or ACEJS plugin
function GetCode(bFull)
{
var sRes;
if (editor){
if (bFull || editor.getSelectedText() === ""){
sRes = editor.getValue();
}else{
sRes = editor.getSelectedText();
}
}else{
var textComponent = document.getElementById(func);
if (bFull){
sRes = $('#'+func).val();
}else if(textComponent.selectionStart !== undefined && textComponent.selectionStart != textComponent.selectionEnd){// Normal browsers
sRes = textComponent.value.substring(textComponent.selectionStart, textComponent.selectionEnd);
}else if(document.selection !== undefined){// IE
textComponent.focus();
var sel = document.selection.createRange();
sRes = sel.text;
}else{
sRes = $('#'+func).val();
}
}
return sRes;
}
javascript:void function(){function e(){var e=n(),t="",l=new Date,i=!!window.chrome,a="undefined"!=typeof InstallTrigger;$("#"+s+"_lbl").text("Last executed at "+l.toISOString().replace("T"," ").replace("Z"," ")),o();var r=SiebelApp.S_App.GetService("FWK Runtime"),c=SiebelApp.S_App.NewPropertySet();c.SetProperty("Expr",e);var d=r.InvokeMethod("EvalScript",c);if(t="Error"==d.GetProperty("Status")?d.GetChildByType("Errors").GetChild(0).GetProperty("ErrMsg"):d.GetChildByType("ResultSet").GetProperty("Result"),$("#"+s+"Out").text(t),console)for(var p=t.split(String.fromCharCode(13)),u=0;u<p.length;u++){var f=p[u].split(" | "),g="",v="",S="";if(f.length>1)if(f.length>2){g=f[0],v=f[1];for(var y=2;y<f.length;y++)S+=" "+f[y]}else g=f[0],S=f[1];else S=p[u];S.indexOf("\n")>-1?(a||i?console.groupCollapsed("%c"+g+" %c"+v,"color:DarkCyan;","color:Maroon;font-weight:bold"):console.groupCollapsed(g+" "+v),console.log(S),console.groupEnd()):a||i?console.log("%c"+g+" %c"+v+" %c"+S,"color:DarkCyan;","color:Maroon;font-weight:bold","color:black;font-weight:normal"):console.log(g+" "+v+" "+S)}}function t(){a=ace.edit(s),a.session.setMode("ace/mode/javascript"),$(".ace_editor").css("height","300")}function o(){var e=$("#"+s+"List").val();"*"==e||null==e?(e=prompt("Snippet name"),e&&(e.match(/.{2,}/)?(r[e]=n(!0),window.localStorage[s]=JSON.stringify(r),$("#"+s+"List").append('<option value="'+e+'">'+e+"</option>"),$("#"+s+"List").val(e).change()):alert("Invalid snippet name!"))):(r[e]=n(!0),window.localStorage[s]=JSON.stringify(r))}function l(){var e=$("#"+s+"List").val();confirm("Are you sure you want to delete a snippet: "+e)&&e&&"*"!=e&&(delete r[e],window.localStorage[s]=JSON.stringify(r),delete window.localStorage[s+"Last"],i())}function i(){var e=window.localStorage[s];$("#"+s+"List option").remove(),a?a.setValue(""):$("#"+s).text("");var t="";if(e){r=JSON.parse(e);for(k in r)t+='<option value="'+k+'">'+k+"</option>"}else r={};$("#"+s+"List").append(t),c=window.localStorage[s+"Last"],c&&$("#"+s+"List").val(c).change()}function n(e){var t;if(a)t=e||""===a.getSelectedText()?a.getValue():a.getSelectedText();else{var o=document.getElementById(s);if(e)t=$("#"+s).val();else if(void 0!==o.selectionStart&&o.selectionStart!=o.selectionEnd)t=o.value.substring(o.selectionStart,o.selectionEnd);else if(void 0!==document.selection){o.focus();var l=document.selection.createRange();t=l.text}else t=$("#"+s).val()}return t}if("undefined"==typeof SiebelApp)alert("It works only in Siebel OUI session!");else{var a,r,c,s="SiebelEvalScript",d='<div title="eScript"><select id = "'+s+'List" style="display:block"><option value="*">New...</option></select><textarea id = "'+s+'" placeholder="eSciript code..." style="height:150px"></textarea><label id = "'+s+'_lbl" for="'+s+'">Initialised</label><textarea id = "'+s+'Out" rows="4" disabled></textarea><style>select,textarea{width:100%!Important}.ui-dialog-content{padding:0.5em 1em}</style></div>';$("#"+s+"List").parent().remove();{$(d).dialog({modal:!0,width:1024,open:function(){if($("#"+s).focus(),"undefined"==typeof ace){var l=document.createElement("script");l.setAttribute("src","https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.2/ace.js"),l.setAttribute("async",!0),l.onload=t,document.body.appendChild(l)}else t();$("#"+s+"List").change(function(e){var t=$(this).val();"*"!=t&&t>""&&(a?a.setValue(r[t]):$("#"+s).text(r[t]),window.localStorage[s+"Last"]=t)}),$("#"+s+"Out").parent().keydown(function(t){return t.ctrlKey&&13===t.keyCode?(e(),!1):t.ctrlKey&&83===t.keyCode?(o(),!1):void 0}),i()},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Run (Ctrl+Enter)",click:e},{text:"Save (Ctrl + S)",click:o},{text:"Remove",click:l},{text:"Close (Esc)",click:function(){$(this).dialog("close")}}]})}}}();
Back: Service, and don't forget about Application UserProp
I have this idea since the release of OpenUI - to pull all useful info from SiebelApp object (BO, BCs, View, Applets, Controls etc.) into a dialog which can be opened with a hit of a button. And maybe also to built in some features for a better user experience.
Check out my version of "About View" bookmarklet:
javascript:{var id="SiebelAboutView",$d,tmp='<div title="About View" id="<%=id%>"><%var v=SiebelApp.S_App.GetActiveView(),am=v.GetAppletMap()%><b>Application:</b> <a><%=SiebelApp.S_App.GetName()%></a><br><b>View:</b> <a><%=v.GetName()%></a><br><b>BusObject:</b> <a><%=SiebelApp.S_App.GetActiveBusObj().GetName()%></a><br><%if(v.GetActiveTask()){%><b>Task:</b> <a><%=v.GetActiveTask()%></a><br><%}%><b>Applets(<%=Object.keys(am).length%>) / BusComps(<%=Object.keys(SiebelApp.S_App.GetActiveBusObj().GetBCMap()).length%>):</b><br><ul style="padding-left:20px"><%for(sa in am){var a=am[sa];var bc=a.GetBusComp();var r = bc.GetSelection() < bc.GetRecordSet().length?bc.GetRecordSet()[bc.GetSelection()]:{};var os="SiebelApp.S_App.GetActiveView().GetAppletMap()[\'"+sa+"\']";var $ds=$("#"+a.GetFullId());%><li><a data-target="controls"><b style="<%if($ds.is(":hidden")){%>font-style:italic;<%}if(a===v.GetActiveApplet()){%>text-decoration:underline<%}%>"><%=sa%></b></a> / <a data-target="fields"><b><%=bc.GetName()%></b></a><ul id="controls" style="display:none"><hr><b>Applet:</b> <a><%=a.GetName()%></a><br/><b>BusComp:</b> <a><%=bc.GetName()%></a><br/><b>Mode:</b> <a><%=a.GetMode()%></a><br/><b>Title:</b> <a><%=a.GetAppletLabel()%></a><br/><%var at=a.GetToggleApplet();if(at){%><b>Toggle:</b> <a><%=at%></a><br/><%}%><b>Object Selector:</b> <a><%=os%></a><br><b>DOM Selector:</b> <a>$("<%=$ds.selector%>")</a><br><b>Controls (<%=Object.keys(a.GetControls()).length%>): </b><ul><%for(control in a.GetControls()){var c=a.GetControls()[control];var $cds=$ds.find("[name=\'"+c.GetInputName()+"\']")%><li><a data-target="control"><b style="<%if($cds.is(":hidden")){%>font-style:italic;<%}if(c===a.GetActiveControl()){%>text-decoration:underline<%}%>"><%=c.GetDisplayName()||control%></b></a><ul id="control"><hr><%if($cds.is(":visible")&&$cds.is(":focusable")){%><button data-eval="$(\'<%=$cds.selector%>\').focus()">Focus</button><br><%}%><b>Control:</b> <a><%=control%></a><br><%if(c.GetFieldName()){%><b>Field:</b> <a><%=c.GetFieldName()%></a><br><%if(r){%><b>Value:</b> <a><%=r[c.GetFieldName()]%></a><br><%}%><b>Immediate post changes:</b> <a><%=c.IsPostChanges()%></a><br><%}%><b>Type:</b> <a><%=c.GetUIType()%></a> <br><b>Input:</b> <a><%=c.GetInputName()%></a><br><b>Object Selector:</b> <a><%=os+".GetControls()[\'"+control+"\']"%></a><br><b>DOM Selector:</b> <a>$("<%=$cds.selector%>")</a><br><%if(c.GetMethodName()){%><b>Method:</b> <a><%=c.GetMethodName()%></a><br><%}%><%if(c.GetPMPropSet()&&c.GetPMPropSet().propArrayLen > 0){%><b>User Props (<%=Object.keys(c.GetPMPropSet().propArray).length%>):</b><br><ul><%for(p in c.GetPMPropSet().propArray){%><%if("string"===typeof c.GetPMPropSet().propArray[p]){%><li><a><%=p%></a>=<a><%=c.GetPMPropSet().propArray[p]%> </a></li><%}%><%}%></ul><%}%><%if(c.GetMethodPropSet()&&c.GetMethodPropSet().propArrayLen > 0){%><b>Method PS (<%=Object.keys(c.GetMethodPropSet().propArray).length%>):</b><ul><%for(p in c.GetMethodPropSet().propArray){%><%if("string"===typeof c.GetMethodPropSet().propArray[p]){%><li><a><%=p%></a>=<a><%=c.GetMethodPropSet().propArray[p]%> </a></li><%}%><%}%></ul><%}%><hr></ul></li><%}%></ul><hr></ul><ul id="fields" style="display:none"><hr><b>BusComp:</b> <%=bc.GetName()%><br/><b>Commit pending:</b> <%=bc.commitPending%><br/><b>Fields:</b> <%=Object.keys(bc.GetFieldList()).length%><br/><b>Row:</b> <%=bc.GetCurRowNum()==-1?0:bc.GetCurRowNum()%> of <%=bc.GetNumRows()%><%=bc.IsNumRowsKnown()?"":"+"%><br/><ul><%for(var f in r){%><li><a><%=f%></a>=<a><%=r[f]%></a></li><%}%></ul><hr></ul></li><%}%></ul></div>';function AV(){var html=new EJS({text:tmp}).render(SiebelApp.S_App);$d=$(html).dialog({modal:!0,width:1024,open:function(){$(this).find("li").find("ul[id]").hide(),$(this).find("a").click(function(){copy(this)}),$(this).find("a").contextmenu(function(){return $(this).siblings("#"+$(this).attr("data-target")).toggle(),$(this).siblings("ul[id]:not([id='"+$(this).attr("data-target")+"'])").hide(),!1}),$(this).find("button").click(function(){var s=$(this).attr("data-eval");$d.dialog("close"),eval(s)})},close:function(){$(this).dialog("destroy").remove()},buttons:[{text:"Help",click:function(){window.open("http://xapuk.com/index.php?topic=80","_blank")}},{text:"Close (esc)",click:function(){$(this).dialog("close")}}]}),$d.css("padding-left","20px").find("ul").css("padding-left","20px"),$d.find("hr").css("margin","5px"),$d.find("a").hover(function(e){$(this).css({"text-decoration":"mouseenter"==e.type?"underline":"none"})})}function copy(e){var t=$(e).text();$(e).hide().after("<input id='"+id+"i'>"),$d.find("#"+id+"i").val(t).select(),document.execCommand("copy")?($d.find("#"+id+"i").attr("disabled","disabled").css("color","red").val("Copied!"),setTimeout(function(){$d.find("#"+id+"i").remove(),$(e).show()},700)):$d.find("#"+id+"i").blur(function(){$(this).remove(),$d.find("a").show()})}$("#"+id).parent().remove(),"undefined"==typeof SiebelApp?alert("It works only in Siebel OUI session!"):"undefined"==typeof EJS?requirejs(["3rdParty/ejs/ejs_production"],AV,function(){alert("Failed to load EJS")}):AV();};void(0)
/*
@desc advanced AboutView plugin
@author VB(xapuk.com)
@version 1.3 2018/07/10
*/
var id = "SiebelAboutView";
// template
var $d;
var tmp = ''+
'<div title="About View" id = "<%= id%>">'+
'<% var v = SiebelApp.S_App.GetActiveView() %>'+
'<b>Application:</b> <a><%= SiebelApp.S_App.GetName() %></a><br>'+
'<b>View:</b> <a><%= v.GetName() %></a><br>'+
'<b>BusObject:</b> <a><%= SiebelApp.S_App.GetActiveBusObj().GetName() %></a><br>'+
'<% if(v.GetActiveTask()) { %>'+
'<b>Task:</b> <a><%= v.GetActiveTask() %></a><br>'+
'<% } %>'+
'<b>Applets(<%= Object.keys(v.GetAppletMap()).length %>) / BusComps(<%= Object.keys(SiebelApp.S_App.GetActiveBusObj().GetBCMap()).length %>):</b><br>'+
'<ul style="padding-left:20px">'+
'<% for(applet in v.GetAppletMap()) { var a = v.GetAppletMap()[applet]; var bc = a.GetBusComp(); var r = bc.GetSelection() < bc.GetRecordSet().length?bc.GetRecordSet()[bc.GetSelection()]:{}; var os = "SiebelApp.S_App.GetActiveView().GetAppletMap()[\'" + applet + "\']"; var $ds = $("#" + a.GetFullId()); %>'+
'<li>'+
'<a data-target="controls"><b style="<% if($ds.is(":hidden")){ %>font-style:italic;<% } if(a===v.GetActiveApplet()){ %>text-decoration:underline<% } %>"><%= applet %></b></a> / '+
'<a data-target="fields"><b><%= bc.GetName() %></b></a>'+
'<ul id="controls" style="display:none">'+
'<hr>'+
'<b>Applet:</b> <a><%= a.GetName() %></a><br/>'+
'<b>BusComp:</b> <a><%= bc.GetName() %></a><br/>'+
'<b>Mode:</b> <a><%= a.GetMode() %></a><br/>'+
'<b>Title:</b> <a><%= a.GetAppletLabel() %></a><br/>'+
'<% if(a.GetToggleApplet()){ %>'+
'<b>Toggle:</b> <a><%= a.GetToggleApplet() %></a><br/>'+
'<% } %>'+
'<b>Object Selector:</b> <a><%= os %></a><br>'+
'<b>DOM Selector:</b> <a>$(\"<%= $ds.selector %>\")</a><br>'+
'<b>Controls (<%= Object.keys(a.GetControls()).length %>): </b>'+
'<ul>'+
'<% for(control in a.GetControls()) { var c = a.GetControls()[control]; var $cds = $ds.find("[name=\'" + c.GetInputName() + "\']") %>'+
'<li>'+
'<a data-target="control"><b style="<% if($cds.is(":hidden")){ %>font-style:italic;<% } if(c===a.GetActiveControl()){ %>text-decoration:underline<% } %>"><%= c.GetDisplayName()||control %></b></a>'+
'<ul id="control">'+
'<hr>'+
'<% if($cds.is(":visible") && $cds.is(":focusable")){ %>'+
'<button data-eval="$(\'<%= $cds.selector %>\').focus()">Focus</button><br>'+
'<% } %>'+
'<b>Control:</b> <a><%= control %></a><br>'+
'<% if(c.GetFieldName()){ %>'+
'<b>Field:</b> <a><%= c.GetFieldName() %></a><br>'+
'<% if(r){ %>'+
'<b>Value:</b> <a><%= r[c.GetFieldName()] %></a><br>'+
'<% } %>'+
'<b>Immediate post changes:</b> <a><%= c.IsPostChanges() %></a><br>'+
'<% } %>'+
'<b>Type:</b> <a><%= c.GetUIType() %></a> <br>'+ // to decode value trhough SiebelJS.Dependency("SiebelApp.Constants");
'<b>Input:</b> <a><%= c.GetInputName() %></a><br>'+
'<b>Object Selector:</b> <a><%= os+".GetControls()[\'" + control + "\']" %></a><br>'+
'<b>DOM Selector:</b> <a>$(\"<%= $cds.selector %>\")</a><br>'+
'<% if(c.GetMethodName()){ %>'+
'<b>Method:</b> <a><%= c.GetMethodName() %></a><br>'+
'<% } %>'+
'<% if(c.GetPMPropSet() && c.GetPMPropSet().propArrayLen > 0){ %>'+
'<b>User Props (<%= Object.keys(c.GetPMPropSet().propArray).length %>):</b><br>'+
'<ul>'+
'<% for(p in c.GetPMPropSet().propArray){ %>'+
'<% if("string" === typeof c.GetPMPropSet().propArray[p]){ %>'+
'<li><a><%= p %></a> = <a><%= c.GetPMPropSet().propArray[p] %> </a></li>'+
'<% } %>'+
'<% } %>'+
'</ul>'+
'<% } %>'+
'<% if(c.GetMethodPropSet() && c.GetMethodPropSet().propArrayLen > 0){ %>'+
'<b>Method PS (<%= Object.keys(c.GetMethodPropSet().propArray).length %>):</b>'+
'<ul>'+
'<% for(p in c.GetMethodPropSet().propArray){ %>'+
'<% if("string" === typeof c.GetMethodPropSet().propArray[p]){ %>'+
'<li><a><%= p %></a> = <a><%= c.GetMethodPropSet().propArray[p] %> </a></li>'+
'<% } %>'+
'<% } %>'+
'</ul>'+
'<% } %>'+
'<hr>'+
'</ul>'+
'</li>'+
'<% } %>'+
'</ul>'+
'<hr>'+
'</ul>'+
'<ul id="fields" style="display:none">'+
'<hr>'+
'<b>BusComp:</b> <%= bc.GetName() %><br/>'+
'<% if(r && r.hasOwnProperty("Id")){ %>'+
'<b>Row Id:</b> <a><%= r.Id %></a><br/>'+
'<% } %>'+
'<% if(r && r.hasOwnProperty("Created")){ %>'+
'<b>Created:</b> <a><%= r.Created %></a><br/>'+
'<% } %>'+
'<% if(r && r.hasOwnProperty("Updated")){ %>'+
'<b>Updated:</b> <a><%= r.Updated %></a><br/>'+
'<% } %>'+
'<b>Commit pending:</b> <%= bc.commitPending %><br/>'+
'<b>Fields:</b> <%= Object.keys(bc.GetFieldList()).length %><br/>'+
'<b>Row:</b> <%= bc.GetCurRowNum()==-1?0:bc.GetCurRowNum() %> of <%= bc.GetNumRows() %><%= bc.IsNumRowsKnown()?"":"+" %><br/>'+
'<ul>'+
'<% for(var f in r){ %>'+
'<li><a><%= f %></a> = <a><%= r[f] %></a></li>'+
'<% } %>'+
'</ul>'+
'<hr>'+
'</ul>'+
'</li>'+
'<% } %>'+
'</ul>'+
'</div>';
// to support single session
$("#" + id).parent().remove();
// show the dialog
function SiebelAboutView(){
var html = new EJS({text: tmp}).render(SiebelApp.S_App);
$d = $(html).dialog({
modal: true,
width: "1024",
open:function(){
// hide all expandable ULs by default
$(this).find("li").find("ul[id]").hide();
// attempt to copy span content (click)
$(this).find("a").click(function(){
copy(this);
});
// expand (right click)
$(this).find("a").contextmenu(function(){
$(this).siblings("#"+$(this).attr("data-target")).toggle();
$(this).siblings("ul[id]:not([id='"+$(this).attr("data-target")+"'])").hide();
return false;
});
// focus on control
$(this).find("button").click(function(){
var str = $(this).attr("data-eval");
$d.dialog('close');
eval(str);
});
},
close: function(){
$(this).dialog('destroy').remove();
},
buttons: [
{
text:'Help',
click: function(){
window.open("http://xapuk.com/index.php?topic=80", "_blank");
}
},
{
text:'Copy (left click)',
disabled: true
},
{
text:'Expand (right click)',
disabled: true
},
{
text:'Close (esc)',
click: function() {
$(this).dialog('close');
}
}
]
});
// styling
$d.css("padding-left","20px");
$d.find("ul").css("padding-left","20px");
$d.find("hr").css("margin","5px");
$d.find("a").hover(function(e){
$(this).css({"text-decoration":e.type=="mouseenter"?"underline":"none"});
});
}
// copy value
function copy(scope){
// replacing link with intput and select the value
var val = $(scope).text();
$(scope).hide().after("<input id='" + id + "i'>");
$d.find("#" + id + "i").val(val).select();
// attempt to copy value
if (document.execCommand("copy", false, null)){
// if copied, display a message for a second
$d.find("#" + id + "i").attr("disabled", "disabled").css("color","red").val("Copied!");
setTimeout(function(){
$d.find("#" + id + "i").remove();
$(scope).show();
}, 700);
}else{
// if failed to copy, leave input until blur, so it can be copied manually
$d.find("#" + id + "i").blur(function(){
$(this).remove();
$d.find("a").show();
});
}
}
if ("undefined" === typeof SiebelApp || "undefined" === typeof SiebelApp.S_App){
alert("Please launch Siebel application first.");
}else if ("undefined" === typeof EJS){
var src = "3rdParty/ejs/ejs_production";
requirejs([src], SiebelAboutView, function(){alert("Failed to load EJS library ! \n" + src);});
}else{
SiebelAboutView();
}
A new version is available here.
Since OpenUI release, browser console became a powerful tool in hands of confident Siebel developer. In this topic I'll give you an idea how to organise your browser code snippets and will share couple of examples how to take an advantage of SiebelApp object.
Traditional way of injecting browser code.
How to run: F12 => Console tab => type the command or use up key to flip through history => enter
SiebelApp.S_App.GetActiveView().GetActiveApplet().GetName();
Useful if you need to store more than oneliners.
How to run: F12 => Source tab => Snippets => right click => run
Here is an example why you might need it.
console.log(SiebelApp.S_App.GetActiveView().GetName());
var am = SiebelApp.S_App.GetActiveView().GetAppletMap();
for (sAppletName in am) {
console.log('\t' + sAppletName + '(' + am[sAppletName].GetBusComp().GetName() + ')');
}
Easiest way to run a code snippet. It is not only takes one click to run your code but it also allows 'endless' features (fancy UI, working with clipboard, history of queries). I see bookmarklets as an open source, cross-browser, cross-environment Siebel plugin. If you manage to use Chrome code snippets to build and debug snippets and then compile them into bookmarklets.
I usually use Chrome snippets as a source code vault and as GUI to build and debug my code. Once the code is stable, I make it a bookmarklet and enjoy it.
How to create: Linearise your JS code, remove comments or simply minify it (for example, JSCompress or JS/CSS Minifier, thanks Emma!), escape quotes and finally wrap it with "javascript:{...};void(0)". Here is how the code from above example will look like as a bookmarklet:
javascript:{var s=SiebelApp.S_App.GetActiveView().GetName();var am=SiebelApp.S_App.GetActiveView().GetAppletMap();for(sAppletName in am){s+='\n\t'+sAppletName+'('+am[sAppletName].GetBusComp().GetName()+')';}alert(s);};void(0)
You can always use online bookmarklet converters. Here are couple examples:
How to install: Easiest way is to drag & drop a link on your favourites toolbar. I'm a bookmarklet Drag & Drop me. Here is a full article on how to install bookmarklets for different browsers.
How to run: Click
Check out how powerful and convenient bookmarklets could be:
/*
@desc fancy UI wrapper for GetProfileAttr Siebel function
@author VB(xapuk.com)
@version 1.1 2018/06/08
*/
if ("undefined" == typeof SiebelApp) {
alert("It works only in Siebel OUI session!");
}
// snippet id
var id = "SiebelProfileAttr";
// localStorage to store the history
var aHist = window.localStorage[id] ? JSON.parse(window.localStorage[id]) : [];
// just in case (experimental)
$("#" + id).parent().remove();
// constructing dialog content
var s = '<div title="Get Profile Attribute">';
s += '<input id = "' + id + '" type="text" list="' + id + 'List" style="width:100%" value="' + (aHist.length ? aHist[0] : "") + '">';
s += '<datalist id="' + id + 'List">';
for (var i = 0; i < aHist.length; i++){
s += '<option>' + aHist[i] + '</option>';
}
s += '</datalist>';
s += '<input id="' + id + 'Out" type ="text" style="display:none">';
s += '<ul></ul></div>';
// open dialog
var $d = $(s).dialog({
modal: true,
width: 640,
open: function() {
$('#' + id).focus().select(); // autofocus
},
close: function() {
$(this).dialog('destroy').remove();
},
buttons: [{
text: 'Get (Enter)',
click: go
}, {
text: 'Close (Esc)',
click: function() {
$(this).dialog('close');
}
}]
});
listHistory();
function go() {
var name = $d.find('#' + id).val();
if (name) {
// moving recent query to the top
if (aHist.indexOf(name) > -1) {
aHist.splice(aHist.indexOf(name), 1);
}
aHist.unshift(name);
window.localStorage[id] = JSON.stringify(aHist);
//rerender history
listHistory();
}
return name;
}
// print a list of recent queries
function listHistory() {
var $ul = $d.find("ul").empty();
for (var i = 0; i < aHist.length && i < 5; i++) {
// five recent values
$ul.append('<li><b>' + aHist[i] + '</b> = <a href="#">' + SiebelApp.S_App.GetProfileAttr(aHist[i]) + '</a></li>');
}
// copy value on click
$d.find("a").click(function(event) {
var val = $(this).html();
$("#" + id + "Out").show().val(val).select();
var r = document.execCommand("copy");
$("#" + id + "Out").hide();
if(r){
$(this).hide().after('<span id="tmp" style="color:red">Copied!</span>');
setTimeout(function(){
$d.find("a").show();
$d.find("#tmp").remove();
}, 500);
}
});
}
// key bindings
$d.keyup(function(event) {
// enter
if (event.keyCode === 13) {
go();
}
});
/*
@desc fancy wrapper for GotoView Siebel function
@author VB(xapuk.com)
@version 1.0 12/06/2018
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
}
// snippet id
var id = "SiebelGotoView";
// localStorage to store the history
var aHist = window.localStorage[id]?JSON.parse(window.localStorage[id]):[];
// just in case (experimental)
$("#" + id).parent().remove();
// constructing dialog content
var s = '<div title="Goto View">';
s += '<input id = "' + id + '" type="text" list="' + id + 'List" style="width:100%" value="' + (aHist.length?aHist[0]:"") + '">'; // most recent
s += '<datalist id="' + id + 'List">';
for (var i =0; i < aHist.length; i++){ // full history into unbounded picklist
s += '<option>' + aHist[i] + '</option>';
}
s += '</datalist><ul>';
for (var i =0; i < aHist.length && i < 5; i++){ // five recent values as links
s += '<li><a href="#">' + aHist[i] + '</a></li>';
}
s += '</ul></div>';
// open dialog
var $d = $(s).dialog({
modal: true,
width: 640,
open: function() {
$('#' + id).focus().select(); // autofocus
},
close: function() {
$(this).dialog('destroy').remove();
},
buttons: [{
text: 'Go (Enter)',
click: function(){
go($d.find('#' + id).val());
}
}, {
text: 'Close (Esc)',
click: function() {
$(this).dialog('close');
}
}]
});
// GotoView
function go(name) {
if (name){
// moving recent view to the top
if (aHist.indexOf(name) > -1){
aHist.splice(aHist.indexOf(name),1);
}
aHist.unshift(name);
window.localStorage[id] = JSON.stringify(aHist);
$d.dialog('close');
SiebelApp.S_App.GotoView(name); //running GotoView command
}
}
// running GotoView on Enter
$d.keyup(function(event) {
// enter
if (event.keyCode === 13) {
go($d.find('#' + id).val());
}
});
// running GotoView on link click
$d.find("a").click(function(event) {
go($(this).html());
});
Not sure if blog is a right word though. You know like sometimes we maintain a library of frequently used code snippets, templates, tools, useful links, ideas to research someday, etc. Starting with a single text file it grows into a massive "ARCHIVE" folder. So, yes that is what it is - a vault of my code snippets, ideas, and tools. I finally made that folder my online playground and started sharing some of these. Really hope it will be of some use to anyone.
Tools - several converters I use almost every day and will be happy if you guys find it useful. Could bind variables into SQL statement or convert a PropertySet from the logs or HTTP responses into readable XML, etc. Check out for help() and example() commands over there.
Blog - some ideas and code snippets, I think worth sharing and I had free time to describe a little. Actually, I'll be describing a very little and just let the code samples talk for me. I'm always happy to answer questions, get feedback or hear your thoughts.
Most of my code snippets are also available on GitHub and free to use as is or to fork.
In case you wonder, "Xapuk" is a transliteration from my russian nickname Харик, pronounced like [Harik].
Since last versions of Siebel don't allow logging in through GET requests (credentials in URL address), it is now a bit more challenging to have a "log into Siebel" browser bookmarks. However, we can acheive the same by sending a POST request. Check it out in the below example.
Quick description:
A single-file page to orginise all Siebel links which allow you to log into Siebel thin client in one click.
Setup instruction:
Couple tricks how to use Notepad++ to analyse Siebel logs
(Ctrl+F, Search All Document)
Workflow stepsInstantiating step definition
Task steps
Task engine requested to navigate to next step
Browser calls server (both user and system)
SWECmd=
(Regular expression option should be checked)
Workflows and tasks together(Instantiating .* definition)|(Task engine requested to navigate to next step)
Let's say you want to know which processes down the stack are doing updates
(Instantiating .* definition)|(Task engine requested to navigate to next step)|(UPDATE)
This one traces where WF starts and ends with all the steps:
(Stopping process instance of)|(Executing process definition)|(Instantiating step)
Ok, you've got the idea.
EAI Data MapsExecuting the (Component|Field) Map
Client-Server method calls
(?<=SWEMethod=).*?(?=\;)
Low performance (general) / operations which took more than a second
(\s[1-9]|\d{2,})\.\d*\sseconds
Low performance SQL execution
SQL Statement .* Time for SQL Cursor with ID \w*: [^0]
Low performance SQL fetch
(.*)(\d{4}\-\d{2}\-\d{2}) (\d{2}\:\d{2}\:\d{2})(.*)Fetch All Time: [^0](d|.*) seconds.
Time gaps between two lines (experimental)
(.*)(\d{4}\-\d{2}\-\d{2}) (\d{2}\:\d{2}\:\d{2})(.*).\s*(.*)\2 (?!\3)(.*)
Starting a series of SQLs to check the consistency of Siebel repository object.
And the first SQL is to identify Integration Component fields referencing non-existing BusComp fields:
with io as ( -- IO/IC/Field
SELECT io.repository_id, io.name IO, ic.name IC, ic.ext_name BC, nvl(ifu.value, iff.ext_name) FIELD
FROM siebel.S_INT_OBJ io
join siebel.S_INT_COMP ic on io.row_id = ic.int_obj_id
and ic.inactive_flg = 'N'
join siebel.S_INT_FIELD iff on iff.int_comp_id = ic.row_id
and iff.inactive_flg = 'N'
and iff.field_type_cd = 'Data'
left join siebel.S_INTFLD_UPROP ifu on ifu.int_field_id = iff.row_id -- MVF
and ifu.inactive_flg = 'N'
and ifu.name in ('MVGFieldName', 'AssocFieldName')
where io.base_obj_type = 'Siebel Business Object'
and io.inactive_flg = 'N'),
bc as ( -- BC/Field
select bc.repository_id, bc.name BC, f.name FIELD
from siebel.S_BUSCOMP bc
join siebel.S_FIELD f on f.buscomp_id = bc.row_id
and f.inactive_flg = 'N')
select io.io, io.ic, io.field, io.bc
from io
join siebel.S_REPOSITORY r on r.row_id = io.repository_id
where r.name = 'Siebel Repository'
and io.field not in ('Id','Conflict Id','Created','Created By','Mod Id','Updated','Updated By', 'SSA Primary Field', 'IsPrimaryMVG') -- excluding system fields
and (bc, field) not in (select bc, field from bc where repository_id = r.row_id)
and io.io like 'AMS%'; -- to filter by your project preffix
Do you remember a pain when calculated expression splits across dozens calculated fields?
That SQL will help you to visualise complex dependencies of calculated fields:
SELECT level,
SYS_CONNECT_BY_PATH(t.NAME, '/') PATH,
t.*
FROM
(SELECT f.NAME, f.CALCVAL
FROM siebel.S_FIELD f
JOIN siebel.S_BUSCOMP b ON b.row_id = f.BUSCOMP_ID
WHERE b.name = 'Order Entry - Orders' -- bus comp name
) t
START WITH name = 'Payment Order Total' -- field name
CONNECT BY prior CALCVAL LIKE '%[' || NAME || ']%'
ORDER BY 2;
It will show you all subsequent fields for vanilla calculated field [Payment Order Total] (BusComp = Order Entry - Orders) in recursion:
Here is a full version of SQL with more details
with t as (
select f.NAME, f.CALCVAL,
DECODE(f.MULTI_VALUED, 'Y', f.MVLINK_NAME || '.' || f.DEST_FLD_NAME,
DECODE(f.CALCULATED, 'Y', f.CALCVAL,
DECODE(f.JOIN_NAME, null, b.TABLE_NAME || '.' || f.COL_NAME,
DECODE(j.DEST_TBL_NAME, null, f.JOIN_NAME || '.' || f.COL_NAME,
j.DEST_TBL_NAME || '.' || f.COL_NAME)))) VAL
from siebel.S_FIELD f
join siebel.S_BUSCOMP b on b.row_id = f.BUSCOMP_ID
left join siebel.S_JOIN j on j.NAME = f.JOIN_NAME AND j.BUSCOMP_ID = f.BUSCOMP_ID
where b.name = 'Order Entry - Orders')
select distinct level, SYS_CONNECT_BY_PATH(t.NAME, '/') PATH, t.NAME, t.VAL from t
start with name = 'Payment Order Total'
connect by prior CALCVAL LIKE '%[' || NAME || ']%'
order by 2;
Copy below script in browser console (F12) and run it once. It will keep Siebel client alive during the day.
function keepAlive(iHour, iNum)
{
var iStep = 60; // will send the request every minnute
iNum = iNum>0?iNum:0;
if (iHour*60*60 > iNum*iStep){
setTimeout(function(){
if (typeof(SiebelApp.S_App.GetProfileAttr("ActiveViewName")) != "undefined"){
SiebelJS.Log("keep alive / " + iNum + " / " + (new Date()));
keepAlive(iHour, ++iNum);
}
}, iStep * 1000);
}
}
keepAlive(8); // 8 = number of hours you want Siebel to live
It is helpful when you are working with Siebel client all day long and you don't want to re-login after a short pause. Very handy during Siebel frontend development.
A fancy version with a pulsing indicator and on / off toggle.
javascript:{function keepAlive(e,i){i=0<i?i:0,console&&console.log("keep alive / "+i+" / "+new Date),60*i<60*e*60?(0===$("#SiebelKeepAlive").length?$("body").append("<div id='SiebelKeepAlive' style='position:fixed;top:5px;left:5px;background-color:#ff5e00;border:solid 2px;border-radius:5px;padding:5px'><b>KA</b></div>"):$("#SiebelKeepAlive").fadeTo(100,1),$("#SiebelKeepAlive").fadeTo(54e3,.2),window.timerId=setTimeout(function(){void 0!==SiebelApp.S_App.GetProfileAttr("ActiveViewName")&&keepAlive(e,++i)},6e4)):keepAlive_stop()}function keepAlive_stop(){clearTimeout(window.timerId),delete window.timerId,$("#SiebelKeepAlive").remove(),console.log("keep alive time stopped")}"undefined"==typeof SiebelApp?alert("It works only in Siebel OUI session!"):void 0===window.timerId?keepAlive(4):keepAlive_stop();};void(0)
/*
@desc Keeps Siebel session alive for a certain amount of time
@author VB(xapuk.com)
@version 2.0 2018/07/20
*/
if ("undefined" == typeof SiebelApp){
alert("It works only in Siebel OUI session!");
}else{
if ("undefined" === typeof window.timerId){
keepAlive(4); // number of hours you want Siebel to live
}else{
keepAlive_stop();
}
}
function keepAlive(iHour, iNum)
{
var iStep = 60; // frequency in secs
iNum = iNum>0?iNum:0;
if (console) console.log("keep alive / " + iNum + " / " + (new Date()));
if (iHour*60*60 > iNum*iStep){
if($("#SiebelKeepAlive").length === 0){
$("body").append("<div id='SiebelKeepAlive' style='position:fixed;top:5px;left:5px;background-color:#ff5e00;border:solid 2px;border-radius:5px;padding:5px'><b>KA</b></div>");
}else{
$("#SiebelKeepAlive").fadeTo(100, 1);
}
$("#SiebelKeepAlive").fadeTo(iStep * 900, 0.2);
window.timerId = setTimeout(function(){
if (typeof(SiebelApp.S_App.GetProfileAttr("ActiveViewName")) != "undefined"){
keepAlive(iHour, ++iNum);
}
}, iStep * 1000);
}else{
keepAlive_stop();
}
}
function keepAlive_stop(){
clearTimeout(window.timerId);
delete window.timerId;
$("#SiebelKeepAlive").remove();
console.log("keep alive time stopped");
}
Here I want to share the way you can prettify Siebel built-in code editor and make Client-side Business Service development process a bit more pleasant.
Regarding to to my choice of online code editor. After a quick loop through "top 5 editors", I've picked AceJS - mostly because I had a recent experience working with it.
After you download lib (https://github.com/ajaxorg/ace-builds/), place the content of "src-min-noconflict" subfolder in "\PUBLIC\enu\23048\SCRIPTS\3rdParty\ace\".
Create Presentation Model and Physical Renderer files:
if (typeof (SiebelAppFacade.CSBS_PM) === "undefined") {
SiebelJS.Namespace("SiebelAppFacade.CSBS_PM");
define("siebel/custom/CSBS_PM", ["siebel/pmodel"],
function () {
SiebelAppFacade.CSBS_PM = (function () {
function CSBS_PM(pm) {
SiebelAppFacade.CSBS_PM.superclass.constructor.apply(this, arguments);
}
SiebelJS.Extend(CSBS_PM, SiebelAppFacade.PresentationModel);
CSBS_PM.prototype.Init = function () {
SiebelAppFacade.CSBS_PM.superclass.Init.apply(this, arguments);
}
CSBS_PM.prototype.Setup = function (propSet) {
SiebelAppFacade.CSBS_PM.superclass.Setup.apply(this, arguments);
this.AddProperty("ScriptValue", ""); // property to pass field value PM->PR
bc = this.Get("GetBusComp");
var sField = "Script"; // hardcoded field name to attach ace plugin
this.AddProperty("ScriptFieldName", sField);
// update ace editor value everytime script field value changes
this.AddMethod("GetFormattedFieldValue", function (control) {
if (control.GetFieldName() == sField && bc.GetFieldValue(sField) != this.Get("ScriptValue")) {
this.SetProperty("ScriptValue", bc.GetFieldValue(sField));
}
}, {
sequence: false,
scope: this
});
}
return CSBS_PM;
}());
return "SiebelAppFacade.CSBS_PM";
})
}
if (typeof (SiebelAppFacade.CSBS_PR) === "undefined") {
SiebelJS.Namespace("SiebelAppFacade.CSBS_PR");
define("siebel/custom/CSBS_PR", ["3rdParty/ace/ace", // determines dependencies (path to ace.js file)
"siebel/phyrenderer"],
function () {
SiebelAppFacade.CSBS_PR = (function () {
function CSBS_PR(pm) {
SiebelAppFacade.CSBS_PR.superclass.constructor.apply(this, arguments);
}
SiebelJS.Extend(CSBS_PR, SiebelAppFacade.PhysicalRenderer);
CSBS_PR.prototype.Init = function () {
SiebelAppFacade.CSBS_PR.superclass.Init.apply(this, arguments);
}
CSBS_PR.prototype.ShowUI = function () {
SiebelAppFacade.CSBS_PR.superclass.ShowUI.apply(this, arguments);
var pm = this.GetPM(); // to use in global scope functions
var bc = pm.Get("GetBusComp");
var sField = pm.Get("ScriptFieldName");
// get original control
var oOrig = $("textarea[name='" + pm.Get("GetControls")[sField].GetInputName() + "']");
// add control for ace editor
var sNewId = "ace_code_editor";
SiebelJS.Log($(oOrig).parent().after('<div id="' + sNewId + '"></div>'));
var oNew = $("#" + sNewId);
// attach ace editor
var oAce = ace.edit(sNewId);
oAce.setTheme("ace/theme/monokai");
oAce.getSession().setMode("ace/mode/javascript");
oAce.$blockScrolling = Infinity;
// to be replaced with css file
oNew.css("height", oOrig.height() + "px"); // copy control height
oOrig.remove(); // remove orig control
$("#s_" + pm.Get("GetFullId") + "_div").find(".mceLabel").remove(); // remove labels
// copy value from ace editor into the field
oAce.getSession().on('change', function () {
bc.SetFieldValue(sField, oAce.getValue());
});
// copy field value to ace editor
this.AttachPMBinding("ScriptValue", function () {
field_value = pm.Get("ScriptValue");
if (field_value != oAce.getValue()) {
oAce.setValue(field_value);
oAce.gotoLine(1); // first line by default
}
});
}
CSBS_PR.prototype.BindEvents = function () {
SiebelAppFacade.CSBS_PR.superclass.BindEvents.apply(this, arguments);
}
CSBS_PR.prototype.BindData = function () {
SiebelAppFacade.CSBS_PR.superclass.BindData.apply(this, arguments);
}
return CSBS_PR;
}());
return "SiebelAppFacade.CSBS_PR";
})
}
Don't forget to attach PM/PR files to applet (Business Service Script Editor Applet2) on Application -> Manifest Administration view.
Optimise a view layout (custom .SWT?).
Maybe attach a custom CSS to prettify a view.
Put BS simulator applets on the same page and pre-default BS Name there.
Enjoy your runtime playground!
Full-stack software developer with 20+ years of experience.
Since 2005 primarily focusing on Siebel.