Using FogBugz On Demand? We've recently rolled out a new sidebar as part of taking FogBugz forward. Please see this article for details on what's new, what's changed, and where you can find all your favorite things.

This post is a work in progress

The FogBugz user interface, including global javascript objects and DOM elements, can and does change without warning. If you have trouble with FogBugz or your customization after applying a customization or a new release, please confirm that your customization is working as expected.

The customizations feature is currently in Beta. Contact us if you would like to have customizations enabled on your FogBugz site. If you used the Bug Monkey plugin in the past, your customizations will need to be updated. This article will become your guide to migrating your scripts and for making new changes to the case and list pages.

Configuring Customizations

The “Customizations” feature in FogBugz On Demand and FogBugz On Site allows you to add client-side javascript and css to the application. Use code and stylesheets to tweak the UI to your liking and support your own specific workflow and needs. This feature was inspired by the “GreaseMonkey” browser extension and operates in a similar fashion. The customizations feature is currently in Beta. Contact us if you would like to have customizations enabled on your FogBugz site.

To use customizations, go to the avatar menu in the top-right of FogBugz and choose “Customizations” from the drop-down. The main view is a list of all customizations currently active:

Customization List

Click on the title of a script to edit it. Click “On” or “Off” in the Enabled column to enable or disable it for your user. “Edit Rules…” lets you set whether the customization is available for others to enable, if it defaults to on or off for them and if it’s required.

Click “New Customization…” to create a new script. Give it a name and description and fill in the JS and CSS sections. Make sure you save it and also enable it for your user. You can also enable scripts that others have shared in your FogBugz instance in the “Your Available Customizations” list.

Customization Editor

Site admins are offered a “Site Configuration” button on that page to allow them to set which users or groups are allowed to create customizations:

Screen Shot 2016-09-01 at 11.47.14 AM

Writing Customizations

Structure

Each customization has a header where you set the name, description, author and version:

name:        A Quick Thing
description: This script does X, Y and Z
author:      Ada Lovelace
version:     1.1.0.0

The name and description must be changed from the default “Customization” and “Doesn’t do anything” which FogBugz fills in for you. The heading and later sections must be properly formatted in order to save the customization.

After the header, there is a section for javascript and one for css. Within them, make sure you use valid syntax for each type. For example, the js section allows comments using // this formatting, but make sure to wrap your css comments in /* the correct characters */

js:

  // re-name the "Wiki" drop-down to "Wikis"
  $(function() {
    var isOcelot = function() {
      return (typeof fb.config != 'undefined');
    };
    var changeIt = function() {
      $('li.wiki.dropdown a.section-link').text("Wikis");
    }
    if (isOcelot()) {
      fb.pubsub.subscribe({
        '/nav/end': function(event) {
          changeIt();
        }
      });
    }
    else {
      changeIt();
    }
  });

css:

  li.wiki.dropdown a.section-link {
    font-weight: bold !important;
  }

Details

Using the example above, some details and guidelines for customizations:

Stylesheets

The CSS you enter is pulled into FogBugz on page load in both UIs (see below). If it is shorter than 1024 characters, it is added inline on the page after FogBugz’s main CSS. If it is larger, it is pulled into your browser using an @import rule.

Javascript Code

1. Wrap your code in $(function() { … }); so that it runs once the page is ready

2. There are two types of pages in FogBugz: “Ocelot” and “old UI”. Over the last few years, we have transitioned the core portions of the app from our old UI to a new single-page-app we call Ocelot. The old UI pages generally are old-fashioned full-pageloads. Ocelot pages load a mostly blank framework when you first hit enter in the URL, but every transition after that is done AJAXily. (note, if you go from Ocelot to an old page and back, that “back” will be a full pageload)

  • The case list page, case page (view and edit), the iteration planner, kanban board, user options page, and activity feed / notification center are Ocelot pages. Generally, any page with /f/ in it is an Ocelot page
  • The wiki, discussion groups, all configuration pages (other than user options), evidence-based scheduling and anything else not listed above are the old UI

The jQuery we’re running in this example is $(‘li.wiki.dropdown a.section-link’).text(“Wikis”). If we just put that in our script by itself, old UI pages would run it after the whole page was in the user’s browser and it would work fine. On Ocelot pages like the list (search and filter results) and case page, nothing would happen. The code would run in the user’s browser when all they had was our header and footer. We need to run it after all of the single-page-app magic is done. To do that, use pubsub to subscribe to the ‘/nav/end’ event. That event fires at the end of every page in Ocelot.

The simplest way to detect if the code is running in Ocelot is to look for fb.config, as shown in the sample code above.

3. DRY (don’t repeat yourself). In our example, we are working with the main header. Since it is shown on pages in both UIs, we use a function to avoid having to write the same code in two places. As of this writing, the bulk edit page can also be a reason to write DRY code. If you want to make changes to the case edit page, the main one is in Ocelot, but bulk edit is still running in the old UI. It will be migrated soon, but for now you will need to handle both situations. (see the script archive for some example scripts which do this)

Templates and Sample Code

Before you start hacking on FogBugz to implement a client-side change, you might want to look at our list of useful and popular customizations to see if someone has already addressed your need. Even if you don’t find exactly what you are looking for, something there may serve as a good starting point.

Case Page

You can also use this template to create a new customization which runs on the case page. It has all the structure you need to detect which version of the page you are on and what case editing or viewing mode is active. If you need to work with the drop-down fields (e.g. Project and Area), see the next section, “Drop-Down Menus”.

Drop-Down Menus

Drop-down menus in the old UI use the FogBugz “droplist” library. On the page, there is a <select> tag with <option> elements like a normal HTML menu, but it is hidden and the library uses them to manage a custom element. If you want to change the value of one, for example on the edit page for a project, ignore the <input type=text> tag and work with the <select>:

