UI Message in Workspace List View Appears on Next Transaction Instead of Current
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Saturday
Hi All,
I have an existing functionality in classic UI which displays a UI message in list view when there are restricted records that an itil user should not see.
Problem:
In classic UI, it is working fine.
In workspace list view I am facing the below issue:
If I open Incident > Assigned to me list (which has restricted records), the restriction UI message does not show immediately. But when I switch to another list tab (e.g., Incident > Opened by me, which has no restriction), the restriction message from the previous list pops up.
It seems like the message is always getting displayed on the next transaction/tab click, instead of the current list I am viewing.
The scripts that I have used,
Script Include: c_RestrictedUtils (non-client callable)
var c_RestrictedUtils = Class.create(); c_RestrictedUtils.prototype = { initialize: function() { this.groupNamesToContact = ''; this.isSingleRecord = false; this.amountRestricted = 0; this.allRecordsRestricted = false; }, hasRestrictions: function(grListOrRecord, queryRP) { var showMessage = false; var isGroupedList = false; var listAmount = 0; var listQuery = ''; var tableName = ''; this.groupNamesToContact = ': '; if (typeof grListOrRecord == 'object' && typeof queryRP == 'object' && gs.getUser().hasRole('itil')) { isGroupedList = (grListOrRecord.getRowNumber() == -1); tableName = grListOrRecord.getTableName(); listAmount = grListOrRecord.getRowCount(); // either the record amount or the grouped amount if (queryRP != null) { // only for Lists, null for Forms listQuery = queryRP.toString(); } switch (true) { case (listAmount == 0): // no additional actions (not sure when it happens, yet saw it while logging) showMessage = false; break; case (listAmount == 1 && !isGroupedList): // non grouped list with one record or form of the record this.isSingleRecord = true; showMessage = this._checkCanReadOneRecord(grListOrRecord); break; case (listAmount > 1 && !isGroupedList): if (this._checkAccess(listAmount)) { // 100 or less loop through records to determine access showMessage = this._checkCanReadMultipleRecords(tableName, listQuery); this.allRecordsRestricted = (listAmount == this.amountRestricted); } break; case (isGroupedList): // list is grouped, we need to get actual amount of records, not just amount of groupings // as all task tables have more than 100 records => only check when list is filtered by user listQuery = this._cleanQuery(listQuery); // remove order & group by from query if (listQuery != '') { // list has a query => check record amount recordAmount = this._getRealAmount(tableName, listQuery); if (this._checkAccess(recordAmount)) { // 100 or less loop through records to determine access showMessage = this._checkCanReadMultipleRecords(tableName, listQuery); this.allRecordsRestricted = (recordAmount == this.amountRestricted); } } break; default: // unforeseen scenario's showMessage = false; } } return showMessage; }, bannerMessage: function() { var msgParameters = []; this.groupNamesToContact = this.groupNamesToContact.replace(": , ", ": "); // remove comma after colon msgParameters.push(this.groupNamesToContact); msgParameters.push(this.amountRestricted.toString()); var returnMessage = ''; switch (true) { case (this.isSingleRecord): returnMessage = gs.getMessage('ui_action_restricted_ticket_message_single', msgParameters); break; case (this.allRecordsRestricted): returnMessage = gs.getMessage('ui_action_restricted_ticket_message_multiple_all', msgParameters); break; default: returnMessage = gs.getMessage('ui_action_restricted_ticket_message_multiple', msgParameters); } return returnMessage; }, _checkCanReadOneRecord: function(grTicket) { var isRestricted = false; var grpName = grTicket.assignment_group.getDisplayValue(); if (!grTicket.canRead()) { isRestricted = true; this.isSingleRecord = true; this.groupNamesToContact += grpName; } return isRestricted; }, _checkCanReadMultipleRecords: function(tableName, encodedQuery) { var hasRestricted = false; var grRecords = new GlideRecord(tableName); grRecords.addEncodedQuery(encodedQuery); grRecords.query(); while (grRecords.next()) { if (!grRecords.canRead()) { // record not visible, store group name when not already stored this.amountRestricted++; var grpName = grRecords.assignment_group.getDisplayValue(); if (!this.groupNamesToContact.includes(grpName)) { this.groupNamesToContact += ', ' + grpName; } } } hasRestricted = (this.amountRestricted != 0); return hasRestricted; }, _getRealAmount: function(tableName, encodedListQuery) { var recordCount = 0; gaRecords = new GlideAggregate(tableName); gaRecords.addAggregate('COUNT'); gaRecords.addEncodedQuery(encodedListQuery); gaRecords.query(); if (gaRecords.next()) { recordCount = gaRecords.getAggregate('COUNT'); } return recordCount; }, _cleanQuery: function(queryString) { var qry = ''; if (queryString) { qry = queryString; if (qry.includes('^ORDERBY')) { qry = qry.substring(0, qry.indexOf('^ORDERBY')); } if (qry.includes('ORDERBY')) { qry = qry.substring(0, qry.indexOf('ORDERBY')); } if (qry.includes('^GROUPBY')) { qry = qry.substring(0, qry.indexOf('^GROUPBY')); } if (qry.includes('GROUPBY')) { qry = qry.substring(0, qry.indexOf('GROUPBY')); } } return qry; }, _checkAccess: function(amount) { var continueCheck = false; if (amount) { switch (true) { case (amount == 0): // no results in list, no need to check continueCheck = false; break; case (amount == 1): // to have proper message this.isSingleRecord = true; continueCheck = true; break; case (amount <= 100): this.isSingleRecord = false; continueCheck = true; break; default: // no action needed } } return continueCheck; }, type: 'c_RestrictedUtils' };
Called from a UI Action with condition:
var msgUtil = new c_RestrictedUtils(); if (!RP.isRelatedList() && RP.isInteractive() && msgUtil.hasRestrictions(current ,RP.getEncodedQuery()) && gs.getUser().hasRole('itil')) { var msg = msgUtil.bannerMessage(); gs.addInfoMessage(msg); }
This works as expected in classic UI.
Now I am trying to replicate the same functionality in CSM Workspace.
I created a wrapper Script Include: RestrictedUtilsDLA (non-client callable), which calls the same logic of c_RestrictedUtils and url check
var RestrictedUtilsDLA = Class.create(); RestrictedUtilsDLA.prototype = { initialize: function() { // No initialization needed for now }, evaluateRestriction: function(current) { // Only run restriction logic for interactive user sessions if (!gs.getSession().isInteractive()) { gs.info('RestrictedUtilsDLA: Non-interactive session, skipping restriction.'); // return false; } // Clear leftover messages from previous transaction gs.clearInfoMessages(); var transaction = GlideTransaction.get(); var referer = ''; // Attempt to get Referer header from current request if (transaction && transaction.getRequest()) { referer = transaction.getRequest().getHeader('Referer') || ''; gs.info('RestrictedUtilsDLA: Current transaction referer header: ' + referer); // Store Referer header in session for consistency across requests gs.getSession().putProperty('referer', referer); } else { // Fallback: retrieve Referer header from session property if current request unavailable referer = gs.getSession().getProperty('referer') || ''; gs.info('RestrictedUtilsDLA: Retrieved referer from session property: ' + referer); } // Extract list_id from the Referer URL var listIdMatch = referer.match(/list-id\/([a-z0-9]+)/i); var list_id = listIdMatch ? listIdMatch[1] : ''; if (!list_id) { gs.info('RestrictedUtilsDLA: No list_id found in referer.'); return false; } var grList = new GlideRecord('sys_ux_list'); if (!grList.get(list_id)) { gs.info('RestrictedUtilsDLA: sys_ux_list record not found for list_id: ' + list_id); return false; } var listTable = grList.getValue('table'); gs.info("RestrictedUtilsDLA: list ID : " + list_id + " | table : " + listTable); if (listTable !== current.getTableName()) { gs.info( 'RestrictedUtilsDLA: List table "' + listTable + '" does not match current table "' + current.getTableName() + '", skipping restriction.' ); return false; } var encodedCondition = grList.getValue('condition') || ''; gs.info('RestrictedUtilsDLA: List encoded condition: ' + encodedCondition); var grCheck = new GlideRecord(listTable); if (encodedCondition) { grCheck.addEncodedQuery(encodedCondition); } grCheck.query(); // Create fresh util instance per evaluation to prevent stale state var util = new c_RestrictedUtils(); util.groupNamesToContact = ''; util.isSingleRecord = false; util.amountRestricted = 0; util.allRecordsRestricted = false; var restricted = util.hasRestrictions(grCheck, encodedCondition); gs.info('RestrictedUtilsDLA: hasRestrictions result: ' + restricted); if (restricted && gs.getUser().hasRole('itil')) { var bannerMsg = util.bannerMessage(); gs.info('RestrictedUtilsDLA: Adding banner message: ' + bannerMsg); gs.addInfoMessage('[BR_TABLE:' + current.getTableName() + '] ' + bannerMsg); return true; } return false; }, type: 'RestrictedUtilsDLA' };
Added a Declarative Action – List Action [Name: error_message] in Workspace.
In the Advanced condition script of the Declarative Action, I added:
new global.RestrictedUtilsDLA().evaluateRestriction(current) && false;
With this setup, the UI message does appear, but the issue is that the response is delayed.
Problem:
If I open Incident > Assigned to me list (which has restricted records), the restriction UI message does not show immediately. But when I switch to another list tab (e.g., Incident > Opened by me, which has no restriction), the restriction message from the previous list pops up.
It seems like the message is always getting displayed on the next transaction/tab click, instead of the current list I am viewing.
Has anyone faced this delayed UI message issue in Workspace declarative actions?
Is there a way to make the restriction message display immediately for the current list transaction?
Any suggestions are appreciated.
Thanks in advance!