Skip navigation

Free Widgets.png

I'm excited to announce the launch of a new free widget download page on ServicePortal.io.

 

Since day 1, ServicePortal.io has existed to be a resources and source of inspiration for the ServiceNow Community. With the launch of the new widgets page, we can now share some of the most frequently requested widgets with all of our followers. Not all of them will be complex, many of the most frequently used widgets are pretty simple. Our goal is to develop and give away at least one new widget per month.

 

If you haven't already checked it out, head on over to https://ServicePortal.io

--------------------------------
Nathan Firth
Principal ServiceNow Architect
nathan.firth@newrocket.com
http://newrocket.com
http://serviceportal.io

If you're an ancient & decrepit geezer like me and grew up in the 1980's, you probably remember seeing advertisements for these in your Archie comics...

XRay specs!  Giving you vision into untold secrets.

 

 

This month, I rediscovered an administrative "XRay Spec" for ServiceNow:  Show XML.  With it, you can see the invisible, and glean valuable secrets.  Here's 3 use cases to capitalize on Show XML

 

USE CASE 1:  DISCOVERY OF INVALID REFERENCES

Ever been in this situation?  You're looking at a form with a blank reference field, but for some reason the preview icon is still visible?

It happens when you have an invalid reference.  There's an invalid sys_id in the Affected CI field, or a record that has a sys_id but no display.  You'll see this in cases like...

- When you're populating stuff via manual sys_id's in a record producer or inbound action

- When you've used bad code migration strategies and have different sys_id's between instances THEN imported/exported data depending on those references.

- A rookie has populated a reference field in a script by using display value instead of sys_id

 

So lets use Show XML to figure out what's wrong.... (Right click in header -> Show XML).

This pops open a new window with the entire record in XML format.

OOPS!  Somehow Affected CI populated without a sys_id!

 

 

USE CASE 2:  FINDING THE SYS_ID OF A RECORD LIKE A BOSS!

You're looking at an incident for Prostetnic Vogon-Jeltz.  For whatever reason, you need his sys_id.  How do you get it?

 

If you said "click the preview, then right-click the user record header & copy sys_id" you're correct, but also slowShow XML right from the Incident!

 

USE CASE 3:  FINDING VALUES OF FIELDS NOT ON THE FORM

How many times have you needed to see the value of a field and actually modified the form to get access to it?  Don't be ashamed, we've all done it (but go ahead and slap yourself once, just to be sure).  If you've ever put a field on a public form exclusively for admin usage, slap yourself twice.  From now on, Show XML is all you need.  Lets say I have an eBonding relationship with a vendor.  I don't have correlation ID on the form, but I want to know if my Incident is related to an external ticket.  Right Click -> Show XML.

HOLY COW!!

 

SUMMARY

Remember... with Show XML, the *entire* record is at your fingertips.  Why bother with background scripts, form/list personalization, or record transitions when that powerful little Action is just a click away!

It's always a good idea to setup your different instances with unique color schemes so you can quickly identify which one you are in.  Very useful, obviously, for developers and admins who are jumping around between the different instances all the time.

 

However, sometimes your training or documentation teams need to take screenshots of a sub-production instance in order to document some new feature before release and they want to use the same colors as production because, well, that's the one your users will be using.

 

Instead of having to bug your admins to temporarily change the colors in the sub-prod instance and confuse everyone, just create a Theme record with the same colors as production.  All that is needed in the CSS field is the particular variables/properties that you've changed in production from the standard OOB values.  Here's an example Theme I setup for a client where we simply changed the strip of color just under the banner to match their corporate colors:

 

/* Theme to simulate production colors for documentation purposes */
/* Remember the Clone Data Preserver if you change the name of the Theme */

$navpage-header-divider-color:  #00467f

 

I created the Theme record as well as a Clone Data Preserver to preserve the sub-production versions of the record because I then set the record to inactive in production so it does not appear in the list of themes to choose from (would be a little weird).

 

Now your training and documentation teams can switch at will without affecting everyone else.

 

*** Please Like and/or tag responses as being Correct.
And don't be shy about tagging reponses as Helpful if they were, even if it was not a response to one of your own questions ***

Question: What's worse than code that blows up?

Answer:   Code that blows up silently.

 

There are lots of pithy quotes and proverbs about assumptions, but I have a favorite:

 

     ASSuME: Assumptions make an a** out of you and me.

 

When we make assumptions about things like things like data quality, our code can wind up at a dead-end.

 

For example, if you were tasked with writing a ServiceNow function to find the ID of the first VP above an employee, you might assume that you could use the following:

 

 

// @return string sys_id for CIO
// @param  GlideRecord pointing to a user
function getCioUserId(grItEmployee){

      var manager = grItEmployee.manager;

      if( /VP/.test(manager.title )){
            return manager.sys_id;
      } else {
            return getCioUserId(manager);
      }
}

 

But that means that every employee in the chain must have a manager, or else the function will go into infinite recursion.  That bad assumption (that every ServiceNow user has a manager) is a fatal flaw for this piece of code.

 

Sometimes the code can fail in a foreseeable way.  We can plan for such failures by wrapping our code in a try/catch block;

 

Naive approach (counter example):

 

function getApprover(){
     return null;
}
 var approver = getApprover();
 var fn = approver.first_name;

 

 

 

Notice that this issue causes the script to error out and halts all code execution.   If this were to happen in a Workflow activity, then the activity would turn red and the entire item's Workflow would be dead.

 

But If instead we were to wrap the code in a try/catch block, then we can cause the code to fail gracefully, and even retry using another method.

 

