Tempo: Spit and Polish, and the Future

2009-09-02 20:00:00 -0400


We’ve been working pretty hard on Tempo since we released the new design this Summer, fixing bugs, refining the interfaces, and making those subtle tweaks that can really make a difference with a piece of software that we use frequently while we work. With that in mind, we’ve got another maintenance update out that should provide some requested changes and fix yet more “things” that needed fixing.

Here’s the full list of what’s changed, then I’ll go into what we’ve got coming down the pipe:

Stat Chart

  • New user utilization chart on Time screen, click the spark-line to activate!
  • Modal charts return! Click stats metrics and spark-line on Reports screen to activate
  • Modal edit of time entry view was a little mussed in IE7
  • After entry edit, form fields were not clearing
  • After entry add, edit, start, stop, etc causes stats on screen to update for accuracy
  • API: setting exclude-tags param in reports search was ignored, now handled like tags
  • Saved Reports with specific end dates were skewed by a time zone / serialization issue
  • Future dates on reports were being improperly set to ‘today’
  • Last active project cannot be archived or deleted (causes mad bugz, interim fix)
  • Batch tag add and remove with no managed entries resulted in an error
  • Saved reports with exclude tags were broken
  • Hide tag auto-suggest when user hits space bar (a relieving fix)
  • Expanded command line entry form
  • Busted link to Mac OS X Dashboard widget

Coming Soon

We’ve been working hard behind the scenes on another major update for Tempo that should dramatically improve our ability to set up teams and collaborate. The current system can be a little bit confusing for first-time users who are looking to set up a Tempo account for their organization and invite their teammates. In the future, the setup process will distinguish between an Account, and a User. An Account will have a distinct name, e.g. Conglomo, and a subdomain for accessing the account (e.g. conglomo.keeptempo.com). Billing functions will move to the account and users will be set up directly under the account by the account owner or any users assigned as managers.

Once we’ve got that in place, we plan on putting together an iPhone application (we’ve got some experience doing that now), making some much-needed improvements to our API, and getting to work on some of the integrations people have been asking for.

Oh, we’re cooking up a Gadget for the Windows Sidebar, too!


Hewitt is Right, Arment is Wrong

2009-09-01 20:00:00 -0400


Update: Corrected misspelling of Marco Arment’s surname.

Joe Hewitt, developer of Firebug and Facebook’s iPhone application, writes in an article titled, Innocent Until Proven Guilty, that Apple’s review process for third party iPhone apps in the iTunes App Store should be eliminated entirely. I was impressed with this, as it’s really the elephant in the middle of the room, that few commenters seem to address. The driving justification behind Joe’s short article is quite powerful:

Does that sound scary to you, imagining a world in which any developer can just publish an app to your little touch screen computer without Apple’s saintly reviewers scrubbing it of all evil first? Well, it shouldn’t, because there is this thing called the World Wide Web which already works that way, and it has served millions and millions of people quite well for a long time now.

Indeed, somehow we’ve managed all these years. And while the Internet has brought us various catastrophic influenza and garbage, the iPhone OS is already exposed to those threats, and third party applications are sand-boxed to the point of being a much poorer vector for malicious activity than looking for your classic remote buffer overflow. Hewitt continues, emphasis mine:

If you think that all apps should be held prisoner by Apple until proven safe, you should also be able to convince yourself that this is how the web should work.

I think the issue becomes a bit clearer if you change that statement to read, “that this is how your personal computer should work.”

In any event, there are many other developers out there who’ve been beating the drums of discontent, griping at Apple and earning some page views in the process, but the writing is usually done with an oddly sycophantic bent. Marco Arment, who is prone to such outbursts (wherein he righteously chastises Apple but then repeats over and over how much he loves the company and the iPhone itself, perhaps to prove his bona fides), recently pushed back against Hewitt’s sentiment, offering the article, Apple can’t stop reviewing iPhone apps. His main point, that Apple has no choice but to continue to review all apps lest some rogue iFart yield bad press or screw it up for all of us, rings quite hollow and altogether avoids Hewitt’s challenge: should this be how the Internet works? More to the point, should it be how your personal computer works?

Even if Apple stopped reviewing apps completely, they would still be blamed in the press for the effects of problematic apps. This creates a difficult position: Apple must attempt to be the gatekeeper in a market full of gray areas, but any decisions they make fall under intense scrutiny, and many decisions don’t have an indisputably “good” option.

Apple must attempt to be the gatekeeper? Arment is missing the painfully obvious, that Apple has been getting a ton of negative press, the likes of which they don’t normally ever see, thanks to their asinine review system and their poor behavior as a business partner. And that’s for the best, for App Store developers. It’s important to take off the Apple Fanboy bifocals long enough to look at this as a question of business relationships and investment risk.

Rather than fighting to abolish app review, it’s far more productive to guide and influence Apple, through both public and private interactions, to improve the system that we’re all, including Apple, probably stuck with for a long time.

Appeasement, or perhaps I should say bargaining with no chips, doesn’t seem very productive so far (is it ever?) Sure, it works well enough that popular developers like Arment have options for recourse; under the current system, if you are already a very popular developer with a wide audience, you can publicly embarrass Apple before that audience in order to get half-decent treatment as a business partner, adding value to their product and generating continued additional revenue for them.

Zetetic is the creator of the encrypted iPhone data vault and password manager Strip and the open source encryption-enhanced database engine SQLCipher.

Zetetic.Ldap - Bringing LDAP + LDIF tools to .NET

2009-08-31 20:00:00 -0400

