Drag'n'Drop Attachments

Monday, October 04, 2010 by Ben Kamens

Back around the time when FogBugz 7.3 was about to launch, Gmail launched a really cool feature: the ability to drag and drop attachments from your computer directly onto your browser window and into the email you're about to send.

We thought this was an awesome display of browser power and wanted in on the fun. So we secretly snuck drag'n'drop case attachments into FogBugz 7.3 so that we could play around with the feature and see if it felt useful.

Dragging an attachment directly into FogBugz Adding screenshots to FogBugz cases has always been one of the most powerful ways to describe bug reports.

With the launch of FogBugz 8.0, we're bringing a little more attention to the feature. And we'd like to know what our users think.

We're still a little unsure about the usability experience of dragging items from your desktop directly into a browser window. If this behavior becomes more standard in popular web apps across the board, or if our users start heavily relying on the feature, we'll bring in support for more browsers (Firefox in particular), add the ability to drag'n'drop multiple files at a time, and generally refine the experience.

For now, drag'n'drop is a nice little shortcut that Chrome and Safari users can use to quickly attach items to any FogBugz case.  Let us know how it goes!

Props go to Google for continuing to push the limits of browser technology...and for inspiring us to push the boundaries of how FogBugz can leverage that technology.


Categories: FogBugz

Actions: E-mail, Permalink


Subcases and Hierarchy

Wednesday, September 02, 2009 by Ben Kamens

We decided to add subcases
Customers have wanted hierarchy in FogBugz for years.  They wanted to break their cases up into subcases and add to-do lists.  They like things to nest.  Lots of other bug trackers try to satisfy these demands, and a long time ago we decided to make this a high priority for our next release.

How hard could it be?
It should've been a simple task.  Add subcases to FogBugz 7.  Users need the ability to break down their cases hierarchically or create simple to-do lists inside of their bugs.  Most people could code it up in a few hours, or maybe a caffeine-hazed weekend, right?

So why did we wait until FogBugz 7 to do this if people have been asking after it for years?  As it turns out doing it right took some time...as we've gotten used to when building a new feature.

Doing it right - The first stab
Like most things that go into FogBugz, we're highly concerned with getting it right.  Our features need to be intuitive, simple to use.  If we add too much complexity we risk becoming just another arcane project management system requiring an advanced degree in Ganttonomics to operate effectively.  

On the other hand if we made subcases too simple we knew that the whole implementation would be disappointing to users. 

One early design we considered was to nerf subcases by not making them full FogBugz cases.  Users would create little subtasks that were essentially one-level-deep lists within their bugs.  Wallah!  Task lists.  We get to pat ourselves on the back and tick off another "feature" on the giant feature matrix.  But we quickly realized we'd run into all kinds of complaints: "Why can't I tag my subtasks?", or "I need to assign these tasks to someone else, and I can't!"  Before you know it you'd be waiting for the next version to come out with all its grand promises: "FogBugz 8 -- now you can finally add a due date to your subtasks!"  We've seen such missteps made by others when new features aren’t fully integrated into the core of the product, and we knew this option would never be a winner.

Doing it right - Round 2
After we rejected the nerfed subcase option, our program manager produced a slick spec.

It was a great start, so naturally we immediately started to argue.  Everyone wanted to make a fantastic feature, and everyone had different ideas about how to do that.  So we went round and round...and round.  We finally settled on a design and implemented an initial version.  We showed it off as an internal demo.  Surprise!  We needed more and more changes until everyone inside the company was happy with what we had.  Then we ran usability tests with a couple FogBugz customers.  Lo and behold, we discovered some major problems with our display of subcases in the grid view.  Sometimes when sorting and grouping in the grid view a case belongs in more than one grouping.  For example, a case may be assigned to me and show up under the "Assigned to Ben Kamens" group, but it also belongs directly under its parent case, which is "Assigned to Joel Spolsky".  In those instances we were simply displaying the same case twice in the grid view under both groupings.  This basically caused people's brains to melt.