var fn;
var approver;
try{ 
     approver = getApprover();
     fn = approver.first_name;
} catch(e){     
     if(/null/.test( e.message )){ // Error message contains 'null'
           approver = getApproverADifferentWay()
           fn = approver.first_name;
     }
}

 

 

Take another example involving a Regular Expression.   If the execution does not yield any match, then our attempt to retrieve the first match is going to fail.

 

try{ 
     // Put code here in case something goes wrong.
     var re = new RegExp('(bar)');
     var someStringToTest = 'foo_baz';
     var m = re.exec(someStringToTest);
     gs.print(m[1]);
} catch(e){     
     gs.error(e.message);
}

 

Since the regex matches zero times, the assumption that there would be something in the array of matches turned out to be a dangerous one.

 

In this case, we don't have a retry; but the catch block gives us a nice way to quickly know what went wrong by logging the error.

 

I take this so far as to break things up into multiple try / catches within the same function, so that I can identify exactly what I was trying to do when the error was thrown. There is nothing more frustrating that receiving "could not access property 'z' from null" or "'undefined' is not an object" error in a lengthy function.

 

Log: Script x broke when you tried to access a property on something that doesn't exist."

Me:  "Great.  Now all I have to do is figure out which dot (on the myriad of possibilities) is actually trying to dereference an object that is undefined.

 

But if we surround different sections of our code with individual try / catch blocks, our code can actually tell us exactly what went wrong.

 

function x(){       
     var anAppleObject = new Orchard(1).pickAnApple();
     var anOrangeObject = new Orchard(2).pickAnOrange();
     try{
          anAppleObject.bite();
     } catch(e){
          gs.addErrorMessage('Exception throw while biting apple:'+e.message);
     }

     try{
          anOrangeObject.bite();
     } catch(e){
          gs.addErrorMessage('Exception throw while biting orange:'+e.message);
     }
}

 

It's a silly example, but it makes the point.   If one of the objects turns out to be undefined, you'll know which one right away.

 

Wrapping all code in try/catch blocks is a foundational best practice that greatly decreases time to troubleshoot issues.   The practice also seems to facilitate better code by forcing us to consider what could go wrong, and what the right response to an error should be.   In our organization, code that is not wrapped in a try/catch block will not pass a code review.   The payoff to the simple discipline of surrounding all of your code with these try/catch constructs cannot be overstated.   Yes, you can still track down the error without this, but the ability to track down precisely what went wrong quickly is greatly enhanced when you log errors strategically.

 

Once you make the decision to embrace a proactive error-handling stance, there are still some practical considerations.   "How do I embrace this practice without making it too cumbersome?"   Syntax Editor Macros to the rescue.

 

I have 2 Syntax Editor Macros so that quickly insert try/catch blocks into any place where I'm writing a script inside of ServiceNow.

 

Using try/catch is super simple for server-side code:

 

// stry: stands for server-side try
try{ 
     // Put code here in case something goes wrong.
} catch(e){
      if(gs.isInteractive() && gs.hasRole('admin')){
           gs.addInfoMessage('Name of module where error happened:'+ e.message);
      }
     gs.error(e.message);
}

 

It's a little more involved on the client, because we must invoke a GlideAjax call in order to get the message into the system log:

 

// ctry: stands for client-side try
try{ 
     // Put code here in case something goes wrong.
} catch(e){
      if(g_user.hasRole('admin')){
           alert('Name of module where error happened:'+ e.message);
      }
     logError(e.message, '<Script Source Goes Here>');
}

function logError(message, source) {
     var errorUtil = new GlideAjax('GMF_ClientLogUtil');
     errorUtil.addParam('sysparm_name', 'error');
     errorUtil.addParam('sysparm_errorMessage', message);
     errorUtil.addParam('sysparm_errorSource', source);
     errorUtil.getXML();//Add a callback if you want to verify lo
}

 

GMF_ClientLogUtil is a very simple client callable GlideAjax script include:

 

var GMF_ClientLogUtil = Class.create();
GMF_ClientLogUtil.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    //Log the error using server side error logging
    error: function () {
          var errorMessage = this.getParameter('sysparm_errorMessage');
          var errorSource = this.getParameter('sysparm_errorSource');
          gs.error(errorMessage,errorSource);
    },

    info: function(){
          var message = this.getParameter('sysparm_message');          
          gs.info(message);
    },

    warn: function(){
          var message = this.getParameter('sysparm_message');          
          gs.warn(message);
    }
});

 

 

So, having gone to all of this trouble, I find it very frustrating that ServiceNow does not always throw an Exception when it ought to.   I had a piece of code (nicely wrapped in try/catch blocks) silently fail recently.  When I went to the error log I found nothing.  Eventually, I looked at all system entries and found a "warning" that my function had exceeded the maximum capacity for the array data type.  Hmmm...  That seems more like an error to me.

 

I have occasionally bumped into these types of omissions in the platform.   But the one that really bothers me is this: A GlideRecord object's addQuery() method throws no error even when we pass a bad column name.   Given the egregious consequences that can result from such a silent error, I find this really unacceptable.  (I worked on a team where a global ServiceNow project was threatened by a silent column-name typo.)   So the GlideRecord class has a serious deficiency.  We simply must know about errors involving GlideRecords.

 

This need led me to create the u_GlideRecord class (see attachment).

 

 

 

 

// Using u_GlideRecord throws an exception when we pass a bad column name
try{
     var gr = new u_GlideRecord("sc_task");
     gr.addQuery( 'snort_description', 'Complete prescribed work' );  // Should be short not sNort
     gr.setLimit(1);
     gr.query();
     while ( gr.next() ) {
          gs.print( gr.number );
     }
} catch(e){
     gs.print(e.message);
}

 

 

