Development

Supporting developers and IT folks is worth the extra effort

I’ve noticed an interesting trend among our customers…the developers start really small, but buy more over time. Sometimes a lot more. This makes sense, of course, since freelance developers move from project to project and each new project has a new budget and a new server. Our product makes managing a server and the websites on it really, really, incredibly easy and fast, so folks who’ve used it end up using it on every web server they deploy. We’ve had dozens of web developers buy our cheapest offering one month and the next buy two or three of the more expensive versions.

Developers are also more demanding than most customers. They recognize when difficulties they are having are failings of the software rather than failure of their knowledge of the subject matter (generally), and so they file more bugs and ask harder questions. So far, you’re probably with me that developers are awesome users.

They also have some flaws, as users. They want things that are only useful to other developers. And I’m not just talking about APIs here. APIs are awesome, of course, and we’ve got APIs all over the place, and you should, too. But sometimes there are opportunities for making a product more useful to developers just by the features that you include.

An example in our product is support for private IP addresses, dynamic DNS, and the ability to operate correctly behind a NAT firewall. This is actually quite a bit of code to support, and I was reluctant to add it. Our product already has too many features and too many options (flexibility can be a curse…but it’s also nice to always be able to answer “yes” to the question “does you product support X?”, so it’s a wash), so adding a bunch of stuff that’s only useful to web developers on their development servers in their home office seemed maybe a little crazy.

But, we did it anyway (OK, Jamie did it without really consulting with me on it, because his thought process on just about any new feature is, “yeah, I can do that” and then he does it that evening and launches it the next day). And, it turns out I was wrong. It’s worth the effort, the additional support expense, and the added options, because developers and IT are the key to customers for any technology product. If the nerd working on a project likes you and finds what you do valuable, they will recommend it at every opportunity within their company, or on every project they work on if they are contractors. If you save the IT department, or contractor, time and repetitive effort, you will have the whole company using your product in short order.

From here on out, when one of our developer customers has a request I’m going to think long and hard before dismissing it as too much of a niche feature. If I can visualize a large number of web developers across the world breathing a sigh of relief when we roll out such a feature, then we will roll it out. Our primary customer will always be web hosting providers, the bigger the better, and their usually non-technical end users. But, if we piss off the developers who actually build the websites, I’m certain we’d regret it.

So, what’s the punchline? What can you do about it? Well, if you’re building a web service that’s useful to a large segment of people, you may have built something that can save the IT departments in major companies time and repetitive effort. An obvious example of a fellow Y Combinator company is Wufoo. They make a really nice form builder service. Seems simple, until you realize how many forms could be used in day to day operations of most businesses…got a trip to plan and need to know who’s going? got a software audit coming up and need to know who’s using what products? got T shirts to distribute to customers and need to know where they are and what sizes to send out? Wufoo’s got it covered and you don’t need to bother your IT department about building another stupid form!

Online office productivity software is another obvious example. Saves IT departments tons of time and effort. Getting PowerPoint or Office or whatever for a system that doesn’t have it is a PITA in most companies. File a requisition, wait for an IT to get approval from your manager, wait for IT to license it and install it, find out that it needs more memory or disk space, requisition more memory and disk or new computer, etc. Three weeks later, the project has come and gone and now you’ve got PowerPoint or Office and don’t need it anymore. If you’ve got IT on your side, they will instead say to their user, “Try Zenter, it’ll let you do a nice presentation right away.” Or Google Apps. Or whatever…lots of possibilities there for making the life of IT guys better.

Development
Startups

Comments (0)

Permalink

Being everywhere is a killer feature

I’ve been in the planning stages of a new product for Virtualmin users (and eventually for anyone who has a web server), which would predominantly run on on our servers. It would be our first application that allows us to choose the implementation language (our current codebase is over nine years old and includes hundreds of thousands of lines of Perl…we won’t be changing languages for our installable products, though we do look forward to Perl 6).

This got me to thinking about language popularity. PHP is, by far, the most popular language for the web. It’s also the worst among the popular choices. At least it’s the worst by every metric I consider important…except one: Availability.