Doing it right - Round 3
We had to redesign again.  After that last usability test, I was 100% positive that showing a case twice in the grid view was a terrible mistake.  Luckily, Dan, our FogBugz PM, wasn't so sure; he came up with an excellent set of affordances to make the duplicate cases much clearer and to give users the option to just show all cases (including subcases) in a flat view.  He was sure we could do it right and he went to work on me.

We went round and around about this again, as the case below demonstrates (and trust me, this is just a portion of the back-and-forth).  There’s a lot of passion about these issues at Fog Creek.

My opponent didn't back down and, many bugevents further below the fold than this screenshot can provide, proved that I was wrong.


I was eventually persuaded that we could show duplicates if we were very careful about how the display worked.  The next round of feedback showed that this was right, and the two views we built in were a big success.

Other challenges
Subcases also presented a purely technical challenge: We needed to query hierarchical case structures without slowing down FogBugz.  More accurately, we had to run fast hierarchical SQL queries (get me the children of case 5, and their children, and their children, ... ) in all three of our supported databases: MSSQL, MySQL, and Access.  The support for recursive queries in these three databases is varied at best, and nonexistent in one.  Can you guess which is which?

Turns out there's a pretty cool solution for this problem explained by Joe Celko.  Instead of attempting to maintain a bunch of parent/child relationships all over your database -- which would necessitate recursive SQL queries to find all the descendents of a node -- we mark each case with a "left" and "right" value calculated by traversing the tree depth-first and counting as we go.  A node's "left" value is set whenever it is first seen during traversal, and the "right" value is set when walking back up the tree away from the node.  A picture probably makes more sense:

The Nested Set SQL model lets us add case hierarchies without sacrificing performance.


How does this help?  Now we just ask for all the cases with a "left" value between 2 and 9 to find all of the descendents of B in one fast, indexed query.  Ancestors of G are found by asking for nodes with "left" less than 6 (G's own "left") and "right" greater than 6.  Works in all databases.  Greatly increases performance -- particularly when querying large hierarchies.

What we delivered
Waiting to add this feature was worth it. Initial customer reactions have been very positive, and we've even had people who had no real interest in subcases when they heard it was coming tell us that it's changed the way they use FogBugz. 

  • We added a context menu to the grid view which allows you to very quickly add all your subcases right there.  This makes it super easy to set up a whole set of tasks and break them up into their parts without leaving the grid.
  • The outline view lets users easily see their cases’ hierarchical relationships.
  • We clearly mark subcases that exist but do not match your current filter or search.
  • We've added auto-complete to the case view so you can make existing cases into subcases of the one being currently viewed by simply typing a case number or searching for words in its title.
  • You have the option to resolve and/or close all subcases when resolving the parent case.
  • Subcases are fully integrated with search, filters, and all standard FogBugz workflow.

That's the feature we fought so hard over.  So far the feedback has been uniformly good, and we think it's a keystone in our best release yet.  You should try it out and sign up for a free FogBugz 7 trial.
 


Categories: FogCreek, FogBugz

Tags:

Actions: E-mail, Permalink


FogBugz in Chrome

Thursday, September 04, 2008 by Ben Kamens

Update: FogBugz 6.1.36 has been released in order to fully support Google Chrome! 

To all those FogBugz users who've already switched to Google's Chrome browser and are enjoying its blazing speed: rest assured that we are working on improving the few pieces of FogBugz which are not yet fully Chrome-compatible. 

We know of the following issues when using Chrome to browse FogBugz:

1) FogBugz autocomplete dropdown lists are not supported.
2) Clicking inside the search bar doesn't bring up Recent Searches.
3) FogBugz keyboard shortcuts are slightly buggy.
4) FogBugz is too fast.

We are already working to resolve these issues (except the last one), but feel free to let us know if you discover any other problems.


Categories: General, FogBugz

Actions: E-mail, Permalink


Wasabi: Fragment Caching and Memoization