ServiceNow Notify is a great feature that provides additional communication vehicles to the platform via SMS and voice; the datasheet can be downloaded here.  Included with this plugin is Notify on Task which provides the ability to send an SMS message and initiate a conference call from any task within your instance.  This includes incidents, problems, changes, and any table that extends task.  The only issue is out of the box it only works for individual users and you cannot leverage groups, which is a common requirement. 

 

Recently posted on Share is an update set that extends Notify on Task that adds the ability to send SMS messages and initiate conference calls to groups.

Notify on Task for Groups

 

This update set adds a Groups section at the bottom of the popup as show above.  If On-Call Scheduling is enabled in your instance, you can also elect to only include the current on-call person for the selected groups versus all the members of the group.  This option will not appear if On-Call Scheduling is not activated within your instance.  Since group managers may not always be members of the group, there is an option to include the group manager as well.  Duplicates are automatically removed with the out of the box code so users won't receive more than one message or call.

 

Note: It is important to note that several out of the box components are being modified with this update set so please be aware of this as you upgrade your instance.

 

Enjoy!


Please mark this post or any post helpful or the correct answer so others viewing can benefit.

This week's podcast brings in the winners of ServiceNow's 2017 Hackathon, Lisa Slavin, Katrina Miller, and Tom Conklin, to talk about how they created an learning management system (LMS) in only one day.

 


volume_icon.png

Listen

 

 

Subscribe

 

to iTunes

 

This episode covers:

  • ServiceNow Hackathon
  • LMS and what it does
  • Developing an app
  • Issues and challenges
  • Tips and tricks for developing your own app

For more information on developing applications, see:

 

Your feedback helps us serve you better! Did you find this podcast helpful? Leave us a comment to tell us why or why not.

 

LISTEN BELOW

 

 

To catch clips behind the scenes with our podcast guests and find out what topics we'll be covering next before they are posted, follow @NOWsupport on Twitter. You can also search Twitter using the hashtag #SNtechbytes for all previous podcasts, video clips, and pictures.

Embedded Help and Guided Tours are getting a boost in the Jakarta release to assist users. While both have been available since the Helsinki release, with Jakarta comes the introduction of the Embedded Help app, allowing administrators to create customized versions of both Embedded Help and Guided Tours. As you might expect, we are as excited about this as you are!

 

2016-12-26_0-36-01.png

 

The new application, called Embedded Help, includes the following modules: Help Content, Role Priority, and includes the Guided Tour Designer, allowing you to create guided tours and list existing ones.

embedded help modules.png

 

What is Embedded Help?

Embedded Help provides focused help to a user in a UI page based on their role. When you click the help icon, Embedded Help content appears in the right sidebar. If there is embedded help for the current UI page, the help icon has this indicator, showing there is embedded help available:

Embedded help indicator on help icon

Organizations now have the ability to add or replace custom embedded help content to suit their business needs.

If embedded help is not available for a topic, the User Guide and documentation site search is available in the sidebar.

embedded-help-catalog-task.png

 

If you click the Search Documentation link, a documentation search is performed for the page you started from. For example, if you click Search Documentation from the Catalog Tasks list view, the search term becomes Catalog Task, and any matching information is listed.

 

What are the benefits of Embedded Help?

Embedded Help provides help for custom applications, focused instructions aligned with specific business purposes, embedded help content where it currently does not exist, and different content based on different roles.

 

Additional information available for administrators:

 

What is a Guided Tour?

Guided Tours demonstrate how to use a feature, using different elements to provide steps, instruction, and how to move along within the guided tour. They show you how to do something within the instance, such as how to perform a task.

 

Using the Guided Tour Designer, administrators can now create customized guided tours. An administrator creates a guided tour within the instance. If the guided tour shows you how to insert a record, at the end of the tour the record now exists in the database. For example, if a manager completes a guided tour to create a department report, that report becomes available to everyone in that department.

 

Parts of a Guided Tour include: steps which provide definition or instruction, callouts to indicate current steps and how to proceed with additional instruction, and triggers to move the tour along, like clicking Enter or Next.

guided tour complete.png

In the above example, each of these elements exist. The step is the action. For example, the Delegates list opens, with the new record added. The callout is the box with the upward pointing arrow box explaining what has just been accomplished, or the action that has just been completed. The trigger is the Done button, concluding the tour.

 

Each callout should be selected based on the direction it is pointing; it must touch the element it is assigned to.

 

To access an existing guided tour, navigate to a page with a guided tour. If your role is assigned to the tour, the Help panel opens and the Take a Tour button is available at the bottom. If there is more than one tour available to choose from, like the image below, just select the correct tour from the list. Tours might also be available to various roles for access and availability.

 

guided tour.png

 

Additional Information available for administrators:

 

Customizing Embedded Help and Guided Tours allow administrators to help employees perform their jobs better and more efficiently. Updating and modifying embedded help provides targeted assistance to a user. Creating customized guided tours let administrators demonstrate step-by-step walkthroughs, navigating through the details of what the user is doing.

 

Looking for more information? Check out the Embedded Help Release notes for Jakarta, or watch a Guided Tour unfold with josh.nerius' Getting Started with the Guided Tour Designer in Jakarta.

There is a functionality in GlideRecord such that you can end up querying the entire table instead of a specific subset of records if an invalid query is accidentally passed into GlideRecord query methods. When the invalid query is run, the invalid part of the query condition is dropped, and the results are based on the valid part of the query, which may return all records from the table. Using an insert(), update(), deleteRecord(), or deleteMultiple() method on bad query results can result in data loss! The good news is that there is a quick and easy way to avoid this. Simply set the glide.invalid_query.returns_no_rows system property to true to return no rows (instead of all rows) when the query is invalid.

 