PHP syntax is horrible. The vast majority of PHP code in the wild is horrible. The PHP standard library is inconsistent from top to bottom. It encourages bad practices, and dangerous behavior is the default for many PHP functions. Only in the past few years has PHP acquired the ability to safely and sanely use databases. The database problem is the cause of a huge percentage of the horrible PHP code in the wild…and folks are still writing code using the ugly way to access databases, despite better ways existing, because it’s all they’ve ever seen. PHP code, in general, is hard to maintain, hard to read, and often poorly documented (again, a lot of the documentation covers historic techniques that are simply dangerous). The object system is bolted on. It has no functional programming constructs to speak of.

So, why the heck is it so pervasive? Why on earth would smart people choose to use a language that is less effective for almost every non-trivial task in web development? And, I should be clear, smart people do choose to use it, and some fantastic applications have been written in it. This isn’t a situation where a bunch of idiots are using it because they don’t know better. Some of the coolest web applications are written in PHP, by some of the coolest developers working in the field.

The answer, and an answer that the Ruby On Rails folks, Pylons folks, Django folks, Perl Catalyst folks, etc. should all pay really close attention to (assuming they want their apps to be widely used), is that PHP is pervasive because it is pervasive. Tautology, I know, but what I’m trying to get at is that people code in PHP because they know it will work wherever they go or whoever they hand it off to. They know every web developer will be familiar with it, and will have access to a webserver that can run it. They know it will run on the cheapest DreamHost account, the midrange TextDrive account, or the most expensive RackSpace managed server, without modification and without jumping through too many hoops. They also know that if they have to hire someone to maintain it, or round up a community of users to help add features, there are no shortage of knowledgeable developers (it’s hard to find the good ones, as in any language, but some things don’t require good developers). Further, it’ll be reasonably fast, if they don’t do anything stupid or pathological with databases or files.

In short, if you want your installable application to be used, probably the single most useful thing you can do is write it in PHP. You also need to make it do something good, of course, and without huge glaring bugs. I’m guessing, but based on UI studies that show that for every field you add to a form, the completion rate drops by a huge amount (an order of magnitude in some extreme cases), I believe that an installable PHP application will initially get ten times the users of a Ruby On Rails application that does the same thing. I don’t have numbers to back up this bold assertion, of course, but I’m pretty sure that’s the way it will play out, unless and until Ruby On Rails is as pervasively available as PHP (which is unlikely to ever happen).

I don’t like this fact. I’m obviously not a fan of PHP as a language. I can’t think of a commonly used web development language that I would choose PHP over from a purely technical standpoint, except Java or C# (and even those have certain areas where they are particularly strong and well-suited for the task). But if I were building a small application (by small, I mean anything under 50,000 lines of code) that I wanted thousands of users to install, I would probably write it in PHP.

Of course, I’m not building an installable application this time…so I’ll be picking among the languages I like (Perl, Haskell, Python, Erlang). But my moments of blinding insight are few and far between, so I thought I’d share this one with the 12 people that visit my blog.

Development
Open Source

Comments (21)

Permalink

YACBGA (Yet another CSS bar graph article)

OK, so I’ve read the existing articles about building bar graphs with CSS. All are great work. But they didn’t exist before I needed such a creature…and now that they all exist, I still need functionality that doesn’t exist in them. What, you might ask, could I possibly need from a bar chart that these examples do not provide? They look good, they’re generally clean, and they work in all (or most) browsers. Ah, well, we’re developing web-based system administration tools, and one of the things we do a lot of (and will be doing a lot more of in the future) is presenting data about the system to the user in a clear graphical form.

System data does not generally come in the form of multiple numbers to be presented in single part bars. No, more interesting is “how much is available and how much is used”, and even more interesting is, “what is it used for”. For example, we have disk usage graphs that use a different color for each type of usage: mail, web, databases, unused. Ah, so there’s the problem. We need a single bar with multiple elements, and so far, none of the examples provides that. Now, if you happen to be looking for a multi-segment bar CSS bar graph, today is your lucky day, because I’m going to show you two examples.

Example 1 (Apples to Oranges derived)

Note: I finally tested this on IE7 (I run Linux, so I’m using Wine and the ies4linux script to setup IE…IE7 didn’t work on my box until recently), and, of course, it looks atrocious. If anyone knows a fix, I’d appreciate hearing it.