Wednesday, March 05, 2008 by Ben Kamens

Stefan recently discussed some of the benefits of picture functions and metaprogramming in Wasabi. One of the first (and arguably one of the most useful) internal tools that these features spawned during FogBugz 6 development was the ability to easily memoize and cache function results.

Performance was one of our primary concerns when working on FogBugz 6, so the ability to reuse previously calculated results within any single request (memoization) or to store longer-lasting results in the database for use in future requests (fragment caching) was badly needed. Those who are familiar with other popular web frameworks are probably used to having these caching tools readily available, although their implementations are sometimes quite clunky. We were a little bit jealous, so we decided to chase after our own implementation.  Unfortunately, in classic ASP/VBScript there is no way to easily achieve this behavior without massive code duplication and, as a result, an increasingly high likelihood of cache-related errors.

Wasabi metaprogramming lets us solve the problem gracefully. What we need is a way to tag a function as <Cacheable>, in which case every call to the function will check the database for previously stored results and short-circuit if such a result is found, avoiding the possibly expensive function execution (for the rest of this article I will only discuss caching, which uses the FogBugz database as its storage, although memoization is an almost equivalent piece of functionality which uses per-request in-memory storage).

However, there are a number of complicating factors:

1. Different function inputs produce different outputs.
2. Cached results, or fragments, are usually user-specific. I should never see the cached result of a function called by Stefan or Joel.
3. Changes in the database state over time may invalidate pieces of the fragment cache. 

Let's modify Stefan's flower picture function and make a little utility function that generates the picture of a flower stem with a leaf either on the left or the right. It also places the flower in a pot depending on the state of the database: 

<Cacheable(“FlowerPots”), Picture> _
Sub MyStem( fLeft )
    If fLeft Then
        %><pre>
         __ |
         \ \|
          \ |
           \|
        </pre><%
    Else
        %><pre>
            | __
            |/ /
            | /
            |/
        </pre><%
    End If

    If HasFlowerPot() Then
        %>
        _________
        \       /
         \     /
          \___/
        <%
    End If
End Sub 


Notice that we've added a <Cacheable> attribute to this function. This means that anytime, say, Joel's user logs into FogBugz and generates a call to MyStem requesting a leaf on the left, we check the database to see if the result of this function with matching parameter values has already been stored for Joel. If so, we give him back the stored value in presumably much shorter time than letting the entire function run (you'll have to pretend for this example that execution actually takes a significant amount of time), Joel doesn't wait as long for the page to load, FogBugz is faster, and everyone's happier.

What did we need to accomplish this?  We need to intercept calls to MyStem, normalize all parameters, and check the database for any matching cached results.  If we don't find any, we need to run the original MyStem function, take a picture of its result, store the result in our cache, and return the result. These pieces are easy with Wasabi, and Stefan has already covered most of the necessary tools. I won't go into the specifics here, but the intermediate code generated by Wasabi would look something like:

Sub MyStem( fLeft )

    Dim sArgs = Hash(CStr(fLeft))

    If FragmentCache.Exists(“MyStem”, sArgs) Then 
        Response.Write FragmentCache.Retrieve(“MyStem”, sArgs) 
    Else 
        Dim sResult = PictureOf OriginalMyStem(fLeft)
        FragmentCache.Store(“MyStem”, sArgs, sResult)
       
Response.Write sResult
   
End If

End Sub


A slightly more interesting piece of the fragment cache is its database-oriented cache maintenance. The original <Cacheable> attribute sent an additional argument along to its code generator, “FlowerPots”. This string matches the name of a database table which MyStem now depends on, and anytime the contents of table FlowerPots changes, the cached results of MyStem are invalidated.

How does this work? Since Wasabi knows about all Cacheable functions at compile time, we can build up a dictionary which associates database table names with their dependent cached functions. Since all SQL queries go through our own SQL command wrapper, any time a SQL command is run from within FogBugz we can simply detect which tables are being changed and invalidate all associated function results.  This means that whenever I “DELETE * FROM FlowerPots”, the cached results of MyStem are also deleted so no stale potted flowers are incorrectly drawn. Although it may seem that detecting any and all updates to FlowerPots is too aggressive of a cache-invalidation tactic, we do have all types of more specific tools which let programmers choose whether or not to invalidate the cache during specific queries.

As mentioned by Stefan, one of the nicest things about letting Wasabi generate code at compile-time is that the user doesn't have to wait for these associations to be created and for cache maintenance to be performed.

Being able to simply add <Cacheable(“TableName”)> to any expensive function and gain safe, database-dependent fragment caching has been an enormous advantage, particularly when generating large chunks of HTML. There is no way FogBugz 6 would perform anywhere near its current speed if we weren't able to make use of easy tricks like <Cacheable> and <Memoizable>.

In the future, when we have nice serialization/deserialization worked out for all of our objects, we'll also be able to cache objects (which we can already memoize since in this case they're simply stored in memory).