Use this glide sys property to prevent data loss and other issues with custom scripts

Here is an example of where we can end up in a situation where we return all rows instead of specific ones because we don't validate the query parameter:

var a;
var gr = new GlideRecord("incident");
gr.addQuery("number", a);
gr.addQuery("caller", a)
gr.query();

while(gr.next()) {
     gs.print(gr.number);  //Printing all incident numbers but would expect 0 records back, if this called deleteMultiple() you could delete ALL incidents!
} 

To protect yourself from the above scenario, set the glide.invalid_query.returns_no_rows system property to true.

 

What is the glide.invalid_query.returns_no_rows system property?

The glide.invalid_query.returns_no_rows system property controls how invalid GlideRecord queries are handled. When this property is true, invalid queries always return no rows. When this property is false (default), if a query is incorrect, such as by including an invalid field name, the invalid part of the query condition is ignored and results are based on the valid part of the query. You can find more information about this sys property, as well as all the sys properties available in the platform, here: Available System Properties

 

How to set this system property

  1. Navigate to <instance_name>.service-now.com/sys_properties_list.do
  2. Create a New sys_properties record with the following parameters set:
    • Name: glide.invalid_query.returns_no_rows
    • Type: true | false
    • Value: true

new system property.png

By setting glide.invalid_query.returns_no_rows to true, you can prevent data loss and other issues with custom scripts.

SOAP is one of the available Web Services that the ServiceNow platform offers and supports to integrate your instance (A) with another instance or third-party applications (B) that support SOAP. This post focuses on why and how to debug the SOAP integration when there is any interruption identified. If you are not too familiar with how SOAP integration works in ServiceNow, I highly suggest you take a look at the product documentation before reading ahead: SOAP web service

 

Why and how to debug a SOAP integration

When SOAP debug is enabled, the platform generates additional logs that allow you to see the SOAP transactions, be it inbound or outbound. Let's consider that SOAP integration is set up between instance A and B for the incident management process. Therefore, when an incident (INC00001) is created/updated on the A side, we expect our integration to perform the same on the B side. But what if no such incident is created/updated on the B side? What went wrong?

 

This is where debugging can provide some answers. When inbound SOAP debug is enabled on the B side, you can see the SOAP communication sent by the A side in your logs. When outbound SOAP is enabled on the A side, you can see what SOAP communication was generated on the A side logs and posted towards the B side.

 

Debugging inbound SOAP

Enabling inbound SOAP debug allows you to view the following information:

  • The SOAP message received in a ServiceNow instance
  • Time when the SOAP message was received
  • Whether it includes all the mandatory inputs to insert/update the target record

 

There is a system property glide.processor.debug.soapProcessor of type Boolean type (true/false). If you don’t see this property in your instance, you can create it.

inbound_soap_debug_prop.png

When you set this property value as true, platform will start logging all inbound soap transactions to your instance with source value as SOAPProcessor as shown below:

inbound_soap_debug_logs.png

Debugging outbound SOAP

Enabling outbound SOAP debug allows you to view the following information:

  • The SOAP message sent from ServiceNow instance
  • Time when the SOAP message was sent
  • Whether it includes all the data that your integration party expects

 

Outbound SOAP Envelopes can be seen in two ways:

  1. Using ECC Queue
  2. Generate debug logs using system property

 

There is a system property glide.soap.outbound.debug of type Boolean (true/false). If you don’t see this property in your instance, you can create it.

outbound_soap_debug_prop.png

When you set this property value as true, the platform will start logging all outbound SOAP transactions in system logs, which can be seen from a related instance node using one of following log utilities:

  • System Logs > Utilities > Node Log File Download

 

2017-07-12 05:16:04 (117) Default-thread-25 E81F6F5F4F73B200826B098D0210C7A2 DEBUG: SOAP Msg Outbound - SOAPMessageClient : Executing synchronous request
2017-07-12 05:16:04 (117) Default-thread-25 E81F6F5F4F73B200826B098D0210C7A2 DEBUG: SOAP Msg Outbound - SOAPMessageClient : Prepared requestBody for soap request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap
/envelope/" xmlns:u="http://www.service-now.com/u_incident">
   <soapenv:Header/>
   <soapenv:Body>
      <u:insert>
         <active>true</active>
         <comments></comments>
         <short_description>Performance problems with CPU</short_description>
                 <description></description>
                 <correlation_id>SOAPINT00002</correlation_id>
                 <number>INC0020003</number>
      </u:insert>
   </soapenv:Body>
</soapenv:Envelope>
  • System Logs > Utilities > Node Log File Browser

outbound_soap_debug_logs.png

 

This is how SOAP debug assists in investigating issues and answering these questions:

  • Was the SOAP message sent successfully from the A side?
  • Was the SOAP message sent from the A side reached successfully on the B side?

Note: Enabling SOAP debug adds extra logging in system logs, but it helps and does not impact your instance performance, so you can decide whether to leave it active or enable it on demand. Just keep in mind that when you enable it on demand, it requires reproducing the issue again after enabling.

Related Links:

Brgds, LK

PS: Hit Like, Helpful or Correct if I was able to assist you

Ever since the "Genius Lounge" concept was created by Apple many years ago many companies, including ServiceNow internally, have created walk up style technical support, especially as BYOD has gotten popular.  While ServiceNow is a powerful platform to schedule and capture the details of a task/request, it lacks a nice feature rich UI that allows users to easily schedule an appointment similar to OpenTable's view of making a restaurant reservation.  That is until now!

 

