- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎06-03-2020 01:33 PM
Hello,
I'm having issues with setting up the KB Category Picker to work with a custom lookup table / form that I've created. The desired KB Category Picker that I want is shown below; it is the same picker that is used when creating a new knowledge article ("kb_knowledge" table). It is different from a regular list in that it allows you to drill down through the different levels of categories for the selected KB.
My table ("u_kb_category_to_approval_group") is extended from "dl_matcher" (lookup) and has the following three user-columns:
These fields all have the "u_" prefix because they are custom user fields. I want to have the same KB Category Picker for my "u_kb_category" field. The default KB Category Picker is handled by the "kb_category_reference_lookup" UI macro and "kb_categories_dialog" UI page. However, they both work with the "kb_category" and "kb_knowledge_base" fields whereas my equivalent fields include the "u_" prefix. As such, I had to create a copy of both the UI macro ("kb_category_reference_lookup_2") and the UI page ("kb_categories_dialog_2") and change them accordingly to reference my prefixed fields. The KB Category Picker dialog opens correctly for my form except it doesn't behave correctly when I select a category and press the "Ok" button; it doesn't acknowledge the selected category so an empty value is copied to the parent form's category field. I've troubleshooted and I don't believe that the "columnview_select" code in "kb_categories_dialog_2" client script is running as it should. It should be setting the hidden control called "categoryId" (in the dialog window) to the selected value each time a category is selected but its not. Your help in solving this issue would be greatly appreciated. Thank you!
The relevant code and configurations are shown below.
u_kb_category field decorations and reference qualifiers:
"kb_category_reference_lookup_2" UI macro XML code:
<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:inline template="ie_checker.xml" />
<g:requires name="scripts/lib/jquery_includes.js"/>
<g2:evaluate var="jvar_table_name">
var tableName = current.getTableName();
tableName;
</g2:evaluate>
<g2:evaluate var="jvar_contribute_kb">
var knowledgeBases = new SNC.KnowledgeHelper().getWritableKnowledgeBases();
var answer = [];
while (knowledgeBases.next())
answer.push(knowledgeBases.getValue('sys_id'));
answer;
</g2:evaluate>
<g2:evaluate var="jvar_timeout">
gs.minutesAgo(gs.getProperty('glide.ui.session_timeout') || 40);
</g2:evaluate>
<g2:evaluate var="jvar_msg_lookup_picker">
gs.getMessage("Lookup using picker");
</g2:evaluate>
<script>
var canCreateCategory='false', oldKnowledgeBase, category_link_id;
function getCompatibleColumnWidth() {
if ((window.frameElement) $[AMP]$[AMP] ( window.frameElement.id == 'dialog_frame')){
return (window.frameElement.getWidth() * 0.75);
}
// Resize if displayed from a dialog
return 700; // Default value for content width
}
function canContribute(kbId) {
return '$[jvar_contribute_kb]'.indexOf(kbId) > -1;
}
function openDialog() {
var kb_knowledge_base = g_form.getValue('u_kb_knowledge_base');
var kb_knowledge_base_gr = g_form.getReference('u_kb_knowledge_base');
var table_name = g_form.getTableName();
//Verify for canCreate access only if Knowledge Base is changed
if(oldKnowledgeBase!=kb_knowledge_base){
var ga = new GlideAjax('KBCategoryCrud');
ga.addParam('sysparm_name','canCreate');
ga.addParam('sysparm_id', kb_knowledge_base); // ID of parent Knowledge Base
var responseXML = ga.getXMLWait();
if (responseXML $[AMP]$[AMP] responseXML.documentElement) {
canCreateCategory = responseXML.getElementsByTagName("result")[0].getAttribute("canCreateCategory");
oldKnowledgeBase = kb_knowledge_base;
}else{
console.log("ERROR: Error in checking for canCreate access on category. Please check the server response below.");
console.log(responseXML);
canCreateCategory = 'false';
}
}
if (!kb_knowledge_base || !kb_knowledge_base.length)
return;
if (kb_knowledge_base_gr.kb_version === '3') {
//Allow only those KBs to which current user can contribute and are created newly by him
if (!canContribute(kb_knowledge_base) $[AMP]$[AMP] !(kb_knowledge_base_gr.sys_created_by == g_user.userName $[AMP]$[AMP] kb_knowledge_base_gr.sys_created_on > '$[jvar_timeout]'))
return ;
}
var disable_editing = 'false';
if(canCreateCategory == 'false' || kb_knowledge_base_gr.disable_category_editing == 'true'){
disable_editing = 'true';
}
var dialog = new GlideDialogWindow('kb_categories_dialog_2');
dialog.setTitle('${gs.getMessage('Category picker')}');
dialog.setSize(getCompatibleColumnWidth(), 250);
dialog.setPreference('kb_knowledge_base', kb_knowledge_base);
dialog.setPreference('disable_editing', disable_editing);
dialog.hideCloseButton();
dialog.on('beforeclose', function(){
setTimeout(function(){
if(category_link_id){
$j(category_link_id).focus();
}});
});
dialog.render();
var isQuirksMode = (document.compatMode !== 'CSS1Compat');
if (!isQuirksMode) {
// In modern browsers only: make the dialog box fixed to avoid it scrolling away while using its scrollable content.
// Do dialog css adjustment only after whole dialog has been rendered
dialog.on("bodyrendered", function() {
var wndw = $j(window);
var dlgSecPart = $j('.drag_section_part');
dlgSecPart.css('width', 'auto').css('height', 'auto');
dlgSecPart.css('position', 'fixed');
dlgSecPart.css('top', Math.max(0, ((wndw.height() - dlgSecPart.outerHeight()) / 2) + wndw.scrollTop()) + 'px');
dlgSecPart.css('left', Math.max(0, ((wndw.width() - dlgSecPart.outerWidth()) / 2) + wndw.scrollLeft()) + 'px');
//Set focus to first element or add icon
var firstListElm = $j("#body_kb_categories_dialog .colview-container .column .list-item");
if(firstListElm.length)
firstListElm.first().focus();
else{
$j("#body_kb_categories_dialog .colview-container .column .btn-add-cat").focus();
}
});
}
}
$j(function($) {
//doctype case in split layout
var doctypeSplitView = '.vsplit.col-sm-6 > div';
//doctype case in row (non split) layout
var doctypeRowView = '.section-content > .row > .col-xs-12 > div';
//non doctype case in split layout
var nonDoctypeSplitView = '.vsplit_bottom_margin > tbody > tr';
//non doctype case in row (non split) layout
var nondoctypeRowView = '.wide > tbody > tr';
var category_form_id = 'element.' + '$[jvar_table_name]' + '.u_kb_category';
category_link_id = 'button[id="lookup.' + '$[jvar_table_name]' + '.u_kb_category"]';
var init = function(selector) {
$(selector).each(function() {
if ($( this ).attr('id') == category_form_id){
$( this ).find(category_link_id)
.removeAttr("data-type" )
.removeAttr("onclick" )
.attr("type", "button")
.click(openDialog)
.removeAttr("title" )
.attr("title", '$[jvar_msg_lookup_picker]' )
.removeAttr("aria-label" )
.attr("aria-label", '$[jvar_msg_lookup_picker]' );
}
});
};
//override of deafault onclick event will happen only for one selector that exixt on the page
init(doctypeSplitView);
init(doctypeRowView);
init(nonDoctypeSplitView);
init(nondoctypeRowView);
});
</script>
</j:jelly>
"kb_categories_dialog_2" UI page HTML code:
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<g:requires name="scripts/lib/jquery_includes.js"/>
<g:include_script src="jquery_columnview.jsdbx"/>
<g:evaluate var="jvar_json" copyToPhase2="true">
var kb_knowledge_base = RP.getWindowProperties().get('kb_knowledge_base');
var disable_editing = RP.getWindowProperties().get('disable_editing');
var jsonStr = new SNC.KnowledgeHelper().getJSONCategories(kb_knowledge_base);
var escape_text = gs.getProperty('glide.ui.escape_text');
if (escape_text == 'false'){
jsonStr = GlideStringUtil.escapeHTML(jsonStr);
}
jsonStr;
</g:evaluate>
<p id="err_message" class="notification notification-error" style="display:none"></p>
<div style="padding: 6px">
<div id="kbCategoriesJSON" style="display:none">$[jvar_json]</div>
<input type="hidden" id="categoryId" value="" />
<div id="dialog_buttons" align="left">
<g:dialog_buttons_ok_cancel ok="setCategory()" ok_style_class="btn btn-primary category-ok-btn" ok_type="button" cancel_type="button" />
</div>
</div>
<!-- placeholders to read the icon char code from by name. -->
<span class="icon-edit" style="visibility:hidden; position:absolute;"/>
<span class="icon-chevron-right" style="visibility:hidden; position:absolute;"/>
<span class="icon-chevron-left" style="visibility:hidden; position:absolute;"/>
</j:jelly>
"kb_categories_dialog_2" UI page client script code:
var catBeingCreated = 0, //flag
closePickerClicked = 0; //flag
// Update the form
function setCategory() {
if(!catBeingCreated){
var id = $j('#categoryId').val();
g_form.setValue('u_kb_category', id);
GlideDialogWindow.get().destroy(); //Close the dialog window
}
else
closePickerClicked = 1;
}
function getCompatibleColumnWidth() {
if (window.frameElement && window.frameElement.id == "dialog_frame")
return (window.frameElement.getWidth() * 0.75); // Resize if displayed from a dialog
else
return 700; // Default value for content width
}
$j(function() {
var kbCategoriesJSON = $j('#kbCategoriesJSON');
var kbCategoryId = g_form.getTableName() + '.u_kb_category';
kbCategoriesJSON.columnview({
jsonData: kbCategoriesJSON.html(),
maxWidth: getCompatibleColumnWidth(),
idValue: gel(kbCategoryId).value || false,
blockEditing: $[disable_editing]
}).bind({
columnview_select: function (ev, obj) {
alert("Here!"); // This code should run each time a row is clicked in the picker list; it behaves like that for the original picker.
//$j("#categoryId").val(obj.id); // hidden field located at component level: gets destroyed with the component.
},
columnview_create: function(ev, obj) {
// root nodes don't have a parent: use the KB article's knowledge base ID
var kbElementVal = null;
if(g_form.getControl("u_kb_knowledge_base")){
kbElementVal = g_form.getValue("u_kb_knowledge_base");
}else{
return;
}
catBeingCreated = 1; //setting flag
var id = (obj.id) ? obj.id : kbElementVal;
var ga = new GlideAjax('KBCategoryCrud');
ga.addParam('sysparm_name','create');
ga.addParam('sysparm_label', obj.value);
ga.addParam('sysparm_is_root', (obj.id ? 'false' : 'true'));
ga.addParam('sysparm_id', id); // ID of parent
ga.getXML(function(serverResponse) {
if(serverResponse.status > 199 && serverResponse.status < 300){
var result = serverResponse.responseXML.getElementsByTagName("result");
var id = result[0].getAttribute("sysId");
$j("#categoryId").val(id);
if ($j.isFunction(obj.callback)) {
obj.callback(id, obj);
}
catBeingCreated = 0;
if(closePickerClicked) // in case 'OK' button was already clicked
setCategory(); //set the new category and then close
}
else{
console.log("ERROR: Error in creating the new category. Please check the server response below.");
console.log(serverResponse);
catBeingCreated = 0;
if(closePickerClicked) // in case 'OK' button was already clicked
setCategory(); //set the new category and then close
}
});
},
columnview_update: function(ev, obj) {
var ga = new GlideAjax('KBCategoryCrud');
ga.addParam('sysparm_name','update');
ga.addParam('sysparm_id', obj.id); // ID of self
ga.addParam('sysparm_label', obj.value);
ga.getXML(function(serverResponse) {
var result = serverResponse.responseXML.getElementsByTagName("result");
$j("#categoryId").val(obj.id);
var currentId = g_form.getValue('u_kb_category');
if (currentId === obj.id) {
// refresh the field when the user updates the label of same category.
g_form.setValue('u_kb_category', obj.id);
}
if ($j.isFunction(obj.callback)) {
obj.callback(obj.id, obj);
}
});
}
});
});
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎06-10-2020 12:57 PM
Had the same problem.
In UI Scripts clone "jquery_columnview" script and rename it.
In cloned UI Script search for "#body_kb_categories_dialog" and change it to "#body_<name of your dialog>" - in your case change it to #body_kb_categories_dialog_2
In UI Page HTML change include part to the changed name of the UI Script.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎06-10-2020 12:57 PM
Had the same problem.
In UI Scripts clone "jquery_columnview" script and rename it.
In cloned UI Script search for "#body_kb_categories_dialog" and change it to "#body_<name of your dialog>" - in your case change it to #body_kb_categories_dialog_2
In UI Page HTML change include part to the changed name of the UI Script.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎06-16-2020 06:30 AM
Thanks so much for your help Sam, this was the missing piece!
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-05-2024 05:55 PM
Wow was banging my head. Thanks for the solution!
Jim
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎03-25-2024 05:05 AM
I was doing exactly the same but the dialog was not initiated at all,
I see no errors in the console or anything
event the decorations itself seems not to be triggered or its breaking on the beginning
as it should state on the loop icon "Lookup using picker" but it says "Lookup using list" ans opens a standard reference list dialog ..
what could be the reason for that ?