The FogBugz Plugin Architecture
August 10th, 2009 by David Fullerton
Feature bloat is a familiar problem for any software project, especially one approaching a decade of continuous development. For FogBugz, the sirens' call of bloat usually sounds something like this:
"Hi Fog Creek! I love the new tagging feature in FogBugz 7! My only problem is that I don't seem to be able to create a reverse-alphabetical, zebra-striped tag cloud. In my organization we only use reverse-alphabetical, zebra-striped tag clouds and without them our entire production line has ground to a halt! How soon will you be adding this feature?"
Now, 90% of our users have never even heard of reverse-alphabetical, zebra-striped tag clouds, and adding support for it to FogBugz will just add one more confusing feature and make FogBugz as bloated as the Michelin Man after Thanksgiving dinner. But this is something that at least one customer really needs, and we don't want to just ignore them forever.
In the past we've always dealt with the threat of bloat by implementing the 90% solution really, really well. The 90% solution means building the features that most people are asking for while we punt on the other 10%. This lets us spend our energy on making sure the features for the 90% are both powerful and easy to use, and make our customers ridiculously happy.
Unfortunately, adding 90% solutions together doesn't mean that 90% of our users are 100% happy. That last 10% is never the same 10% for everyone, so we end up with an insane Venn diagram of competing desires, some of which may even be mutually exclusive! With every release we address the biggest of these, but we also accumulate a few more customers who just wish we would add that one feature that would make FogBugz perfect for their organization.
One of our goals for FogBugz 7 was to provide a way of addressing all of these requests for features, without compromising the core simplicity and elegance of FogBugz out-of-the-box.
Sometimes we get carried away trying to make everyone happy
Plugins provide an elegant solution to this problem: create snap-on features that integrate seamlessly with FogBugz, but can be added and removed with the click of a button. With a simple plugin, we can satisfy the 10% of customers dying for that reverse-alphabetical, zebra-striped tag cloud without the other 90% ever having to know that it even exists.
Even more exciting, plugins finally allow other people to modify FogBugz without having to dive into our scary Wasabi source code. Anyone can write their own custom plugins, and then share them with the world in the FogBugz Plugin Gallery.
Installing plugins is dead simple: if you have FogBugz on your own server just download the zip file from the Plugin Gallery and click the upload link in FogBugz. For FogBugz On Demand, just follow a link to the Plugin Gallery from your FogBugz account and it will be automatically installed when you click the install button.
Install plugins from the FogBugz Plugin Gallery with just a few clicks
The Plugin Architecture
So how did we actually do it? First we had to port FogBugz from classic ASP to ASP.NET, but that's the subject of another post. Once we did that, we were able to use some cool facilities built into .NET.
In FogBugz, plugins are simple .NET assemblies (DLLs) loaded into FogBugz. As such, they can be written in any .NET language (C#, VB.NET, F#, etc.), and can leverage the powerful .NET Framework libraries.
These plugin assemblies communicate with FogBugz in two ways: by calling methods on the API, and by implementing special interfaces.
The interfaces that a plugin implements determine when and how FogBugz calls into the plugin. Let's say a plugin wants to add a color-coded label to a case, and it should be displayed as a column in the case list. All the plugin developer has to do is implement IPluginGridColumn, which specifies how to display the column and sort it, and FogBugz does the rest. When FogBugz needs to display the column, it looks up the plugin and calls the appropriate methods (see an example).
Inside those methods the plugin will probably want to query FogBugz for some information. It does this using special API methods that let a plugin do things like query the database, edit a case, or add a notification to the top of the page. Since the plugin is running as an assembly on the server, there are no crazy hoops to jump through: for example, loading a bug is as simple as calling api.Bug.GetBug(caseId)!
Since these interfaces and API methods are the heart and soul of the plugin architecture, every single method and property has full Visual Studio IntelliSense documentation, and every interface has an example implementation in the Plugin Developers Wiki.
The FogBugz API includes complete Visual Studio Intellisense
What Plugins Can Do
So what exactly can Plugins do? A lot, and we're actively expanding it. Here are just a few quick examples:
- Create new FogBugz pages
- Add new column types to the case list grid
- Create custom fields on cases
- Extend search with custom search axes
- Add customizable filter criteria
- Load, modify, and save FogBugz entities like Cases, Users, Projects, Milestones, Wikis, and more
- Create custom database tables
- Query FogBugz database tables
- Create FogBugz "Editable Tables" with AJAX popups
- Write raw binary data and set content headers to allow custom file downloads
- Run periodic tasks during FogBugz background maintenance
Much more information and loads of examples can be found in the Plugin Developers Wiki; many of the examples above have a complete annotated example to help developers get started!
The Kanban Board plugin adds a new column to the grid
Plugin Security and AppDomains
Being able to run directly on the server is great for plugin developers, but it could be a nightmare for system administrators, especially for plugins coming from unknown third parties. In fact, our sys admins nearly had a fit when we first started talking about how plugins would work. The problem, however, is not nearly as bad as it seems at first blush thanks to the power of .NET AppDomains.
AppDomains in .NET are a way to run assemblies in specially managed sandboxes with fine-grained control of what they can and can't do. For example, the .NET FileIOPermission specifies what directories an assembly can read or write, and the WebPermission controls what URLs an assembly can hit over the web. .NET itself defines dozens of permissions, and has facilities to allow developers to define their own.
So what permissions do we deny plugins in FogBugz? Almost everything! They are forbidden from accessing the file system or the registry, or connecting to databases, or running unmanaged code, or…well it's easier to say that we basically start with a completely blank slate, and only give plugins the handful of permissions they need to run.
"Wait!", you say. "Plugins can't access the database? How do they do anything interesting?". Plugins aren't allowed to talk to the database, but FogBugz sure can. So if a plugin wants to do something that it doesn't normally have permission to do, the request has to go through FogBugz via an API. FogBugz, in turn, is very restrictive about what it allows plugins to do: for example, every SQL query is run through a validator to ensure that it doesn't do anything dangerous.
"But what if I want my plugin to be able to talk to another database", you cry again. Well, in the 7.0 release all plugins run in a single, very restricted AppDomain, but in future releases we plan to add support for FogBugz administrators to loosen the restrictions for certain plugins known to be safe. This would allow, for example, a plugin that integrates FogBugz with a sales database, or lets the plugin access certain network resources.
We start with a blank slate and only allow necessary permissions
Enforcing FogBugz User Permissions
Of course, when loading information from the database, Plugins need to be aware of another level of security: FogBugz user permissions. Not every user in FogBugz has access to every case, or is allowed to modify a project. To make things simpler for plugin developers and to reduce the risk of data exposure, whenever possible the FogBugz plugin architecture adopts a "fail closed" policy.
A "fail closed" policy means that by default we enforce the permissions of the current user when the plugin runs. For example, if a user visits a plugin page that displays information about a case, a permission check is automatically run when the plugin tries to access the case data. If the current user does not have access to view the case, the plugin will receive an exception.
Automatically enforcing permissions means that much of the time plugin developers can write without having to worry about permissions. In the worst case, the plugin will return an error rather than revealing potentially sensitive data. In the special situations where a plugin needs to do something unusual with permissions, it can turn off the automatic permission handling on an object and manage the permissions itself.
FogBugz automatically enforces the user's permissions
The Future of the Plugin Architecture
We're all extremely excited about the first version of the plugin architecture in FogBugz 7. We think it provides a powerful foundation for a wide variety of plugins. Already we've implemented several long-requested features for FogBugz as plugins: Workflow, Custom Fields, Project Backlog, and CSV Export (built-in to FogBugz 7) are just the start!
We're also excited about the plugins under development that will start to push the architecture to its limits. The Balsamiq Mockups plugin allows creating beautifully simple user interface mockups from inside FogBugz which can be attached to cases and wiki pages. The Kanban Board plugin allows creating an Agile Kanban board with FogBugz cases, complete with AJAX drag-and-drop. You can browse these plugins and many more at the FogBugz Plugin Gallery.
FogBugz is just getting started with plugins. Stay tuned in the coming weeks and months for more and more feature-loaded plugins, along with expansions and additions to the plugin architecture that will allow developers to do more and more. If you have an idea for a plugin that you'd like to see, you can even suggest it to the development community at the FogBugz Plugin Ideas uservoice site, and maybe somebody will take it and run with it!
Or, even better, stop waiting for somebody else to add reverse-alphabetical, zebra-striped tag clouds and get started writing it yourself! The FogBugz Developer Wiki has all the information you need to get started, and the Plugin Developer Discussion Group is a great place to get all of your questions answered.
A simple "Hello, World" plugin is only a few lines long