This provides a "no-code" solution of defining how appointments should be scheduled and Service Portal widgets to allow users to schedule new appointments and view their existing appointments.

 

 

The ServiceNow platform offers several features that make this solution possible:

  • Feature Rich Task Table:
    • The task table is the base table for most applications within ServiceNow.  Incident, Problem, Change, Case, etc all extend Task.  This table includes common attributes for any type of task/case and extending it speeds up development time and it brings many features along with it such as SLAs, On-Call Scheduling, etc.
    • The task table includes Start and End attributes that various applications leverage for the scheduling of work.  Because of this, this Task Appointment Scheduling solution can be applied to any application within your instance that extends the task table.
  • Service Portal:
    • The Service Portal, introduced in the Helsinki release, has brought a whole new dynamic of creating responsive and feature rich UI's that improves the usability of ServiceNow.
    • Using AnjularJS and Bootstrap, reusable widgets can be created that can be easily leveraged on any page within your Service Portals.
    • The Service Portal widget is reusable in that each instance of the widget is tied to Task Appointment Definition record.  So you could have two widgets on a page where one creates an Incident appointment while another one creates a Change appointment.

 

Sush Chandrashekar(sush_c) and I partnered up to come up with a Task based solution that allows you to:

  • Define a schedule for accepting appointments and a duration of time for each appointment
  • Set which Task table to use to store the appointments.  This can be any table that extends task, including out of the box tables like Incident, Problem, Change, Case, etc, or any custom table.
  • Set how many concurrent appointments that can be scheduled before marking an appointment time unavailable.  Default is 1.
  • Set template values that will be applied when the appointment record is created.
  • Set the style of how you want the available appointments to be presented within the Service Portal:
    • Timecards
    • Chiclet Style Buttons
    • Dropdown

 

The Code:

The solution involves several components that is available for download from ServiceNow Share: Task Appointment SchedulingDownload the update set from Share, load/preview/Commit it, and then you can leverage it in your instance.

 

The Setup:

  • After committing the update set from Share, Navigate to Task Appointments\Task Appointments and click New.
    • Give it a name
    • Set the schedule for the time frame that you wish to accept appointments and the duration of the appointments.  You may need to create a new schedule if you don't already have one defined that matches the appointment schedule.
    • Optionally set a group you wish to route the appointments to and set the number of concurrent appointments this group can accept.  You can also set the group in the Task Values instead.  The task values will override the value set in the Group field.
    • Set the Task table used to store the appointment records
    • Use the Task Values template field to set any other fields in the task record such as short description, assignment group, category, etc which is applied as an appointment is created.
    • Click Submit.
  • Navigate to Service Portal\Service Portal Configuration and choose Designer to add the widgets to a page.
    • Search for the page that you wish to add this solution to or create a new page if necessary.
    • On the left under Widgets, you should find Appointment Scheduling and Appointments List, drag them onto to your page.
    • Once added, click the Pencil to edit the properties of the Appointment Scheduling widget.
      • Give the widget a Title that will appear above the widget.
      • Set the Task Appointment record to use from step 1.
      • Choose the layout of how you want to display the available appointments.  See a screenshot of the options above.
      • Set whether you wish to allow reminders to be sent prior to an appointment.  This utilizes a little known feature covered by ctomasi in TechNow Episode 39.
      • Set the maximum number of days out that you wish to allow appointments.  Default is 30 days.
      • If you would like to collect Short Description, Description, and Location when creating appointments, check the applicable box(s).
      • Click Save.
    • Click the Pencil to edit the properties of the Appointments List widget.
      • Give the widget a name and set the Task Appointment record to use from step 1.
      • If you would like to show Short Description and Location in the list, check the applicable box(s).
      • Click Save.
  • Enjoy!

 

Other Important Notes:

  • The update set adds a new field to the out of the box Task table called Task Appointment (u_task_appointment) which is a reference to the Task Appointment (u_task_appointment) table.
    • This reference is utilized to link appointments back to the defined configuration record.  It is also leveraged when displaying a user's scheduled appointments.
  • The work_start and work_end fields on the Task table are leveraged to store the appointment start and end time.  The out of the box labels for these fields are Actual Start and Actual End, but this could vary by instance and table.
  • A business rule called "Enforce Task Appointment Schedule" is included to
    • Zero out the seconds associated to an appointment.  This is important to track reserved time slots.
    • Ensure that the work_start and work_end values are valid for the schedule within the Task Appointment definition record
      • The work_end of a task can be extended beyond the Appointment Duration defined within the Task Appointment record, however it must conform to that duration time frame.  In other words if the Task Appointment Duration is set to 30 minutes, a task can have a duration of 1 hour, 1.5 hours, 2 hours, etc in 30 minute increments.
      • If the work_end date of a task is outside the duration time frame, the business rule will prevent the record from being saved and the following message will appear at the top of the screen to the user:
        • End time must be in X minute intervals.
        • This message can be modified in the TaskAppointmentUtils Script Include, checkTaskDates function.
    • A link to this script is provided in the Task Appointment application navigator.
  • The TaskAppointmentUtils Script Include defines all the API's leveraged by the Service Portal Widget.
    • Each function is documented within the script include.
    • This script can be edited to provide further detail in the widget or change data that gets set as appointments are created, updated, and deleted.
    • A link to this script is provided in the Task Appointment application navigator.


Please mark this post or any post helpful or the correct answer so others viewing can benefit.

The Automated Test Framework (ATF), introduced in Istanbul, enables you to create and run automated tests on your ServiceNow instance to ensure that it still works as designed after an upgrade. In Jakarta we've added several new features that give you more flexibility in how you test your instance.

 