The first is the most recently developed, based on the nice Apples to Oranges definition list example. It works well in all of the browsers I’ve tested except the bastard IE, in which it repeats a piece of the image vertically no matter how I try to stop it from doing so. Otherwise, these graphs rock.

CSS Bar Graph with multiple sections

Click here for the live example (which you can view the source of to see how it’s done). Now, in addition to the IE nastiness, there’s two other nasty things about it…you have to do math, and the order in which you insert the colors matters. Math is required because it’s the only way I could get the inner sections to display correctly. Otherwise they either drop below the bar, or they completely cover the bar, depending on how they’re styled. I would more than welcome fixes for any of those issues…but especially the IE thing.

I only used four colors in the example, but it scales out as far as you need just by copying the div.yellow or div.green section and changing the color.

Example 2 (I think I came up with this one all by my lonesome)

This one is a bit less CSS and more HTML, but it is also cross-browser and doesn’t even really require CSS (though, it too can end up going astray on IE–it’s unbelievable how little time I spend on web design work, and yet I still manage to hit IE bugs several times a day…I always thought web designers were just bitching). It does require tables, despite it not seeming to on first glimpse. In fact, I thought I would rip out the tables we use in our actual UI to simplify the example, but found that the images picked up some extraneous space underneath, because they are block elements and the browser is leaving room for tails on g and p and q!

So, here’s what it looks like:

Table and Img Bar Graph with multiple sections

Click here for the live example. This one is probably going the way of the buffalo in our UI because wrapping everything in tables is a thing of the past, so it’ll begin to look funny soon. Scaling this one out to more colors is too obvious for me to bother talking about, and you don’t have to do any math (beyond figuring out percentages).

As mentioned, this one also has an IE issue. Because IE is retarded, it’s easy to end up with oddly sized graphs…it doesn’t handle percentage sized tables, so if you use percentages in the wrapping table widths, you’ll get broken graphs.

Development

Comments (3)

Permalink

Using yui-ext widgets from markup

I’ve written several posts about using yui-ext in Virtualmin’s new GUI, and I recommend it whole-heartedly to folks looking for a really nice set of JavaScript widgets. It, like the YUI library it is based on, is targeted to widgets useful in general applications, rather than very simple task-focused applications. It’s great for the kind of applications that have a lot of data to present, lots of pages to navigate via roughly random access, and if care isn’t taken with presentation could place quite heavy demands on the end user. We’ve got that kind of application. Virtualmin does a lot of widely varied tasks. So, yui-ext is a great fit.

But, one place where yui-ext falls down, that some other libs do better, is in building widgets from markup. It pretty much completely lacks the capacity to build any of its complex widgets from HTML markup. They must be generated with JavaScript. There are clever things like a loader function that’ll load a JSON object automagically…but nobody who’s making an app that can target a wide array of client devices, like we are, is going to be able to deliver data in that form (at least, not without major re-writes of significant portions of the application, and a new layer of abstraction).

Nonetheless, the widgets are so nice that I’ve looked past that, and with some help from Jack Slocum, I’ve begun building functions that will parse the tree and replace the elements with the yui-ext elements.

Ext.tree.TreePanel.readMarkup = function(root, el){
    function readBranch(pnode, ul){
        var cn = ul.childNodes;
        for(var i = 0, len = cn.length; i < len; i++){
            var li = cn[i];
            if(li.tagName && li.tagName.toLowerCase() == 'li'){
                var a = li.getElementsByTagName('a')[0];
                var cul = li.getElementsByTagName('ul')[0];
                var node = pnode.appendChild(new Ext.tree.TreeNode({
                    id : a.id,
                    text : a.innerHTML,
                    cls: a.className,
                    href : a.href
                }));
                if(cul){
                    readBranch(node, cul);
                }
            }
        }
    }
    readBranch(root, YAHOO.util.Dom.get(el));
    return root;
};
 
treeMenu = function(el){
  // create the hidden root
  var root = new Ext.tree.TreeNode('');
 
  // get the existing markup element
  var prenl = el.getElementsByTagName('UL');
  var nl = getEl(prenl.item(0).id);
 
  // read it
  Ext.tree.TreePanel.readMarkup(root, nl.dom);
 
  // remove the markup
  nl.remove();
 
  // initialize the tree
  var tree = new Ext.tree.TreePanel(el, {rootVisible:false, enableDD:false, animate:true});
  tree.setRootNode(root);
  tree.render();
};
 
