
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-07-2023 01:18 PM
Hello,
I am trying to extend the script include 'SCPopularItems'. This script include is not read only, but there is only one method contained in it that I want to override. We'd like to leave 'SCPopularItems' OOB so we can continue to receive updates to it during upgrades.
I've looked at two official docs from ServiceNow on this as well as countless Community articles.
Right now the only difference (for testing purposes) between 'SCPopularItems' and 'MySCPopularItems' is I've placed a gs.log statement within MySCPopularItems to see when it's triggered. I do have additional customizations to it that I will place, but wanted to just prove out whether it will override the method contained within the OOB script include.
Can anybody tell me why this isn't working?
I've tried uncommenting and commenting the three lines of code for 'initialize: function()...'
New script include name: 'MySCPopularItems'
var MySCPopularItems = Class.create();
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-13-2023 10:25 PM
Sorry for the delay in responding, but I'm glad my previous response triggered the solution, as it was exactly what I was going for 🙂 I hate simple "do this" "answers".
When we create a Script Include that "overrides" another one, it doesn't automagically override it. We must use the new class in order for the overrides to apply. That is what I figured was the issue you were having as you did not mention using the new class name in your code.
We actually did this for a client with the OOTB Script Include used to create new User records because we wanted to use our own set of temporary bilingual passwords instead of the set used by SN. We created the new Script Include and pointed a couple of lines of code to use it so that the new passwords would be used but not change the rest of the functionality of the Script Include.
It's kind of hard to decide which method is "best": override/clone an OOTB record or modify it. I struggle with that all the time. Overriding or cloning something helps on upgrades because you do not have to review skipped records and you'll get any updated code automatically. BUT, at least the skipped record is a trigger for you to look at something that has potentially changed. Maybe you don't actually want the new code to be used because your code is important. Maybe you changed something and the newest SN code actually works the way you want it to now but you'd never know it unless you actively look at the code that you've cloned or overridden (which is not very likely). This gives you a means to revert back to the OOTB code. You can't compare code from 2 different records in SN directly, so you'd have to do it manually.
Another thing, like you mentioned, is some new code may have been introduced that uses the "old" OOTB code that you would prefer use the new Script Include you created. You'd have to actively search for those on upgrades to be sure your new code is used.
I know someone will jump in and say "best practice" says you never touch OOTB code. Well, I prefer the term "good practice", as the "best" way to do things is not the best way for everything/everyone/all the time. There is very rarely an absolute answer to everything.
Please remember to mark any response as being the correct one or helpful to you in solving the problem.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-11-2023 12:13 PM
Hi,
I appreciate you responding, but I feel my script include and explanation in my original post is very similar to what you showed. Assuming I don't have a blatant error in mine (totally possible), I'm questioning whether it's possible to do what I'm trying to do without the script include I'm extending being read-only or some other requirement that I am missing. I've successfully used the 'helper' script includes OOB for overriding and extending multiple times without issues.
New script include name: 'MySCPopularItems'
My expected behavior is that the private method _getFromChannelAnalytics from within MySCPopularItems is used vs the one contained in the OOB script include SCPopularItems. Perhaps I can't override private methods? We've done it before though with OOB 'helper' script includes, so I assume I can.
var MySCPopularItems = Class.create();
MySCPopularItems.prototype = Object.extendsObject(SCPopularItems, {
initialize: function() {
SCPopularItems.prototype.initialize.call(this);
},
_getFromChannelAnalytics: function() {
gs.info("SBK MySCPopularItems _getFromChannel called");
gs.log("SBK MySCPopularItems _getFromChannel called");
var itemsResult = [];
var citm = 0;
var count = new GlideAggregate('catalog_channel_analytics');
count.setCategory('catalog_meta');
count.setGroupByFollowRef(false);
count.setOrderByFollowRef(false);
count.addAggregate('COUNT', 'catalog_item');
count.addQuery('catalog_item.u_exclude_from_popular', 'false'); //customization added
gs.info("SK Initialized");
gs.log("SK Initialized");
count.groupBy('catalog_item');
count.addEncodedQuery(this._basicQueryFilters[this._baseQuery] || '');
if (Array.isArray(this._allowedItemsList))
count.addQuery('catalog_item', "IN", this._allowedItemsList);
count.addNotNullQuery('catalog_item');
// Donot Include items ordered as part of orderguide
count.addNullQuery('order_guide');
count.addNotNullQuery('document_key');
count.orderByAggregate('COUNT', 'catalog_item');
count.query();
while (citm < this._itemsLimit && count.next()) {
var catItemId = count.getValue("catalog_item");
var catalogItemJS = new sn_sc.CatItem(catItemId);
var catItemDetails = this._itemSummaryFetcher(catalogItemJS);
if (!this._allowItem(catalogItemJS, catItemDetails))
continue;
if (this._itemValidator(catalogItemJS, catItemDetails)) {
var response = this._responseObjectFormatter(catItemDetails, catItemDetails.sys_class_name, count.getAggregate('COUNT', 'catalog_item'));
var massageWith = this._getMassageObject(catItemDetails.name, count.getAggregate('COUNT', 'catalog_item'));
itemsResult.push(this._massageResponseObject(response, massageWith));
citm++;
}
}
var guides = 0;
count = new GlideAggregate('catalog_channel_analytics');
count.setCategory('catalog_meta');
count.setGroupByFollowRef(false);
count.setOrderByFollowRef(false);
count.addAggregate('COUNT', 'sys_id');
count.groupBy('catalog_item');
count.addNotNullQuery('catalog_item');
count.addQuery('table_name', 'sc_cat_item_guide');
count.addEncodedQuery(this._basicQueryFilters[this._baseQuery] || '');
if (Array.isArray(this._allowedItemsList))
count.addQuery('catalog_item', "IN", this._allowedItemsList);
count.orderByAggregate('COUNT', 'sys_id');
count.query();
while (guides < this._itemsLimit && count.next()) {
var guideId = count.getValue("catalog_item");
catalogItemJS = new sn_sc.CatItem(guideId);
catItemDetails = this._itemSummaryFetcher(catalogItemJS);
if (!this._allowItem(catalogItemJS, catItemDetails))
continue;
if (this._itemValidator(catalogItemJS, catItemDetails)) {
response = this._responseObjectFormatter(catItemDetails, 'sc_cat_item_guide', count.getAggregate('COUNT', 'sys_id'));
massageWith = this._getMassageObject(catItemDetails.name, count.getAggregate('COUNT', 'sys_id'));
itemsResult.push(this._massageResponseObject(response, massageWith));
guides++;
}
}
itemsResult.sort(function(a, b) {
return a.order == b.order ? a.name.localeCompare(b.name) : -(a.order - b.order);
});
return (itemsResult.length > this._itemsLimit) ?
itemsResult.slice(itemsResult.length - this._itemsLimit, itemsResult.length) :
itemsResult;
},
type: 'MySCPopularItems'
});
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-11-2023 12:40 PM
Can you include the code you are using to call the new Script Include?

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-11-2023 02:05 PM
@Jim Coyne Your question prompted my resolution, I think. In the server script of the widget 'SC Category Page', it's using the OOB script include, 'SCPopularItems'. I cloned the widget and updated it to the new script include 'MySCPopularItems', which is working now. I didn't have to make any modifications to the new script include 'MySCPopularItems'.
When I've overridden the OOB script includes using the existing 'helper' script includes previously (I'm not sure what to call them), but for example when I add my customizations to TaskUtils for TaskUtilsSNC it automatically works. I never gave much thought to which script include was being referenced/called...I just looked at an OOB reference qualifier using taskUtils, and noted it's referencing the customer extensible one, which makes sense now.
Back to my original use case of trying to make upgrades easier - creating 'helper' script includes to avoid directly modifying (non-read protected) OOB ones - doesn't sound like I'm saving much effort here? If we modify them directly, we'll have to go through the extra review we're already doing when we upgrade, or we would have to customize everywhere we're calling the OOB script include to the newly customized one.
Would you agree or have anything to add?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-12-2023 08:43 AM
Hi Sarah,
If I understand your concern correctly on creating a new Class that overrides a private function in OOB SCPopularItems, a test shows that version of that private function runs depending on which class you instantiate. I added a gs.info(); line to that private function in SCPopularItems and ran the following test in Scripts Background:
var scpopitem = new SCPopularItems();
var result1 = scpopitem.generate();
gs.info("result1 = " + result1);
var myscpopitem = new MySCPopularItems();
var result2 = myscpopitem.generate();
gs.info("result2 = " + result2);
What I got follows:
*** Script: SCPopularItems: _getFromChannelAnalytics called.
*** Script: result1 =
*** Script: SBK MySCPopularItems _getFromChannel called
*** Script: SBK MySCPopularItems _getFromChannel called
QueryEventLogger: Invalid query detected, please check logs for details [Unknown field catalog_item.u_exclude_from_popular in table catalog_channel_analytics]
Invalid query detected, stack trace below [Unknown field catalog_item.u_exclude_from_popular in table catalog_channel_analytics]
... java stack dump
*** Script: SK Initialized
*** Script: SK Initialized
*** Script: result2 =
Which indicates to me, that you will not see a Skipped Update for the 'SCPopularItems' script include. You have added a custom version: "MySCPopularItems" that doesn't exist OOB.
I don't know how you call the custom version. For me to get the custom version, I had to create a 'MySCPopularItems()' object. When I used the OOB Object (SCPopularItems) your private function was not called. It seems if you want the custom version, you will need to update everywhere you are calling the OOB version. And those changes may be needed in OOB 'items' that will get skipped on upgrade. Seems to me a different design is needed to avoid this. Maybe create your own versions of 'items' that use the new private function, and direct users to the new version. Then upgrades won't affect your customizations. But you may want to check the OOB version for changes that may apply to your customized version.
I found:
for Servicnow's extension, based on:
https://javascript.info/class-inheritance
I'm new at class inheritance, I hope I've helped. Others with more experience may contribute here.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
07-13-2023 10:25 PM
Sorry for the delay in responding, but I'm glad my previous response triggered the solution, as it was exactly what I was going for 🙂 I hate simple "do this" "answers".
When we create a Script Include that "overrides" another one, it doesn't automagically override it. We must use the new class in order for the overrides to apply. That is what I figured was the issue you were having as you did not mention using the new class name in your code.
We actually did this for a client with the OOTB Script Include used to create new User records because we wanted to use our own set of temporary bilingual passwords instead of the set used by SN. We created the new Script Include and pointed a couple of lines of code to use it so that the new passwords would be used but not change the rest of the functionality of the Script Include.
It's kind of hard to decide which method is "best": override/clone an OOTB record or modify it. I struggle with that all the time. Overriding or cloning something helps on upgrades because you do not have to review skipped records and you'll get any updated code automatically. BUT, at least the skipped record is a trigger for you to look at something that has potentially changed. Maybe you don't actually want the new code to be used because your code is important. Maybe you changed something and the newest SN code actually works the way you want it to now but you'd never know it unless you actively look at the code that you've cloned or overridden (which is not very likely). This gives you a means to revert back to the OOTB code. You can't compare code from 2 different records in SN directly, so you'd have to do it manually.
Another thing, like you mentioned, is some new code may have been introduced that uses the "old" OOTB code that you would prefer use the new Script Include you created. You'd have to actively search for those on upgrades to be sure your new code is used.
I know someone will jump in and say "best practice" says you never touch OOTB code. Well, I prefer the term "good practice", as the "best" way to do things is not the best way for everything/everyone/all the time. There is very rarely an absolute answer to everything.
Please remember to mark any response as being the correct one or helpful to you in solving the problem.