atf.png

 

What's new with the ATF in Jakarta?

What's been improved?

The recorded_at field in the sys_atf_result_item and sys_atf_transaction_mtom tables supports the ability to view transaction data for automated test results.

 

Where can I find out more?

A workflow should not be allowed to start until there is at least one supporting task in a related list.

A Major Incident should not be allowed to close if there is not at least on VIP on the watchlist.

A notification should fire if there is not at least one active user in a group.

 

We often find ourselves given requirements that mean we need to find out if there is at least one record that matches a condition.  But there are several ways to accomplish this.

 

You may have already heard that gs.getRowCount() is inferior due to the fact that it iterates the entire record set in order to count the elements.   But writing the code for the Aggregate is just more verbose, and therefore more effort.

 

Consider

 

function getRowCount(){
     var gr = new GlideRecord("sys_user");
     gr.addQuery("active", "true");
     gr.query();
     if(gr.getRowCount() > 0){
          // Do something awesome
     }
}

 

Versus

 

function getAggregateCount(){
     var gr = new GlideAggregate('sys_user');
     gr.addAggregate('COUNT');
     gr.addQuery('active', 'true');
     gr.query();

     if (gr.next() && gr.getAggregate('COUNT') > 0)  {
          // Do something awesome faster
     }
}

 

How much faster is the aggregate method?

 

howLong(getRowCount);
howLong(getAggregateCount);

function howLong(func){
     var cal1 = Packages.java.util.Calendar.getInstance();
     var startMillis = cal1.getTimeInMillis();

     func.call();

     var cal2 = Packages.java.util.Calendar.getInstance();
     var endMillis = cal2.getTimeInMillis();
     gs.print('Millis for Query:'+(endMillis - startMillis ));
}

 

 

Efficiency 1.png

 

The GlideAggregate gets the job done in almost 1/4 of the time.  Wow.  That's compelling.  We really ought to use the GlideAggregate COUNT over getRowCount().

 

Just watch out for one thing:  You have to call next() in order for getAggregate('COUNT') to work!

 

However, this got me thinking.   By writing these conditions...

 

if ( gs.getRowCount() > 0 ) { }
//OR 
if (gr.next() && gr.getAggregate('COUNT') > 0)  { }

 

We're really on trying to figure out if there is at least one row — right?

 

So, what about counting the rows after limiting the result set to a single row.

 

function getOneRow(){
     var gr = new GlideRecord("sys_user");
     gr.addQuery("active", "true");
     gr.setLimit(1);
     gr.query();
     if(gr.getRowCount() > 0){
          // Do something awesome at blinding speed
     }
}

 

//Drum roll please
howLong(getOneRow);

 

Efficiency 2.png

 

So that's 1/11th of the time that it took without the setLimit(1) statement.   But, surprisingly, it beat the getAggregate('COUNT') method by a factor of 3.

 

Another factor to consider is that for this example performance has been measured using the sys_user table, which has relatively few entries.  Execution time for getRowCount() is going to grow proportionally with the number of results; so performance using getRowCount() with a table with a large number of records is going to be much worse.

 

So the next time I need you need know if there is at least one record that matches a particular condition, try using getRowCount() with a setLimit(1) statement.

 

ctomasi Thanks for challenging me on that sloppy answer.  

 

Now my question to any MSSQL database gurus out there:   What are the database impacts of each of these approaches?    I wouldn't want to count something as a performance win if it was just pushing the work into the database where it was still a performance bottleneck for the instance!

With the Jakarta release, we have made accessibility updates throughout the platform in relation to 508 compliance and WCAG 2.0 A and AA standards. While basic accessibility is included as part of the platform, these enhancements are meant to make the system more available and understandable to all users.

10937399ecba071fe55abddebac4d774.jpg

 

What is Section 508 and WCAG?

Before jumping into the accessibility enhancements in Jakarta, here's a little background on why we have them in place. Section 508 was signed into law in 1998, allowing for equal access to electronic and information technology. This means that a system is operated in a variety of different ways, not relying on a particular sense or ability of the operator. This expanded the previous Rehabilitation Act of 1973, as a result of the invention of the Internet, ensuring accessibility to web content. The Web Content Accessibility Guidelines (WCAG) 2.0 are a set of guidelines organized under four principles: websites must be perceivable, operable, understandable, and robust. Our goal in following these guidelines and principles is to make sure the interface is accessible to users with disabilities.

 

What accessibility enhancements were made in Jakarta?

We made several changes to accessibility throughout the platform. Here is a quick overview of each, but you can find more information for feature-specific changes in the corresponding documentation.

 

Contrast UI theme

New in the Jakarta release, the Contrast UI theme provides more options and accessibility for those who have a hard time distinguishing colors with low contrast levels. You can even make these changes yourself. The theme is one of the system settings for the user interface that you can easily customize. I'll show you how to enable the Contrast UI theme in the next section.

 

Color and style accessibility improvements

In addition to the Contrast UI theme, these color and style changes make the platform more accessible:

  • Improvements to alert color and font styles to make them more visible.
  • Switch any color-heavy graphics, such as charts or graphs, to use patterns in addition to colors.

Accessibility enabled charts.png

 

Keyboard accessibility improvements

These keyboard accessibility features allow users to navigate the platform using a keyboard:

  • Tab order is consistent with expectations.
  • Button labeling.
  • Improved access to different UI elements.
  • Consistency for fields and UI elements that have hints (or tooltips).
  • Tab-through and arrow keys move items around in a slushbucket.
  • When focused on tabs, right and left arrow keys switch between tabs.
  • Arrow keys work within menus to navigate between entries.

 