var Trees = function(){
  return {
    init : function() {
      // Get all of the div ids of class treemenu
      var elements = YAHOO.util.Dom.getElementsByClassName('treemenu', 'div');
      YAHOO.util.Dom.batch ( elements, treeMenu );
    }
  };
}();
 
YAHOO.ext.EventManager.onDocumentReady(Trees.init, Trees, true);

Trees.init, which is run once the document has finished loading searches the DOM for elements of class “treemenu” and then uses a YUI batch to iterate over all of them with the treeMenu function. The treeMenu function is merely a wrapper for the hard work being done by readParse, which runs through the tree to figure out the structure of the list and put it into a TreePanel.

The markup read by this function looks like:

<div id="nav" class="treemenu">
  <ul id="navul1" class="nav-list">
    <li><a href="...">Leaf Node</a></li>
    <li><a href="...">Node with children</a>
      <ul>
        <li><a href="woot">Chillun 1</a></li>
        <li><a href="wooty">Chillun 2</a></li>
      </ul>
    </li>
  </ul>
</div>
 
<div id="nav2" class="treemenu">
  <ul id="navul2" class="nav-list">
    <li><a href="...">Another Leaf Node</a></li>
    <li><a href="...">Second Node with children</a>
      <ul>
        <li><a href="woot">Chillun 1</a></li>
        <li><a href="wooty">Chillun 2</a></li>
      </ul>
    </li>
  </ul>
</div>

The same kind of technique can be used for all of the Ext functions, though I believe I'll be able to use a mix of markup and JavaScript for the remainder of my conversion...the tree menus were particularly challenging because of the way they are generated in our code. Continue Reading »

Development

Comments (0)

Permalink

Some great technical and startup-related videos and audio recordings

While I enjoy seeing Mentos and Diet Coke symphonies as much as the next guy, I find myself spending a lot more time watching technical videos on Gootube and Yahoo! Video…Some of them are simply fantastic, so I thought I’d share a list of my favorites.

Guy Kawasaki’s Art of the Start presentation – Holy crap. You don’t get much better than this. Best 40 minutes you can spend if you’re starting a company or thinking of doing so.

YUI Theater – This one is awesome. Douglas Crockford has a three part workshop series that’s a soup to nuts JavaScript and DOM introduction. I learned JavaScript (by some definition of “learned”) in one weekend from this series of videos. There’s also a nice video in which Joe Hewitt introduces his FireBug, which is the greatest thing to happen to Firefox since…ummm…ever. Nothing else comes close to FireBug, and it’s been absolutely vital to my successes with JavaScript, so far. There’s a lot of other cool stuff in FireBug that this video doesn’t cover, but it’ll get you started.

Anatomy of a Debian Package – Unfortunately, I find Debian and Ubuntu documentation about packaging to be horribly opaque, due to lack of examples of the process of packaging. This video runs a little slow, but it’s at least a usable source of information. I really just want to see a “create this file, put this into it, now run some-command to build the software and bundle it into a package…and here’s the command you use to sign it”, similar to several existing examples for RPM. That part of the video starts at about 20 minutes in. I wish some of the docs would have pointed out dh_make and how to use it…would have saved me hours of frustration when I first started packaging debs. And, I also wish some of the too many options for building debs would be deprecated. There’s just too many, “or you could do it this way…or you could to it that way…or you could do it typing only with your toes”. Let me explain something to the Debian folks: I never want to build the damned thing manually. Get mentions of that stuff the hell out of the introductory packaging documentation. Same goes for signing packages and repositories. I don’t care how the hell you implemented package signing…show me what command to run and tell me where the resulting files (if any) go in my apt-get repositories. (Actually, this video doesn’t do much good with package signing, either.)

Competing on the Basis of Speed – This one isn’t particularly technical, but it’s interesting. I believe running really fast is absolutely key to success in the current tech industry. The key to this is stripping away things that are pointless or stupid about your business, product, or process. The trouble is figuring out what is pointless and/or stupid. Being a young and tiny company, it seems easier…but I suspect that even our two-man shop has some stupid and pointless pieces, which I hope we’ll shake out pretty quick once we have some infrastructure in place for measuring how our customers are really using the product.

Development
Linux
Startups

Comments (0)

Permalink

Hitting the road with Webmin (and a perl function for detecting mobile user agents)

As I mentioned in a previous post, I’ve been hanging out with a lot of mobile application developers lately. Now, I’ve never made a secret of my disdain for telephones. But seeing the things they’re doing with mobile technology got me to thinking. What’s a cool use for mobile technology that applies to what I’m doing every day with Webmin and Virtualmin? Well, it turns out it’s the same thing that gets me to spend $72/month on a Sidekick. Being able to administer my server, fix customer problems, and deal with email, while traveling…even if there’s no laptop in sight or WiFi in range. So, on our way home from dinner last Tuesday night, Jamie and I chatted about what keeps Webmin and Virtualmin from being useful on our phones. Technically we can login to Webmin via our phones (Jamie has a Treo and I have a Sidekick), the problem is that it’s so slow to load all of the graphics and deal with the frames and menus (not to mention clicking “OK” when any site with more than a few lines of JavaScript eats up all of the memory on my Sidekick and it can’t keep running them), that it’s faster to drop in via SSH (both of our phones also have SSH clients) and do it all on the command line. Despite the horrid little keyboard, this is faster than using the web interface.

So, we came to the conclusion that we needed a new theme just for mobile phones. A theme targeted to the most common operations, and stripped of everything unnecessary, so it would be fast despite the client being slow and limited. But, we’d also have to address the problem of showing that theme only when a mobile client connected, since we can’t reasonably make an AJAX-filled fully graphical theme degrade all the way down to tiny. So, Jamie added some code to detect mobile browsers:

# is_mobile_useragent(agent)
# Returns 1 if some user agent looks like a cellphone or other mobile device,
# such as a treo.
sub is_mobile_useragent
{
local ($agent) = @_;
local @prefixes = (
    "UP.Link",    # Openwave
    "Nokia",      # All Nokias start with Nokia
    "MOT-",       # All Motorola phones start with MOT-
    "SAMSUNG",    # Samsung browsers
    "Samsung",    # Samsung browsers
    "SEC-",       # Samsung browsers
    "AU-MIC",     # Samsung browsers
    "AUDIOVOX",   # Audiovox
    "BlackBerry", # BlackBerry
    "hiptop",     # Danger hiptop Sidekick
    "SonyEricsson", # Sony Ericsson
    "Ericsson",     # Old Ericsson browsers , mostly WAP
    "Mitsu/1.1.A",  # Mitsubishi phones
    "Panasonic WAP", # Panasonic old WAP phones
    "DoCoMo",     # DoCoMo phones
    );
local @substrings = (
    "UP.Browser",         # Openwave
    "MobilePhone",        # NetFront
    "AU-MIC-A700",        # Samsung A700 Obigo browsers
    "Danger hiptop",      # Danger Sidekick hiptop
    "Windows CE",         # Windows CE Pocket PC
    "Blazer",             # Palm Treo Blazer
    "BlackBerry",         # BlackBerries can emulate other browsers, but
                          # they still keep this string in the UserAgent
    "SymbianOS",          # New Series60 browser has safari in it and
                          # SymbianOS is the only distinguishing string
    );
foreach my $p (@prefixes) {
        return 1 if ($agent =~ /^\Q$p\E/);
        }
foreach my $s (@substrings, @mobile_agents) {
        return 1 if ($agent =~ /\Q$s\E/);
        }
return 0;
}

So, there’s a quick and dirty perl function (BSD-licensed, like all of Webmin core) that makes detecting a mobile client easy. Of course, there’s more granularity out there than “mobile” or “big” client. That’s where something like WURFL will come in (that’s not gonna happen in Webmin in the short term, but it’ll come later, as our themes get smarter and more dynamic). Anyway, so now that Webmin can detect what kind of client it’s talking to, it can choose a theme to suit the target. We’ve made a new dead simple, zero-graphics, menu-based theme for Virtualmin, that allows really quick administration on even the most limited web client.

Now, as of version 1.328 of Webmin and next weeks version of our themes, you’ll get a different interface based on how advanced your client is. Small clients will see something like this:

Screenshot of Virtualmin mobile theme

While big clients will see one of the all-singing, all-dancing, maybe AJAX-filled, themes with images and such. Everybody wins, and now I don’t really need an SSH client on my next phone–this interface is faster, and if I really, really, need a shell, I can wing it with the non-interactive CGI-based Command Shell module in Webmin. The neat thing is that this will also work in Usermin, so we’ll have a kick ass webmail client that is fast and clean in our mobile devices. I’m glad I’ve been thrown in with a bunch of mobile guys lately. It’s an eye-opening experience.

Development
Linux

Comments (3)

Permalink

Usermin gets a face lift

For the past couple of weeks, I’ve been focusing on Webmin/Virtualmin, and how much nicer they could be with an AJAX face-lift. Well, we gave a demo of the product before a bunch of really smart folks last night (many of whom had already seen the ongoing UI work on Virtualmin), so I wanted to have something new to show. So Sunday night I hunkered down and began the port of the first AJAX-enabled Webmin theme to Usermin. By the time of the demo the next evening it was ready to roll (or, at least, ready for a demo in which I could carefully navigate around all of the new bugs introduced by all of the new JavaScript code). The funny thing is that I believe the theme in Usermin is somehow more immediately “cool!” than the much harder project of getting it working for all of Webmin and Virtualmin (which are still under way, and won’t wrap up for another week or two).

Webmail is one of those things that has become a fact of life for nearly everybody. Yahoo and Google raised the bar with their next generation AJAX webmail products, and we’re not quite there with Usermin. But the new theme makes it more obvious how great the core mail reader in Usermin really is. With merely a pretty and AJAX-ified face, it has suddenly become the best Open Source webmail available (possibly excepting RoundCube, which is admittedly awesome–but give me another week or two…I only have to write the front-end to an already robust and complete mail client, while they’re writing everything from scratch). Sometimes, even I’m surprised by how much amazingly useful code is in Webmin and Usermin. Jamie’s like a damned machine, just churning out thousands of lines of great software every year.

New theme for an old mail client

And, sure enough, at our demo, the mail client drew quite a bit of the attention. Everyone understands mail clients. It’s like the mythical bike shed. Maybe when all of this is done, we’ll roll up a special version of Usermin that is focused on webmail. I think that’s probably why it isn’t used more. Nobody knows to look at Usermin for webmail…so they end up with SquirrelMail, which is fine, but there are several better products out their to satisfy the need. (Horde also has an image problem…it has a great mail client, too, but it’s far less popular than Squirrel.)

Development
Linux

Comments (4)

Permalink

Hooking up

The cool bit about AJAX is, of course, the A for “asynchronous” thing (what, you didn’t really think it was the X for “XML”, did you?). Sure, everybody and their brother is saying they’ve got an AJAX app because they’ve added a highlight effect or something wobbly on one of their elements. But until you’re whipping things around in the DOM to make the page content change based on the users behavior, and without reloading that page, you’re not doing AJAX. Me being an expert and all, having worked with JavaScript for all of a week, I thought I’d share with you our first real AJAX element in Virtualmin: hooking up a dropdown menu to the contents of a tree menu just below it. When a new domain is chosen in our domain selector, the menus below it update to link to the administration pages for that domain.

Being a perl coder, I’m lazy, so rather than wrap up my menu as a JSON object and fire it off for rebuild, I’m just grabbing an HTML menu and re-running the treeMenu function I wrote a couple of weeks ago in the beginning of this adventure on that div that is being replaced. This stuff is easy! I can’t figure out why I haven’t been doing it all along.

So, here’s what we have in the markup to specify what will be replaced:

<div class='menu' id='domain'>
...blahblahblah...
</div>

Hmm…that doesn’t look like a lot of markup. But it’s all I added for this trick, so it’s all we care about–I could wrap anything in that sucker and replace it with a simple JavaScript function.

So, now in the dropdown, we have an onChange function call:

<select name="dom" size=1 onChange="rebuildMenu('domain',this.form.dom)">
<option value="116468149219819" selected>domain.tld
<option value="116899265022946">fcgidtest.com
<option value="116897866018104">suphptest.com
</select>

It calls a function called rebuildMenu, which is this:

rebuildMenu = function(menu, id){
  // Load a new menu for id and insert it in div menu
  var div = document.getElementById(menu);
  var dom = id.options[id.selectedIndex].value;
  var sUrl = "menu.cgi?dom=" + dom + "&menu=" + menu;
 
  var handleSuccess = function(o){
    if(o.responseText !== undefined) {
      div.innerHTML = o.responseText;
      var elements = YAHOO.util.Dom.getElementsByClassName('treemenu', 'div', menu);
      YAHOO.util.Dom.batch ( elements, treeMenu );
    }
  }
  var handleFailure = function(o){
    div.innerHTML = "Loading new menu failed.<p>"
  }
 
  var callback =
  {
    success:handleSuccess, 
    failure: handleFailure 
  }; 
 
  var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
}

And that is where the magic happens. Whenever the selected item in the dropdown changes, it fires up this function with two arguments, the menu to load (which doubles as the id in the dom to load it into), and the domain id for the menu. I’m using the YUI util library to make the request for me with the URL built based on the information sent from the onChange call. All very straight-ahead, though because I’ve got a YUI-Ext tree built from markup, I have to call that treeMenu function again on everything that got reloaded (or I’d have a plain ol’ UL there after the update). Surprisingly, it all works.

Next up, making the content pane update based on the selected item in the dropdown. This sucker’s gonna look like a desktop application before too much longer. Rich UI, here we come!

Development

Comments (0)

Permalink

Our new rule about icons

So, Webmin is a very icon-heavy product. There are several hundred icons in the default Webmin theme, and the first iteration of a Virtualmin theme. We had loads of new icons drawn by David Vignoni in the style of his Nuvola icon set, and I’m happy to recommend his work to anyone and everyone. He’s among the best icon guys working today. But we’re done with icons in Virtualmin. Mostly.

The problem with icons, as I intuitively knew, but had explicitly pointed out to me by Evan, the designer on the SocialMoth team, is that they introduce a new language to your application. When the elements are well-known icons, or mild derivations, it’s not a problem and might even enhance the users comprehension. But when everything has an icon and the icon takes the focus away from much clearer language, it’s just confusing. So the last several iterations of our Virtualmin themes have been moving away from introducing new iconography for every element of the interface, and we’ve also been spending some time on testing and clarifying the text to be more clear and concise.

Just in case you’ve never seen the old Webmin icon model, here’s a shot:

Whoah! Too many icons!

Crazy, huh? Sure, part of the problem is the uglitude of the art-work. Jamie’s never been accused of being a great graphic artist. So, my focus for many years was replacing the icons and the colors with something nicer. I believe it even looked pretty good by the time of the Nuvola versions of my themes. But it didn’t solve the problem caused by so many icons…new language that the user had to learn while using the product. Sure, everything with an icon also had a text description…but it’s clear that many users also took time to make sense of the icons.

So, the rule in our new design is “icons only when they represent well-understood concepts in well-understood ways, and maybe when they don’t get in the way and make things look nicer”. This is the only place, so far, where I’m convinced that icons aren’t doing harm, and its even one of the few spots we didn’t have icons in the previous theme:

A few icons, tastefully placed.

So, we’ve got a “home” icon, well-recognizable as such, to send users to the default first page which shows lots of stats about the system. Then we have an edit button…looks like all edit buttons ever, for editing the selected domain; a mail and FTP users icon that looks like users; a databases icon that looks like the database icon in several products on Windows, Mac and Linux; and finally, the Install Scripts link has an icon showing a page with code brackets (this one might be a slight stretch, since we can install scripts other than PHP scripts…and only PHP uses <> in a manner similar to HTML, but it’ll have to do, since nothing else is generic enough to encompass all languagses). I don’t believe it will slow folks down, and will probably help a bit, but if user testing proves otherwise, it’ll be gone before you know it.

Development

Comments (0)

Permalink

Information organization for users, not developers

As mentioned in the past two posts, we’re overhauling the interface of Virtualmin to make it easier, faster, more intuitive, and better looking. Recently I’ve been moving things around on the page and putting them into human comprehensible categories. Historically, the items in our left-hand menu, for example, were laid out in a way that was easy to code–alphabetically sorted, and pulled automatically from various Webmin modules. foreach is such a powerful construct that it’s tempting to write everything such that we can use it to build everything in the UI. But it’s not so intuitive for users…so, we’re in the midst of writing more code to accommodate a more user-centric layout (of course, we’re not looking to suffer, so we’ve figured out a way to make the modules give us back the information we need in our foreach loop).

So, without further ado, here’s where we were yesterday:

Menu midway through categorization

As you can see, if you’ve been following along, we’ve added sections to the left-hand menu to make it clear which menu items apply to the selected virtual server, and which apply to the whole system. This was the very low-hanging fruit. Very little code, very little effort, but quite an improvement–many users had a hard time grasping which sections applied to what part of the system, and I believe this will relieve much of that trouble. The added white space also makes it easier to read.

But, now, we need to address that Virtual Services and Logs menu. It’s a hodge podge of unrelated items, that show up in the same place merely because the software knows about them in the same way (these are auto-discovered at runtime based on user privileges and the enabled features of the selected virtual server). To fix this problem, we need more data than is current available, so we had to modify the Virtualmin module, as well as the theme code.

In Virtualmin, we’ve added a ‘cat’ field to the associative array that define various module actions:

push(@ot, { 'mod' => $1,
    'page' => 'index.cgi',
    'desc' => $minfo{'desc'},
    'cat' => 'webmin',
    'other' => 1 });
}

Now our theme can discover the category for any given action at the same time it pulls all of the other information. The same bit kind of category data has been added to all of the Virtualmin plugin modules, like this feature_links function in the DAV module:

# feature_links(&dom)
# Returns a link to the DAV module for this domain
sub feature_links
{
local ($dom) = @_;
return { 'mod' => $module_name,
   'page' => "index.cgi?dom=$dom->{'id'}",
   'desc' => $text{'index_desc'},
   'cat' => 'services',
       };
}

Hmmm…It’s probably worth writing a whole post just about the menu management and writing a “discoverable” plugin for Virtualmin…but we’re on the UI today, so let’s keep moving. So, now that we can categorize all of our links automatically, let’s add those smarts to our theme.pl.

Originally, in the screenshot shown above, we had this code:

  # Feature and plugin links
  @links = &virtual_server::feature_links($d);
  @flinks = grep { !$_->{'plugin'} && !$_->{'other'} } @links;
  @plinks = grep { $_->{'plugin'} } @links;
  @olinks = grep { $_->{'other'} } @links;
  &print_category_opener("vservlog", \@flinks,
    $text{'left_virtualservicesandlogs'});
  foreach $lt (\@flinks, \@plinks, \@olinks) {
    @notlt = grep { !$donelink{$_->{'mod'}} } @$lt;
    if (@notlt) {
      foreach $l (sort { $a->{'desc'} cmp $b->{'desc'} }
           @notlt) {
        &print_category_link("$l->{'mod'}/$l->{'page'}", $l->{'desc'});
        $donelink{$l->{'mod'}}++;
        }
      }
    }

Which iterates over each item in three different arrays. You might think, looking at this, that we could categorize based on the arrays that contain them. And that was what I was tempted to do at first…but those arrays are organized by the underlying code. So plinks and flinks and olinks can contain related items. One could be a core Webmin function to edit the Apache virtual domains owned by the virtual server account, while another could be the same kind of link to a Webmin module but for viewing logs. Since we believe users would make the most sense out of a “Logs and Analysis” category and a “Services” category, we’re gonna have to split those up. Thus the Webmin module links that view logs will be in the logs category, while the services such as BIND and Apache will be under the Services category. The neat bit is that every new plugin can automatically put itself into the right place just by filling in one extra field in the feature_links hash.

Post categorization

So that’s what it looks like once converted…and here’s the code:

  @links = &virtual_server::feature_links($d);
  my @cats = &unique(map { $_->{'cat'} } @links);
  foreach my $c (@cats) {
    my @incat = grep { $_->{'cat'} eq $c } @links;
    &print_category_opener("cat_$c", \@cats,
      $virtual_server::text{'cat_'.$c});
    foreach my $l (sort { $a->{'desc'} cmp $b->{'desc'} } @incat) {
      &print_category_link("$l->{'mod'}/$l->{'page'}",
               $l->{'desc'}, undef, undef,
               "right");
      }
    &print_category_closer();
    }

Interestingly, it’s a little shorter…Jamie seems to have refactored a few bits when he converted the code to supply categories.

Development

Comments (0)

Permalink