We’ve just released the Zetetic.Ldap library to github, which makes it, to the best of our collective knowledge, the first and therefore most kickin’est .NET library for working with RFC 2849 LDIF, LDAP schema, and LDAP entry change tracking.

LDIF: This file format is still the best and most universal way to move data in or out of directory systems like OpenLDAP, Active Directory, Fedora or Red Hat Directory Server, etc. Zetetic.Ldap makes it easy to write and read directory entries with LDIF.

LdifWriter ldif = new LdifWriter(@"c:\temp\stuff.ldif"); 
ldif.BeginEntry("cn=joe cool,o=zetetic");
ldif.WriteAttr("givenName", "joe");
ldif.WriteAttr("jpegPhoto", File.ReadAllBytes(@"c:\temp\joe-photo.jpeg");
ldif.WriteAttr("dateCreated", DateTime.Now);
ldif.Close();

Zetetic.Ldap knows how to format binary data, dates, and long fields in the proper way — you don’t have to mess with it!

The same is true for reading entries out of an LDIF file.

LdifEntryReader ldif = new LdifEntryReader(@"c:\temp\stuff.ldif"); 
for (Entry entry = ldif.ReadEntry(); entry != null; entry = ldif.ReadEntry())
Console.WriteLine("Found: {0}", entry.DistinguishedName");
}

We’ve also included a command-line pivoter program that transforms LDIF files into tab-delimited files, for easy loading into Excel, RDBMS, or what-have-you. This can be incredibly useful for analyzing the data in your directory.

Change tracking: Normally, if you’re using Microsoft’s System.DirectoryServices.Protocols classes, you’ve got to manage the concept of preparing specific LDAP modification commands yourself. It can get pretty complicated, and sometimes you have to resort to trial-and-error to learn the right way to delete an attribute, for example, or rename or move an entry. Zetetic.Ldap provides a wrapper around this complexity, and will track your changes until you’re ready to send them to the server.

MutableEntry entry = MutableEntry.CreateUncommitted("cn=frank grimes,o=snpp", "person");
entry.SetAttr("sn", "Grimes");
entry.CommitChanges(ldapConnection);
MutableEntry entry = new MutableEntry(mySearchResult);
entry.ClearAttr("description");
entry.CommitChanges(ldapConnection);

Schema: This part is a little more abstract. Directory servers contain an area that describes the allowable objectClasses (types) and attributes (fields), and those attributes have syntaxes (like string, number, date, etc.). The classes in Zetetic.Ldap.Schema help you work with these data programmatically, so you can quickly discover what the server supports. Moreover, you can find out what kind of value (syntax) each attribute uses conceptually, without delving into the world of OIDs.

Here’s a snippet from the project’s unit tests, showing how to export the basic objectClass definitions from a Microsoft LDAP server.

ISchemaInfo target = new AdsSchemaInfo();
using (LdapConnection conn = new LdapConnection("localhost:20389"))
{
conn.Bind(System.Net.CredentialCache.DefaultNetworkCredentials);
target.Initialize(conn);
}

foreach (ObjectClassSchema o in target.ObjectClasses)
{
System.Console.WriteLine("oc: {0}", o);
foreach (AttributeSchema a in o.MustHave)
System.Console.WriteLine(" must: {0} as {1}", a, a.LangType);

foreach (AttributeSchema a in o.MayHave)
System.Console.WriteLine(" may : {0} as {1}", a, a.LangType);
}

We also included some helpers to manage parsing special attributes like GUIDs, or “pwdLastSet” and “accountExpires,” which are usually treated like DateTimes, but stored as 64-bit integers, sometimes outside the range of what DateTime can handle—I always enjoy telling people, “Oh, it’s simply the number of 100-nanosecond intervals since January 1, 1601 UTC!”

So anyway, when you find yourself working with LDAP or LDIF on .NET, grab the release at github, and drop us a line, we’d love to know how you like it.

Interview with Subvert.ca

2009-08-19 20:00:00 -0400

There’s an extensive email-based interview of us over on Subvert.ca. There’s juicy bits about certain large companies trying to kill off one of our early projects, column fodder, and the approaches we’ve developed for taking on new clients and starting new development projects. Here’s an excerpt:

Many people we speak to find it unusual that we don’t actively prospect, solicit, or answer blind RFPs, etc

Honestly, we like it this way: when we get a referral or start a new project for a past customer, there’s already a relationship in place. The client already knows that they can trust us, and it cuts out the entire “dance” that we’d otherwise have to do to prove ourselves. There are other benefits too.

People only ask us to prepare a proposal when they are seriously considering a project. Plus, we rarely find ourselves as column fodder behind another incumbent company—we call it column fodder when you have no hope of winning a deal and your estimate is just filling in a cell on a spreadsheet for comparison purposes.

This level of trust also means that we can work more closely with our customers to develop requirements. They take our estimates and advice seriously. In the end it works out better for everyone involved.

That's a Good Plan

2009-08-16 20:00:00 -0400


Great blog post from Carl over at nGen Works about what’s he learned over the last six years running his business, in particular this list of goals, or statements of intent:

  1. Understand what we want personally and align company and individual goals as much as possible
  2. Discuss issues openly and honestly
  3. Admit mistakes and fix them
  4. Take the high road in grey areas
  5. Consistently recognize good stuff and hold people accountable for bad stuff
  6. Support each other through successes and failures

Item 1 is not something you often see out of a company, especially the larger sort. Items 2 and 3 are things that people always struggle with, and they are a nice reminder to me who runs a musical operation on the side. It’s always tempting to make unilateral decisions or to try and cover up mistakes, especially public ones, and doing so always comes back to haunt you.