Improvements for screen readers

These screen reader accessibility improvements make the platform accessible to screen readers:

  • Screen readers announce when a page reloads.
  • Add alternative text to styles. For example, you can add alternative text to the VIP style icon so that screen readers can call it out.
  • In Connect, screen readers inform users when a member leaves a conversation, when a user is added or removed from a chat, and when uploads complete or fail.

 

For a full list of features in Jakarta that support WCAG to make the interface accessible to users, view the product documentation here: Accessibility features

 

How to enable accessibility in the platform

For accessibility within the platform, no additional configuration is needed and users just need to enable a user preference to make it more accessible. This turns on tabbing for each page, including every item, with the option to skip to different places on a page.

 

Use the gear icon, which is located within the banner frame, to display the system settings pop-up window (shown below), and the system settings are organized by tab. Those users with an admin role see the Switch to UI15 button if UI16 is activated.

 

To enable accessibility:

  • Users can enable this option from the system settings menu > General tab
  • Admins can do this on behalf of users by navigating to User Administration > User preferences, and searching for the preference glide.ui.accessibility

 

This is just a small sample as to what is available with the release of Jakarta. Stay tuned for more articles and updates coming your way soon!

There are situations when your ServiceNow instance is up and operational but your users still cannot log in because they don't see the login page asking for a username and password. This article covers one of such situations where a user accesses a ServiceNow instance and, instead of the login page, the user is redirected to session_timeout.do. This redirection keeps on running in an infinite loop and you never see the login page.

 

Let's say your instance URL is https://instance.service-now.com and when you access it, you get redirected to https://instance.service-now.com/session_timeout.do infinite loop. This looks something similar to below image.

 

session_timeout.jpg

 

Troubleshooting the session timeout loop when logging in

When I began investigating this issue, my first attempt was to access the instance using side_door.do or let's say login.do to check if the default login displays. I was relieved to say, yes it does. As a workaround, users can access their instance using a suffix of login.do (https://instance.service-now.com/login.do) and bypass this issue. Now, Let's move further and look into this, ask ourselves what is it that is causing this issue and how to fix it.

 

Is the loop SSO related?

First I checked if Multi Provider SSO or old SAML plugin is enabled in the instance by verifying below system properties quickly:

 

Results from Multi Provider SSO: glide.authenticate.multisso.enabled

Results from SAML plugin: glide.authenticate.external

 

Both of the system properties appeared as false, which confirms this is not SSO related.

 

What can the transaction logs tell us?

Accessing an instance is also a type of transaction; therefore, I decided to check Transaction Logs where I see multiple transactions with below URLs one after another:

 

/nav_to_do?uri=welcome.do

/session_timeout.do

 

Transactions keep on generating as long as your browser is open because of infinite looping. In logs, I can see the related session as well as the node name. I checked the system logs considering Transaction Logs creation timings for keywords navpage.do and session_timeout.do transaction and this is what I see:

 

2017-07-07 14:25:27 (454) http-23 New transaction 3613CCCEDB73F2004159F37EAF96198D #25515 /navpage.do
2017-07-07 14:25:27 (460) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D #25515 /navpage.do Parameters -------------------------
2017-07-07 14:25:27 (460) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D *** Start  #25,515, path: /navpage.do, user: guest
2017-07-07 14:25:27 (479) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D Memory transaction: 2mb total: 275mb free: 52%  Allocated: 571mb
2017-07-07 14:25:27 (479) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D *** End  #25,515, path: /navpage.do, user: guest, total transaction time: 0:00:00.025, transactio
n processing time: 0:00:00.025, network: 0:00:00.000, chars: 13, uncompressed chars: 13, SQL time: 12 (count: 5), business rule: 0 (count: 0), phase 1 form length: 0, largest
 chunk written: 0, request parms size: 40, largest input read: 0

2017-07-07 14:25:27 (547) http-21 New transaction 3613CCCEDB73F2004159F37EAF96198D #25516 /sp/
2017-07-07 14:25:27 (552) Default-thread-55 3613CCCEDB73F2004159F37EAF96198D #25516 /sp/ Parameters -------------------------
    id=login
2017-07-07 14:25:27 (554) Default-thread-55 3613CCCEDB73F2004159F37EAF96198D #25516 /sp/ -- total transaction time: 0:00:00.000, transaction processing time: 0:00:00.000, tot
al wait time: 0:00:00.000, session wait: 0:00:00.000, semaphore wait: 0:00:00.000, source: 86.90.145.21, chars: 0, uncompressed chars: 0, SQL time: 1 (count: 3), business rul
e: 0 (count: 0), phase 1 form length: 0, largest chunk written: 0, request parms size: 64, largest input read: 0

2017-07-07 14:25:27 (631) http-28 New transaction 3613CCCEDB73F2004159F37EAF96198D #25517 /session_timeout.do
2017-07-07 14:25:27 (708) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D #25517 /session_timeout.do Parameters -------------------------
2017-07-07 14:25:27 (708) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D *** Start  #25,517, path: /session_timeout.do, user: guest
2017-07-07 14:25:27 (721) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D [0:00:00.012] getRealForm
2017-07-07 14:25:27 (726) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D Memory transaction: 4mb total: 281mb free: 51%  Allocated: 571mb
2017-07-07 14:25:27 (726) Default-thread-57 3613CCCEDB73F2004159F37EAF96198D *** End  #25,517, path: /session_timeout.do, user: guest, total transaction time: 0:00:00.095, transaction processing time: 0:00:00.095, network: 0:00:00.000, chars: 6,023, uncompressed chars: 20,143, SQL time: 4 (count: 11), business rule: 0 (count: 0), phase 1 form length: 52,115, largest chunk written: 9,636, request parms size: 40, largest input read: 0

 