Categories: Wasabi, FogBugz

Actions: E-mail, Permalink


Customizing Your FogBugz Wiki and Editor

Wednesday, November 28, 2007 by Ben Kamens

Customization of your wiki’s look and feel is easy in FogBugz.  Wiki administrators have complete control over both the wiki’s template (which lets you control the actual HTML code that will enclose each article’s content) as well as its stylesheet (which controls the look and feel of your HTML).

Editing a template is straightforward for anyone who has done any basic work with HTML or CSS. You have complete control over the HTML and there are plenty of other variables to have fun with that’ll let you embed everything from RSS links to styled wiki search boxes.  A nice example of template customization has already popped up thanks to the owners of the Business of Software Wiki.

However, one of the coolest (and, I’ll guess, lesser known) features of wiki customization is the fact that the WYSIWYG editor picks up on your stylesheet changes and lets anyone easily use your custom styles when editing an article.

I’ll walk you through some basic template changes and examples so you can see the WYSIWYG editor’s reactions.  For a reference list of elements you can customize, see this post on the FogBugz Knowledge Exchange. Check this out:

Say I start with a brand new wiki and template.  The default stylesheet gives me a few font and paragraph styles:

These are the styles and block formatting options available right off the bat with a new template.

Now, what if I want to add my own font, paragraph, andheader styles?  If I edit my template by adding the following CSS…

span.MyCustomFont { font-family: Arial; font-weight: bold; }
p.Emphasize { background-color: yellow; font-size: 16px; }
h2.ExtraSpace { padding: 20px; }

...I wind up with the following options:  

Anybody editing an article within this wiki (or any other wiki using this template) will now see these style options in their respective dropdowns.  Using them in the WYSIWYG editor will also show you the resulting style. The text below uses both “MyCustomFont” from the font menu and the “Emphasize” paragraph style.

Fonts and paragraph styles are not the only customizationsavailable in the FogBugz WYSIWYG editor. As seen above, header styles can be added via the “h2.YourStyle”syntax.  Tables inside the editor workthe exact same way.  After adding thefollowing CSS to my template… 

table.Ludicrous { font-size: 100px; BORDER-COLLAPSE: collapse; }
table.Ludicrous TR TD { border:1px solid black; color: white; background-color: black; }
table.Ludicrous TR TH { border:1px solid black; background-color: blue; }
table.Ludicrous TD.LessLudicrous { font-size:20px; background-color: white; color: black;}

…I get the following table style options:

…which results in some fully customized, albeit ridiculous, table formatting.


Once we’ve created this “Ludicrous” table, we can take advantage of the custom table cell style we added with the “table.Ludicrous TD.LessLudicrous” line.  Place your cursor inside one ofthe table cells and check out the cell formatting dropdown:

Choosing “LessLudicrous” will apply our new style.

Hopefully wiki administrators will find lots of good uses for the custom styles and control provided by FogBugz templates and the WYSIWYG editor.


Categories: FogBugz

Tags: , , ,

Actions: E-mail, Permalink