// for the primary contact field on the edit project page, the id is 'ixPersonOwner'. change the selection:
$('#ixPersonOwner').selectedIndex = -1;
$('#ixPersonOwner').find('option[value=3]').attr('selected','selected');
// tell FogBugz to re-generate the text input based on the modified <select>
// (make sure to use the actual element, not the jQuery array
DropListControl.refresh($('#ixPersonOwner')[0]);

On the case page in FogBugz Ocelot, there is a new droplist library. Like the old library, there is a text-type <input> tag. Unlike the old library, there is no <select> element and the value is stored in a hidden input. The element to work with is the <div> which has its ID set to the internal name of the case field you want, e.g. the project id, ‘ixProject’. When you select the field with jQuery, the droplist object for use in your code is accessible by calling .droplist() on that jQuery object. Here is the whole progression:

var theField = $('#ixProject');
// get the actual droplist object:
var theDropList = theField.droplist();
// get the currently selected value:
var selectedIxValue = theDropList.val();
// if you want to apply styles and classes to the field, you don't need to use
// jQuery to go find it. it's right on the field:
var theInputTag = theField.input;
// we have the 'ix' value of the field above. Use the droplist library to get
// the display name for it:
var selectedText = theDropList.getChoiceFromValue(selectedIxValue).text;
// enable or disable the drop-down:
theDropList.enable(); theDropList.disable();
// change the value:
// if you know the 'ix' value you want change to, set it with .val(ix):
// var ixProject = 15;
theDropList.val(ixProject);
// the available choices for the droplist are in config.choices:
console.table(theDropList.config.choices);
// loop the choices, e.g. if you want to find the ix for a given text
for (i=0; i<theDropList.config.choices.length; i++) {
  var choice = theDropList.config.choices[i];
  console.log("ix: " + choice.value + ", text: '" + foo.text + "'");
}

In the old library, you could hook into changes on a droplist via the onchange event:

$('#ixPersonOwner').change(function() { console.log("primary contact drop-down changed"); });

Like most of the new Ocelot UI, the droplist library uses events fired on the <body>:

function handleChange(event) {
  var theField = event.target;
  var theDropList = theField.droplist();
  console.log('droplist with id ' + theField.id + ' changed to ix ' + theDropList.val() +
              ' / text "' + theDropList.getChoiceFromValue(theDropList.val()).text + '"');
}
$('body').on("droplistUserInput", function(event) {
  handleChange(event);
});

FogBugz Entities

FogBugz has many entities which you are familiar with from working with cases: Bugs (cases), Projects, Areas, Categories, etc. Internally, these all have a set of properties which follow naming conventions. For example, each entity has a numeric ID, ‘ix’ followed by the entity name, and a display name which is called ‘s’ followed by the entity name: Project has ixProject as its ID and sProject as its name; Category has ixCategory and sCategory. Cases opened via email in your FogBugz account probably have their category set as ixCategory = 3 and sCategory = “Inquiry”. Information about all of the entities is available in javascriptland in the fb.db object. Here is a sample function to demonstrate working with the lists. Here we look up an ixProject from an sProject:

function getIxProjectFromSProject (sProject) {
  var ixProject = -1;
  for (i=0; i<fb.db.Project.length; i++) {
    var project = fb.db.Project[i];
    if (project.sProject == sProject) {
      ixProject = project.ixProject;
      break;
    }
  }
  return ixProject;
}

Here is a sample script which sets the category of the case being edited / created to “Inquiry” if the project is “Inbox”. You can modify it using code like getIxProjectFromSProject above to look up the ix’s by name if you wish. Remember to use the basic pubsub method in “Structure” above so that your $(‘body’).on code is run after the Ocelot case page is fully rendered. You might want to use the larger template script in “Case Page” above.

$(function() {
  var ixProjectInbox = 2;
  var ixCategoryInquiry = 3;
  var checkForProjectInbox = function(event) {
    if (event.target.id == "ixProject") {
      var dropList = event.droplist;
      if (dropList.val() == ixProjectInbox) {
        var categoryDroplist = $('#ixCategory').droplist();
  categoryDroplist.val(ixCategoryInquiry);
      }
    }
  }

  fb.pubsub.subscribe({
    '/nav/end': function(event) {
      $('body').on("droplistUserInput", function(event) { checkForProjectInbox(event); });
    }
  });
});

Migrating from Bug Monkey

Before the new Ocelot UI, FogBugz included an optional plugin called Bug Monkey which worked the same way Customizations do now. When you switch to Ocelot, your customizations come along, but they will need changes if they ran on the case page or the list page. Since these two pages have changed in the new UI, you will need to update your customizations to look for the correct DOM elements and use new JS events and APIs.

A migration guide is coming soon, so watch this space. For now, you can find some explanation of the changes and how some basic things work in the rest of this article. If you only need to run code on the case page, take a look at our template. If you want to run code on the case list page, you can just pull out the “pubsub” portion of that template to run your code on ‘nav/end’. You can also wait for a complete guide here, or drop us a line for personalized help migrating your scripts.

If you just moved over to Ocelot from the old UI and your critical customizations aren’t running on the case page, you may want to temporarily revert all users to the old UI to make sure your workflow or other changes which Bug Monkey provided are in effect. If you email us, we can make that change so your users keep seeing the important customizations you have made while you update your code for Ocelot.

Caveats

The DOM, javascript events (such as ‘/nav/end’) and javascript objects and apis (like fb.config and fb.api) are not public, stable APIs. Expect them to change without notice.

This guide is for FogBugz On Demand and On Site. If you are running FogBugz For Your Server, see this guide to the Bug Monkey plugin.