Log statements 09 to 14 in above logs explains that Service Portal execution is happening immediately after navpage.do which then leads to session_timeout.do.

 

The id=login is referring to Service Portal Login page which is not public. The private portal page causes the login attempt to time out on a loop. When I enable public is true for the Login page, the issue no longer appears. If setting the page to public does not help, kindly check the following:

 

  1. There must be an active record for page $sp in sys_public record list.
  2. Check system property glide.entry.page.script value and related script includes which should use this login page.

 

Out of the Box, System property glide.entry.page.script value is new CMSEntryPage().getEntryPage() but you can always replace it based on your business requirements.

 

Hope this helps!

 

For more information:

Transaction Logs

6 ways to set up your Service Portal for redirection SUCCESS!

Brgds, LK

PS: Hit Like, Helpful or Correct if I was able to assist you

Jim Coyne

"Switch to List View" Tool

Posted by Jim Coyne Aug 1, 2017

Here is "Switch to List View".  It's a "Form context menu" UI Action that simply switches to the list view for the table, and filters the list to only display the current record:

 

 

 

It's useful, for instance, when you need to see or modify a field that is not, for whatever reason, displayed on the form view.  You can personalize your list view, add the appropriate fields and update the record if you need to.

 

UI Action details:

Name: Switch to List View

Table: Global [global]

Order: 200,000

Active: checked

Show insert: checked

Show update: checked

Client: checked

Form context menu: checked

Hint: Switches to the List View for the table, returning this one record

Onclick: u_switchToListView()

Condition: gs.hasRole("admin")

Script:

function u_switchToListView(){
    try{
        window.location = "/" + g_form.getTableName() + "_list.do?sysparm_query=sys_id%3d" + g_form.getUniqueValue();
    } catch(err){}
}

 

 

I've attached the XML export of my UI Action record if you want to just import that.  Try it out in a personal dev instance first.

 

Related Posts:

"Show Contents of g_scratchpad" Tool

"Try It (Portal)" Tool

"Developer Toolbox" Tool

"Preview GlideRecord Script" Tool

"Preview in Background Script" Tool

"Xplore GlideRecord Script" Tool

"View Data to Preserve" Tool

"Create Module From Query" Tool

"Clear User Settings" Tool

"Grab Grouped Information" Tool

Showing a Schema Map From a List View

*** Please Like and/or tag responses as being Correct.
And don't be shy about tagging reponses as Helpful if they were, even if it was not a response to one of your own questions ***

We are adding quite a bit of information to the g_scratchpad object on a project I'm involved with at the moment so I decided to create a quick tool to help us see what all ends up there.  It's a simple client-side Related Link UI Action that pops up a window with the current contents:

 

 

1. Here's the configuration for the UI Action:

Name: Show Contents of g_scratchpad

Table: Global

Order: 100,000

Action name: u_show_g_scratchpad

Active: checked

Show insert: checked

Show update: checked

Client: checked

Form link: checked

Hint: Displays the contents of the g_scratchpad object in a popup window

Onclick: u_showGScratchpad()

Condition: gs.hasRole("admin")

Script:

function u_showGScratchpad(){
    var newLine = "~~~newline~~~";  //will be replaced with line breaks in the UI Page
    var scratchPad = "";
    var items = Object.keys(g_scratchpad);
    items.sort();  //sort the item names alphabetically in case they are not already

    var numberOfItems = items.length;
    for (var i = 0; i < numberOfItems; i++) {
        scratchPad += items[i] + " = " + JSON.stringify(g_scratchpad[items[i]]) + newLine;
    }

    //encode the string so it can be passed to the UI Page properly and then decoded there
    scratchPad = encodeURIComponent(scratchPad);

    //open the dialog window
    var gdw = new GlideDialogWindow("u_simple_copy_paste");
    gdw.setTitle("Contents of g_scratchpad");
    gdw.setSize(650, 500);
    gdw.setPreference("sysparm_text", scratchPad);
    gdw.render();
}

 

 

2. The pop-up UI Page:

Name: u_simple_copy_paste

Category: General

HTML:

<textarea id="u_text_area" style="width: auto; height: auto;" rows='15' cols='100' title="Text you can copy and paste">
</textarea>
<div align="right">
<button class="btn btn-default" id="cancel_button" onclick="(window.GlideDialogWindow || window.GlideModalForm).prototype.locate(this).destroy(); return false" style="min-width: 5em;" title="" type="button">Close</button>
</div>

 

Client script:

try {  
  var search = "~~~newline~~~";  //text we want to replace  
  var replace = "\n";  //the text we want to use  
  var textArea = gel("u_text_area");  
  //set the text string into the text area control, first decoding it and replacing the search string with the replacement string to insert proper line breaks  
  textArea.innerHTML = (decodeURIComponent("${sysparm_text}")).replace(new RegExp(search, 'g'), replace);  
  //auto-select the entire text  
  textArea.select();  
} catch(err) {}

 

The UI Page is the same one used by "Preview GlideRecord Script" Tool  and "Grab Grouped Information" Tool.  This one is slightly updated - I've added a "Close" button to the form.  You can use the existing UI Page as is if you are using either of the other two tools, or update the HTML field with the code above to add the button.

 

I've attached the XML files for both the UI Action and the UI Page if you want to just import them into your instance.

*** Please Like and/or tag responses as being Correct.
And don't be shy about tagging reponses as Helpful if they were, even if it was not a response to one of your own questions ***

Filter Blog

By date: By tag: