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!