703.242.7200 [email protected]

Tutorial: Customizing FootPrints Service Core using JavaScript, Part 1

RightStar TeamJuly 8, 2014
Applicability FootPrints Service Core version 11 or later, but not including version 12. Pre-requisites
  • Access to Form Designer in one or more workspaces
  • Access to the file system on FootPrints application server
  • Ability to write JavaScript
  • (Suggested) Familiarity with the Sencha Ext JS framework (version 3.4, specifically)
  • (Suggested) Familiarity with the HTML Document Object Model (DOM)
One of the things I love most about FootPrints is how readily customizable it is. I’m not talking about the fact that administrators can tweak so many options through the web interface, or that the source code is uncompiled plain text that can be edited by anyone who knows how to program Perl, but the fact that the makers of FootPrints have always kept open the means to inject JavaScript onto the incident form. It’s been very rare that a client will ask if something is possible in FootPrints and my answer is not “Yes, with some JavaScript.” In the first part of this tutorial, we will see how to customize the incident form using JavaScript added through the Form Designer. The business case we will use as an example is one where the organization wants a particular field to appear above the Assignees picker in the Assignees and Notifications section of the incident form. The Form Designer does not allow fields to be placed in this section, establishing the case for JavaScript customization. While this particular use case may not be of any interest to your organization, by following this tutorial, you will receive advice on best practices and find code that can be reused in your customizations. Let’s say your organization wants to assign incidents automatically to different groups based on categorization fields. Your categorization is based on two dropdown fields, Category and Sub Category, and so the standard Auto Assignment feature, which can only be based on a single field, cannot be used to determine assignees. Instead, the AutoField feature must be used. The potential problem with AutoField is that it runs every time an incident is updated and we don’t want the assignees to change if they’ve been set by other means throughout the incident’s lifecycle. We’re going to let the editing agent decide on a case-by-case basis whether the assignees should be subjected to the AutoField rules by introducing a field called Let Categories Determine Assignees. Each AutoField rule will be set up to run only if this field is set to “Yes”, like so: Figure 1 – AutoField rules for setting assignees Our JavaScript is going to accomplish the following things: Rename the first option in the Let Categories Determine Assignees field from the built-in value of “No Selection” to “No”. This makes the field a little more user-friendly. If the user wants to override the auto-assignment behavior, they won’t be scratching their heads wondering what the effect of “No Selection” might be. Default Let Categories Determine Assignees to “No”. Prevents auto-assignment from occurring every time the incident is edited. Set Let Categories Determine Assignees to “Yes” when categorization fields change. Necessary to have the auto-assignment rules run after the incident is created or updated. Move the Let Categories Determine Assignees field to the assignee’s area. Intended to improve usability. If the user wants to manually set assignees, they’ll open the assignee’s section of the form where they’ll be more likely to notice this field and understand that they have to change it from “Yes” to “No”. The first step in this tutorial is to create a new text file called incident_management.js inside the C:\FootPrintsServiceCore\html\ directory (throughout this tutorial, we’ll be assuming FootPrints is installed to C:\FootPrintsServiceCore.) Paste the following code into the file and save the file. // Moves the “Let Categories Determine Assignees” field // to the assignees and notifications section. // Defaults this field to “No. // Sets this field to “Yes” when the category fields are selected function assigneeCustomizations() { var categoryFields = [“Category”, “Sub Category”]; // Only run this customization under certain conditions if (isDetailsMode || isCustomerMode) { return; } // field to be moved var fName = “Let Categories Determine Assignees”; var f = document.getElementById(fName.fix()); // verify that the field to be moved exist on the form as a dropdown if (f === null || f.type != ‘select-one’) { return; } //Move the field and label above the assignee picker. // verify that the assignee’s picker is displayed. if (window.assigneePicker !== undefined) { var originalDivHoldingFieldAndLabel = Ext.get(f).up(‘div.cell’); var tbody = Ext.get(assigneePicker.box1).up(‘tbody’); var td = new Ext.Element(document.createElement(‘td’)); td.set({ colSpan: tbody.child(‘tr’).query(‘td’).length }); td.appendChild(originalDivHoldingFieldAndLabel); var tr = new Ext.Element(document.createElement(‘tr’)); tr.appendChild(td); tbody.insertFirst(tr); } // Set listeners on the category fields so that when they change, // the field to set assignees based on categorization // will switch to ‘Yes’ for (var i = 0; i < categoryFields.length; i++) { var c = document.getElementById(categoryFields[i].fix());         if (c !== null) { Ext.EventManager.on( c, ‘change’, function() { f.selectedIndex = 1; } );         } } // Rename “No Selection” to “No” f.options[0].text = ‘No’; // Always default or reset the field to not override assignees // based on categorization f.selectedIndex = 0; } Ext.onReady(function() { afterFormInitialized([setGlobals, assigneeCustomizations]); }); // Supporting code follows. This can be included in // other custom javascript files with no change. var isDetailsMode; var isEditMode; var isCreateMode; var isAgentMode; var isCustomerMode; function setGlobals() { if (document.regform === undefined) { isDetailsMode = true; } else { if (document.regform.MAJOR_MODE !== undefined) { var mode = document.regform.MAJOR_MODE.value; if (mode == “CREATE” || mode == “PREVIEW”) { isCreateMode = true; } else if (mode == “EDIT”) { isEditMode = true; } } } // determine if customer or agent user. // MRP parameter begins with 0 if customer. for (var i = 0; i < document.forms.length; i++) { var f = document.forms[i]; if (f.elements.MRP !== undefined) { var mrp = f.elements.MRP.value; if (mrp.charAt(0) == ‘0’) { isCustomerMode = true; } else { isAgentMode = true; } break; } } } function afterFormInitialized(callbacks) { Ext.TaskMgr.start({ run:  function() { var el = Ext.get(‘wait’); if (el === null) { //The “Loading…” mask hasn’t been created yet } else { if (!el.isVisible(true)) { Ext.each(callbacks, function(i) { i.call()}); return false; } } }, interval: 50 }); } < TODO: Link to a file called incident_managent.js> Next, we’ll use Form Designer to add a Custom HTML element to the form that references the file we just created.

Adding a Custom HTML element to the incident form

The “Custom HTML” form element is listed under the “Special Features” section of the Form Designer palette. Figure 2 – Form Designer palette with Custom HTML highlighted Because of how the code was written [I’ll come back to this], we are free to place the Custom HTML element anywhere on the form. I prefer to drop it into the area above the tabbed sections so that it will always be prominent when viewing the Form Designer. You should be aware that while the Custom HTML element we are going to add is not going to contain any visible content, it will still occupy a space on the form’s layout grid. If you put it on a row by itself, users will see a blank row on the form. For that reason, I recommend you put it in an unoccupied space on a partially filled row. Figure 3 – Placement of Custom HTML element above tabbed sections Once the element has been placed on the form, click the pencil icon to edit its properties. Figure 4 – Populating the Custom HTML element’s body In this example, set the element’s name to “Incident Management Customizations”. Next, disable Rich Text Mode. Important: you must disable Rich Text Mode on a Custom HTML element that holds raw HTML or JavaScript and enter the following content: <script src=“/footprints/incident_management.js”></script> Set the element’s Permissions and Access properties so that it is visible to both customers and agents. You should always do this, even if you have customization that should not run, let’s say, on the customer version of the incident form. Next, add a dropdown field to the form called Allow Categories to Determine Assignees. This field should have only 1 choice, “Yes”, and should be optional to agents/hidden from customers. That’s it for the step-by-step portion of this tutorial. What remains is to create and populate the Category and Sub Category fields, and to create some AutoField rules that set the assignees field based on combinations of these two fields plus the Allow Categories to Determine Assignees (having a value of “Yes” in each rule). I assume the reader knows how to perform these basic administrative tasks already. I will now move on to highlighting several techniques used in this example that I consider to be best practices.

Best Practices

B.P. #1: Delay your code from running until the form has finished initializing

If you have ever experimented with adding JavaScript using the Form Designer before, you have probably had to ask yourself the question “Where is the best place [on the form] to add my code?” Placed too near the top and it might try to address a field before it has been rendered. Adding a fixed amount of delay before the code runs might work most of the time but inexplicably fail at other times. The reason it is a hard question to answer is that browsers use multiple, parallel threads to render the page, fetch resources over the network, and executive JavaScript, and these threads do not execute in any predictable order. The technique I advocate does not make a loose prediction and leaves things to chance. It delays the code from running for as long as needed until it receives a signal that indicates with 100% certainty that the form has finished loading. The code below demonstrates how to call the myCustomCode1 and myCustomCode2 functions after the form has finished initializing. // Your customizations go inside functions whose names are // passed into afterFormInitialized. function myCustomCode1() { alert(“The form is ready.”); } function myCustomCode2() { alert(“I also think the form is ready.”); } Ext.onReady(function() { afterFormInitialized([myCustomCode1, myCustomCode2]); }); function afterFormInitialized(callbacks) { Ext.TaskMgr.start({ run:  function() { var el = Ext.get(‘wait’); if (el === null) { //The “Loading…” mask hasn’t been created yet } else { if (!el.isVisible(true)) { Ext.each(callbacks, function(i) { i.call()});                     return false; } } }, interval: 50 }); } You can see this technique in practice in our example code: the two functions, setGlobals, and assigneeCustomizations, are passed into afterFormInitialized, delaying their execution until the appropriate time.

B.P. #2: Place your JavaScript in a Custom HTML form element

Back in the days before FootPrints Service Core had its Form Designer feature, custom JavaScript had to be injected into the ticket form using what were called “field headers”. Field headers were always tied to a specific field on the form and they were always printed (even if they contained only invisible JavaScript) above the field they were tied to. The equivalent of a field header in version 11 is the “Help Text and Instructions” property of a field. There are a couple of reasons I do not recommend using a field’s “Help Text and Instructions” property to hold your JavaScript. The first is that it makes the JavaScript hard to locate: you cannot see at a glance whether a field’s Help Text and Instructions holds JavaScript, actual instructions for the user, or both. You might be thinking that keeping track of where JavaScript is located can be made into a straightforward task by always putting the JavaScript in the instructions property of the particular field it affects, but what you have to remember is that not all customizations target a specific field. The other reason I do not use any field’s instructions property is that it forces you to use extra care when changing a field’s visibility to particular user roles or when deleting a field, as the injection of the JavaScript will be affected by such changes. With the introduction of the Form Designer in version 11, you can now add Custom HTML elements to the form. By using one of these to hold your JavaScript, you will avoid the two problems mentioned earlier: you can give the HTML element a name that makes its purpose (holding JavaScript) self-evident, allowing you to quickly locate where your JavaScript resides without drilling down into multiple fields; custom HTML elements are not tied to any fields, allowing you to manage fields and custom JavaScript independently.

B.P. #3: Load an external script file

When we created the Custom HTML element, we did not fill it with any actual JavaScript; instead, it only contains a <script> tag that loads an external file containing the JavaScript.   The main reason I advocate this practice is that it avoids an issue where + characters can disappear from JavaScript defined directly in the Form Designer. This can be a serious problem given the role + plays in JavaScript syntax:
Imagine your code includes the for loop: One day, you make a change to some unrelated part of the form and publish. Suddenly, no one can create any incidents–the browser just hangs. It turns out that the + characters in the JavaScript disappeared, leaving your code looks like this: Your incident form now has an infinite loop because the loop’s counter variable, I, never changes!
Keeping the JavaScript in an external file also facilitates easier maintenance of the code. You can write your code in your favorite text editor instead of having to use the Form Designer interface. You can also keep as many different versions and backups of the code as you need just by copying the file to different names.

B.P. #4: Keep all of your JavaScript for a given workspace together

You might be wondering why I chose to name the Custom HTML element “Incident Management Customizations”, as that name doesn’t give any indication as to what the code does. The file name referenced by this Custom HTML is given a similarly generic name, “incident_management.js”. This is intentional. I recommend defining all customizations for a given workspace in a single file for the following reasons:
  • Fewer files that have to be transferred to the browser
  • More control over the order in which the customizations run
  • Less chance of naming collisions resulting from a global function or variable having been defined more than once across multiple files
There are a couple of caveats to be aware of, however. First, if you are editing the JavaScript file on a live system, any syntax error you might introduce can break all of the other JavaScript customizations defined in that file. Therefore, you really must test your changes on a development server, or by making your changes in a copy of the JavaScript file, editing your Custom HTML element to load that new file, and then validating the code using the Form Designer’s Preview mode. Once the code has been tested, you can discard the draft of the form and replace the .js file currently referenced by the Custom HTML element. Second, because you will not be defining your customizations in the Help Text and Instructions property of the specific fields they interact with, you will not be able to control the inclusion of the JavaScript based on whether the field it is associated with appears on the incident form using the options shown here. Figure 5 – Options for conditionally displaying a field’s Help Text and Instructions That’s okay, though, because you can instead take a JavaScript-only approach to conditionally having portions of your code run or not, and this will give you more control than you could get by using the Form Designer’s options for conditionally displaying the Help Text and Instructions content.

B.P. #5: Take a JavaScript-only approach to run your code under certain conditions

So far, I’ve advocated using a single Custom HTML element to inject all of your JavaScript onto the form and allowing the entirety of this JavaScript to appear on the form unconditionally, without any regard for whether certain fields referenced by the JavaScript are present for the given user on the given incident at the given time. You might then be wondering how we are going to have the code run only under certain targeted scenarios such as whether the form being displayed is for creating or editing an incident, whether that form is being accessed by a customer- or agent-level user, whether that user has rights to see a certain field, or even whether the time is currently after midnight on the 4th Friday of the month. The answer is to have the code itself check that all the proper conditions for running a certain portion of custom code are met. You can see this technique in our example code where the function setGlobals defines several flags such as isCustomerMode and isDetailsMode that are later checked throughout the code. For instance, our example customization only runs on the create and edit forms opened by an agent: . . . // Only run this customization under certain conditions if (isDetailsMode || isCustomerMode) { return; } . . . If another customization were later added to the JavaScript file, that customization could also make use of these flag variables. The setGlobals function is suitable for reuse in your customizations. The Form Designer lets you show or hide the Custom HTML element that loads your JavaScript based on the user role. If the element is to be shown, then all of the JavaScript will be loaded; it’s an all-or-nothing deal. This JavaScript-only technique, by contrast, produces JavaScript that assumes it will always be present on the incident form and takes responsibility for controlling which parts execute. This allows for much greater flexibility than can be achieved through the Form Designer options, as you can have single lines of code executed or not based on the current user type, or any other condition you care to check.

B.P. #6: Check form elements (fields) before attempting to access them

Given that JavaScript is going to be present on all forms, as just mentioned, it follows that your code should always check for the existence of form elements before attempting to reference them. In our example, no assumption was made that the assignee’s field is displayed. Perhaps the user accessing the form is an agent whose role does not allow specifying assignees. Therefore, the entire block of code that tries to reference the assignee’s picker is surrounded by the following conditional statement: if (window.assigneePicker !== undefined) { [do stuff to assigneesPicker] Similarly, the code does not assume that any of the categorization fields are present: var c = document.getElementById(categoryFields[i].fix());         if (c !== null) { [do stuff to c] Whenever I’m not sure whether my code should be compared against null or undefined,  I open up my browser’s developer tools and test some on-the-fly JavaScript: Figure 6 – undefined versus null

B.P. #7: Reference fields by their internal names (without needing to know how they’re encoded)

In FootPrints, a field’s internal name is formed by encoding all non-alphanumeric characters in the visible name into a 3-character sequence. Sub Category has the internal name of Sub__bCategory, Phone # is Phone__3, and so on.  If you need to access the form input for a field (to get or set its value), you will need to know this internal name because that’s how the form inputs are named. However, you don’t have to know the actual internal name and all of its special character encodings. Your code can make use of the fix and unfix functions that are available on all String objects. This is what we’re doing in our example: var fName = “Let Categories Determine Assignees”; var f = document.getElementById(fName.fix()); It reads a lot better than var fName = “Let__bCategories__bDetermine__bAssignees”;

B.P. #8: Don’t change field names

Changing a field’s name through the Form Designer will change the name of the corresponding column in the database and the name of the HTML input on the incident form. This could adversely affect reporting tools that read the database, clients of FootPrints’ web services, and JavaScript customizations that interact with the form inputs. If you do need to change a field’s name, consider using the “Language of Workspace and Address Book Fields” feature to do so, rather than changing the name through the Form Designer. This feature merely changes the displayed name of the field in the graphical interface. Figure 7 –  Changing a field’s label but not its internal name Keep in mind that the field’s internal name and therefore the name of the form input for the field will always be the encoded version of its original name; i.e., the name of the field before it has been re-labeled for a particular language.

Until next time

If you’ve taken away anything from this tutorial, I hope it’s what I put forward as a re-usable basis for any incident form customization you might ever need to write:
  1. Create a single JavaScript file for the workspace
  2. Add the afterFormInitialized function to the file
  3. Add the setGlobals function to the file
  4. Call afterFormInitialized as follows
Ext.onReady(function() { afterFormInitialized([setGlobals]); });
  1. Add a Custom HTML element to your form to load the JavaScript file
Once you have that in place, just add a function to the file that performs the real work of your customization, and pass this new function into the afterFormInitialized call Ext.onReady(function() { afterFormInitialized([setGlobals, myCustomization]); }); The next tutorial will cover a method for injecting JavaScript on any page in FootPrints, not just the incident form. I discovered this method during the last year and I am excited to reveal it. Until then, I hope this tutorial has prepared you to start writing your own JavaScript customizations for FootPrints.

Need help?

For help writing a FootPrints Service Core customization, or to discuss a project that might require a custom solution, contact RightStar Technical Services.         Disclaimer In no event shall RightStar Systems, Inc. or its employees be held responsible for loss or damages arising out of the use, inability to use, or the results of the use of, the information and source code contained in this article. The information and materials contained or referred to in this article are for reference only. RightStar Systems, Inc. and its employees make no warranty of any kind regarding the information and source code in this article. In no event shall RightStar Systems, Inc. or its employees be obligated to provide additional support on the topic discussed in this article. Copyright 2014, RightStar Systems, Inc.