Archive for the 'free software' Category

A better DateTime widget for Django

Monday, April 26th, 2010

I’m convinced that every Django developer has struggled with how to present DateTime fields to users.  We all know and love the widgets used in the Django admin, and emboldened by the Django developers,1 every new developer tries to just use those.  But each one discovers quickly that it’s not that simple — you have to link in CSS from the admin site that will screw with your layout, and even after you get it working on your test site, it will not deploy correctly to production.  So let me begin by saying:

Do not use the Django admin’s DateTime widget.

I spent the weekend working out how to do a split DateTime widget properly, and I’m pretty happy with the result, so I’ll share it here.  It uses a text field with a jQuery UI calendar picker for the date, and a simple widget for the time.  Here’s what it looks like:

And here’s the code:

fields.py

from time import strptime, strftime
from django import forms
from django.db import models
from django.forms import fields
from conflux.widgets import JqSplitDateTimeWidget

class JqSplitDateTimeField(fields.MultiValueField):
    widget = JqSplitDateTimeWidget

    def __init__(self, *args, **kwargs):
        """
        Have to pass a list of field types to the constructor, else we
        won't get any data to our compress method.
        """
        all_fields = (
            fields.CharField(max_length=10),
            fields.CharField(max_length=2),
            fields.CharField(max_length=2),
            fields.ChoiceField(choices=[('AM','AM'),('PM','PM')])
            )
        super(JqSplitDateTimeField, self).__init__(all_fields, *args, **kwargs)

    def compress(self, data_list):
        """
        Takes the values from the MultiWidget and passes them as a
        list to this function. This function needs to compress the
        list into a single object to save.
        """
        if data_list:
            if not (data_list[0] and data_list[1] and data_list[2] and data_list[3]):
                raise forms.ValidationError("Field is missing data.")
            input_time = strptime("%s:%s %s"%(data_list[1], data_list[2], data_list[3]), "%I:%M %p")
            datetime_string = "%s %s" % (data_list[0], strftime('%H:%M', input_time))
            print "Datetime: %s"%datetime_string
            return datetime_string
        return None

widgets.py

from django import forms
from django.db import models
from django.template.loader import render_to_string
from django.forms.widgets import Select, MultiWidget, DateInput, TextInput
from time import strftime

class JqSplitDateTimeWidget(MultiWidget):

    def __init__(self, attrs=None, date_format=None, time_format=None):
        date_class = attrs['date_class']
        time_class = attrs['time_class']
        del attrs['date_class']
        del attrs['time_class']

        time_attrs = attrs.copy()
        time_attrs['class'] = time_class
        date_attrs = attrs.copy()
        date_attrs['class'] = date_class

        widgets = (DateInput(attrs=date_attrs, format=date_format),
                   TextInput(attrs=time_attrs), TextInput(attrs=time_attrs),
                   Select(attrs=attrs, choices=[('AM','AM'),('PM','PM')]))

        super(JqSplitDateTimeWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            d = strftime("%Y-%m-%d", value.timetuple())
            hour = strftime("%I", value.timetuple())
            minute = strftime("%M", value.timetuple())
            meridian = strftime("%p", value.timetuple())
            return (d, hour, minute, meridian)
        else:
            return (None, None, None, None)

    def format_output(self, rendered_widgets):
        """
        Given a list of rendered widgets (as strings), it inserts an HTML
        linebreak between them.

        Returns a Unicode string representing the HTML for the whole lot.
        """
        return "Date: %s<br/>Time: %s:%s %s" % (rendered_widgets[0], rendered_widgets[1],
                                                rendered_widgets[2], rendered_widgets[3])

    class Media:
        css = {
            }
        js = (
            "js/jqsplitdatetime.js",
            )

/media/js/jqsplitdatetime.js

$(function() {
   $(".datepicker").datepicker({ dateFormat: 'yy-mm-dd' });
});

To use the field in a form, put something like the following into your form definition:

some_date_field = JqSplitDateTimeField(widget=JqSplitDateTimeWidget(attrs={'date_class':'datepicker','time_class':'timepicker'})

Finally, you need to put the jQuery UI code somewhere. Go get a custom jQuery UI package (I used all of UI Core and Interactions, the Datepicker widget, and Effects Core — your may want more or less depending on where else you’re using JQuery and JQuery UI), put the necessary files (the jQuery UI css and js, the jQuery js) somewhere accessible to your form. I’m using jQuery UI throughout my site, so I’ve got them in my base.html:

    <link type="text/css" href="/media/css/ui-conflux/jquery-ui-1.8.custom.css" rel="Stylesheet" />
    <script src="/media/js/jquery-1.4.2.min.js" type="text/javascript"></script>
    <script src="/media/js/jquery-ui-1.8.custom.min.js" type="text/javascript"></script>

But if you only need jQuery UI for this form, it might make sense to put these in the media class of the JqSplitDateTimeWidget along with jqsplitdatetime.js:

class Media:
   css = {
      "css/ui-custom/jquery-ui-1.8.custom.css"
   }
   js = (
      "js/jqsplitdatetime.js",
      "js/jquery-1.4.2.min.js",
      "js/jquery-ui-1.8.custom.min.js",
   )

A couple of caveats: this widget is not currently very customizable/internationalizable. It only deals with 12-hour time and I should probably pass the date format in as an argument. But it does the trick for what I need, and what a lot of U.S. developers will need, and these things are easily added (I’d love a patch!).

  1. “If you like the widgets that the Django Admin application uses, feel free to use them in your own application! They’re all stored in django.contrib.admin.widgets.” – The Django Form Media documentation []

Clickwrap for Django and best practices for Terms of Service

Wednesday, April 14th, 2010

Last night, I wrote a simple Django application for managing clickwrap legal agreements. I’m developing the artists’ submission system for the Conflux Festival’s new site (which is not yet live), and we require artists to agree to an Artist Agreement before submitting entries for the festival. Since this sort of process is relatively simple and extremely common, I decided to write a reusable app rather than just work the logic into my submissions application. (I took a quick look around and as far as I can tell nobody else has put out an application for this purpose, but I may be wrong and if I am you should tell me in the comments.)

The goals for this application are/were roughly:

  • Give developers an easy way to check whether a user has agreed to a particular agreement when the user accesses a view. I decided the best way to do this was through a decorator, like so:

    @requires_agreement('terms_of_service')
    def any_old_view(request):
    ...

  • Keep versions of each agreement and record users’ agreement to each, so that the site maintainers have a log of which terms bound a user at which date, and the decorator above can determine whether the user has signed the *latest* version of the agreement.

I implemented both of the above features in version 0.1. What I want to do next is build in a feature I think is best practice for any site that maintains terms of service or a privacy policy:

  • When a user is asked to agree to an updated version of an agreement that user has already signed, display a diff of the old form and the current one so that the user can easily see what’s changed.

If you don’t do this, and your site gets big enough, EFF will do it for you. In my opinion, it’s a matter of fundamental respect for users — if you reserve the right in your ToS to make periodic changes to the agreement (and there are good reasons to do this), you should give your users clear notice of those changes.

Unfortunately, the most common practice is to change the terms silently and leave users to seek out a tiny link in the footer if they want to find the current terms. Another common, far better practice is to display the new terms upon the user’s first login to the site after the new terms become active. But few (edit: very few) users read the original terms, much less remember them, so even upon a careful read-through they may have no idea what’s changed. By giving users a diff of the old and new terms, a site makes itself accountable to and builds goodwill with its users. It may also strengthen the legal effect of its terms by giving users actual notice (although courts have been all too ready to enforce online agreements with little to no notice).

FLOSS Dispenser: a free market for Android

Wednesday, February 10th, 2010

I’ve been working on developing a free software application market for Android.1 The obvious place to start was the SlideME Community Edition code, which as far as I know is the only existing free software project that does even part of the job. Unfortunately, SlideME’s Community Edition was abandoned due to “lack of community interest” in April 2008, several months before the first Android phones were even available. So most of the work I’ve done so far was to update the code to work with the current SDK, rework the interface to behave in a more standard way, and rewrite portions that relied on now-unavailable API elements.

FLOSS Dispenser (like SlideME) works in conjunction with a J2EE server application called JVending. JVending’s public repository was also abandoned by SlideME some time ago, so I’m maintaining a fork of JVending along with my fork of FLOSS Dispenser as a part of the Replicant project.

I put up build instructions for FLOSS Dispenser as well as for JVending; using these, you should be able to build both and have them work together. However, neither one is ready, which is why I’m not hosting an application store already. The FLOSS Dispenser code in particular is pretty buggy (most of them aren’t mine, but only because I haven’t written much of it), for one thing. For another, the system doesn’t yet facilitate GPL compliance — you can download and install apk binaries, but they don’t come with source code and license text. Until at least this feature exists, I don’t recommend anyone serve GPL’d apks to the public using JVending. Update: The client application is now in a working alpha state. The really bad bugs have been hammered out and source can be delivered with the apk (the mechanism for this is nonoptimal, but functional). The challenge now is to enable users of the server to submit new apps easily, and to enable admins to moderate them.

I wanted to have this and some other issues hammered out before I put the code up, but I was motivated by Jonathan Corbet’s recent LWN article on Android to just put up what I had and try to get some help. I know other people are interested in something like this, and though I’m hardly proud of the little coding I’ve done on this, it’s the best start we have for a free market.

So by all means, take a look and help me kick this thing out the door.

  1. The quick rationale for this is that the Android Market 1) is not itself free software, and 2) doesn’t enable you to search for applications by their license. []

New IdentiFox that plays nice with TwitterFox

Thursday, February 26th, 2009

IdentiFox is a Firefox addon to get Identi.ca updates.  It’s based on TwitterFox, and up till now you couldn’t use the two together, because they still shared certain unique identifiers in their code.  I spent some time reworking IdentiFox to solve this problem and generally clean up the Twitter references in the code. I created a whole new IdentiFox based on the latest TwitterFox Beta.  It now supports all of the following features:

  • Tab for private messages
  • #hashtags and !groups link back to their homes on http://identi.ca
  • Right-click menu enables copying, redenting, and viewing notice on http://identi.ca
  • New color scheme to match Identi.ca’s current design

I’m working on getting the Addon onto Mozilla’s site, but till then I’ll post the XPI here.  Please comment here or email me with any problems you run into!

  • Here is the source on bitbucket

Why should I use Identi.ca instead of Twitter?

Thursday, February 26th, 2009

I’ve evangelized Identi.ca to a few friends who use Twitter and are only somewhat familiar with free software.  A lot of these friends sign onto Identi.ca and immediately post, “I don’t understand why I would use this instead of Twitter.”

The capsule answer is that Identi.ca is free software, meaning that anyone is free to download the code, share it, or change it to make it better.  As a result, Identi.ca is developed through individual contributions from its user community, rather than according to the desires or needs of a single company.  Some people (like me) believe this freedom and cooperative effort have inherent value even when they don’t work or produce measurably better results, which is the same thing I believe about democracy.  And when a program’s users are not beholden to a single company for new features and bug fixes, many people (like me) also believe better software will often result.

But to get specific about the Identi.ca v. Twitter issue, here are some advantages of Identi.ca over Twitter which are more or less the result of its community development model and ethic:

  • Identi.ca is not a monolithic service. This is the big one: Identi.ca’s underlying software, Laconica, can be installed on other websites (you could even install it on yours), and users on one site can subscribe to users on another.  Identi.ca calls this “federation” — the upshot is that if the people running Identi.ca get lazy and stop fixing bugs or creating new features, or if they adopt insane Terms of Use, you can take your data and set up camp at another site.  Federation also means less downtime; Identi.ca initially sprang into being while Twitter was experiencing frequent failures due to a combination of popularity and poorly scaling software design.  Because Laconica users can be distributed across multiple sites, those sites can share the load and decrease the likelihood of any one server being overwhelmed.
  • Groups. In addition to tagging posts with #tag, Identi.ca allows users to form groups and tag posts with !group.  That way, your feed will show any post directed to your group, even if you’re not subscribed to the user who posted it.

This is not to say that Identi.ca outshines Twitter in all areas — it’s younger, still building a user base, and there aren’t as many applications out there yet.  But Identi.ca is built like web applications should be, because it keeps the power in the hands of the users.  Among other things, this is insurance against the constant privacy invasions that plague Facebook and other social networking sites.  It’s something users should demand, and the best way to assert your rights is by using Identi.ca and other free/open services when possible.

Tilt-shift miniaturization in Gimp

Thursday, January 15th, 2009

In the last week, I’ve seen posts on BoingBoing and my friend Nick Whitaker’s blog about the Tilt-Shift Maker website, which will manipulate photos you upload to make them look like they were taken with a perspective-control lens.  Which got me wondering whether you can do the same thing in Gimp, GNU’s free image manipulation software.  Of course you can, and the basic technique is really easy.  This great post by the Flickr user shygantic in the Tilt-shift Miniature Fakes group explains it all.

My first tilt-shift miniature made in Gimp:

Tilt-shift photo of boats at Le Cabin Scott

The source photo was maybe not the ideal candidate because the field is too deep, but it’s a first try on the first photo I picked, which I think demonstrates how easy Gimp makes this.

Read the rest of this entry »

Android on Openmoko Freerunner (Ubuntu 8.04 Hardy Heron)

Monday, December 8th, 2008

Note: these instructions may be out of date.  Consider trying whatever instructions are posted on the Android-on-Freerunner page at openmoko.org.

I got Android working (as far as it goes) on my Freerunner using Sean McNeil’s kernel and filesystem images.1  These instructions are intended for the average Ubuntu users who wants to see the magic of Android on his/her phone with as little futzing about as possible.

Step 1: Partition your MicroSD card

You need to put a FAT (DOS) partition and an ext2 (or ext3) partition on your MicroSD card in order for Android to boot properly.  If it’s possible to do this by plugging your Freerunner into USB, I don’t know how.  So you will need some other way of reading your MicroSD card with your PC, for example a MicroSD to SD adapter (if your PC has an SD card reader built in) or a MicroSD to USB adapter.  Once you have one of these…

  1. Insert your MicroSD card into your card reader, and your card reader into your PC.
  2. If there is a filesystem on your MicroSD card already, it may auto-mount.  You must unmount the drive(s) on the card before proceeding.  You can do this either through Nautilus (right-click the drive and click “Unmount”) or from the command prompt:sudo umount /path/to/disk/2

    Partitions (old or new) may remount themselves after you add or remove them using fdisk (below). If so, you will have to unmount those as well.

  3. Run fdisk: sudo fdisk /dev/[device number]
  • Enter “p” to display existing partitions and their sizes.
  • For each existing partition, enter “d” and then the partition number to delete it.
  • Enter “p” again to make sure there are no partitions left on the card, and note the total number of cylinders on the card.
  • Create the first partition: enter “n” (new partition), then “p” (for a primary partition), then “1″ (for the first partition), then “1″ (start at the first cylinder), then enter half of the total number of cylinders (to make the first partition half the size of the card).
  • Create the second partition:3 enter “n” (new partition), then “p” (for a primary partition), then “2″ (for the second partition), then accept the defaults for the start and end cylinders.
  • Enter “p” to make sure that both of your partitions have been created.  If they have not, make sure you have unmounted all of the existing partitions and add any missing partition according to #7 and/or 8 above.
  • Enter “w” to write the partition table, then “q” to quit.
  1. Create filesystems on the new partitions:
  • sudo mkfs.vfat /dev/[first partition device number]
  • sudo mkfs.ext3 /dev/[second parition device number]
  1. If any of the new partitions are mounted, unmount them (see #2 above).
  2. Put the MicroSD card and battery back into your Freerunner and close it up.

Step 2: Download the firmware image files

Android requires three image files: the kernel, the root filesystem, and the bootloader.4  For now, the best kernel and rootfs images to use are those packaged by a developer named Sean McNeil.  The latest files can be found here.  I provide links to the existing images below, but the filenames may change.  The Qi bootloader image is packaged by a user named Andy and is available here (for Android, use the file that begins “qi-s3c2442-”).  Again, the names and locations of these files may change as development progresses, so if these links break, just seek out the most recent files.

  1. Download the kernel
  2. Download the root filesystem
  3. Download the Qi bootloader

Step 3: Download and set up the “neotool” flash utility and dfu-util

NeoTool is a handy, lightweight graphical tool for flashing images to your Freerunner’s memory.  It uses a command-line utility called dfu-util to do this, so you’ll need that too.

  1. Download the NeoTool script (this is just a text file, so you may want to right-click and “Save Link As…”)
  2. Download dfu-util
  3. Make both files executable—from the command line, navigate to the directory where you downloaded them and enter: chmod +x neotool dfu-util
  4. Run NeoTool—in the same directory, enter: sudo ./neotool
  5. In the NeoTool menu, select “Setup NeoTool”, then “Locate dfu-util”.  Find dfu-util on your drive and click “OK”.  Click “OK” again to return to the main NeoTool menu.

Step 4: Flash the images to your Freerunner with NeoTool

Now we will load the images into your Freerunner’s memory.

  1. Make sure your Freerunner has a decent charge in its battery, is turned off, and is disconnected from your PC.
  2. Boot the Freerunner into its NOR boot menu: while holding down the AUX (side) button on your Freerunner, press and hold the power button until a menu appears.5
  3. Attach the Freerunner to your PC via USB.
  4. From the NeoTool menu, select “Flash your Neo”.
  5. Select “Root-Filesystem”, “Kernel”, and “Bootloader” from the list.
  6. NeoTool will ask you to locate the image file for the Root FS first.  Locate the file (androidfs.jffs2) on your drive, select it, and click “OK”.
  7. Next, do the same for the kernel (uImage-android).
  8. Next, do the same for the Bootloader (NeoTool will ask you for UBOOT even though we are using Qi).
  9. Click “OK” through the scary message.
  10. Wait for NeoTool to finish flashing the firmware (you can follow the progress in the terminal from which you started NeoTool).
  11. When NeoTool has finished doing its thing, press the AUX button on your Freerunner until “Power off” is highlighted.  Press the power button to select it and power down your Freerunner.
  12. Unplug your Freerunner from USB power.

Step 5: Boot into Android

  1. Hold down the Freerunner’s power button until the screen’s backlight turns on (the screen will still be black) and then for a few seconds more.
  2. Release the power button and wait for Android to load.
  3. Android loads!  Hurrah!  This is way better than Om2008!  It found my network!  Calling works!
  4. Oh look, it’s going into suspend.  Wait, why does it look like nuclear winter?  Why is the screen white?  Why doesn’t the power button do anything?  I guess I’ll take out the battery.  (NB: suspend doesn’t work yet.)

I hope this helps someone.  Feel free to send/post feedback.

  1. Despite the Freerunner’s agonizing early history, there is still a very active community developing and testing the platform, and though none of the many distributions available for the platform are ready for primetime, that community has actually accomplished a lot.  Unfortunately, relatively little effort is dedicated to “bringing up the rear,” i.e. explaining clearly to less sophisticated users how to do all of the fun stuff the hardcore developers are doing.  Sean McNeil’s kernel and filesystem images go a long way toward making Android accessible to the average user, but his instructions for installing them do not work for me, and I couldn’t find any that did.  I pieced together these from 3 or 4 different sources, none of which contained the whole answer.  I hope they’ll be useful to someone. []
  2. When I mounted the MicroSD card using an SD adapter, it mounted as /dev/mmcblk0 and I unmounted the drives using: sudo umount /dev/mmcblk0p*. When I used a USB adapter, it mounted as /dev/sdb and I unmounted the drives using sudo umount /dev/sdb*. Yours may mount differently. Be careful that you have correctly identified the SD card and not some other more important drive on your computer. []
  3. Again, you may need to unmount the first partition before you can do this. []
  4. Supposedly you can use the default bootloader, u-boot, to load Android if you modify its environment to allow for larger kernels.  I tried this several times and was unsuccessful.  I have had no issues using Qi and it is reportedly faster. []
  5. This menu will only be displayed for 30 seconds, and then the phone will turn off again.  If this happens before you begin flashing the firmware, just repeat this step. []

Calm down, Matt Asay

Tuesday, October 7th, 2008

Matt Asay today springs to the defense of the widows and orphans over at Google and their comprehensive prosecution of the Google Chrome trademark. Google sent a DMCA notice to the developer of ChromePlugins.org, demanding he stop using their logos and a remix of their adorable comic, and the developer was understandably upset. Asay’s enthusiasm for Google’s cause is characteristic of his business-first approach to the Business of Open Source (though one wonders if he’s confused Google’s duty to police its trademarks for his own), but his high-handed tone is uncalled-for and his conclusions, though brief, are rife with oversimplification.

Asay responds to a rather balanced account of the enforcement with a volley of fake quotes attributed to god-knows-who: open source means “open to pilfer trademarks” and is “a stick-in-the-eye to ‘The (IP) Man’” and that all related rights are “up for grabs.” He bids the unidentified “some” people espousing these non-quotes, “pay attention… you’re wrong.”

The substance1 of Asay’s argument is a familiar truism: open source copyright licenses are instruments of copyright law. And as he says, “without copyright there is no copyleft.” But the implication that free and open source software depends upon copyright law (and this must be the implication or there is no point at all) is wrong (and doubly wrong because he italicizes it). Were copyright abolished and the default reset to “no rights reserved,” it would be a trivial matter indeed to allow others to modify my source code — I wouldn’t even need a license, because I would have no right to exclude.  It’s true that under a no-copyright regime, copyleft would break — I couldn’t use my right to exclude to compel others to make available source code they build on top of mine. But copyleft was devised in response to copyright, not in affirmation of it. In the GNU Manifesto which launched GNU and eventually the FSF, rms’s stated goal was to combat the increasing proprietarization of software. If software could not be made proprietary, copyleft may have been unnecessary. Of course, a company could still attain a sort of de facto proprietarization by controlling access to their source, but this would be much less effective under a system that didn’t punish reverse engineering or the use of decompiled or leaked source code.

Maybe abolition of copyright isn’t the answer, but copyright (much less in its present captive state2) is certainly not the only means to software freedom. And the mere existense of exclusive rights, even if similar rights are used to beneficial ends, is no justification for anti-social behavior, whether Google’s or Asay’s.

  1. And here more credible claimants to the title “substance,” such as plastic and cocaine, will forgive my lax usage. []
  2. My colleague Bradley Kuhn, who has said all of this before, has proposed a regime under which registrants of a copyright in software are required to deposit the entire source and in exchange are given 3 years of exclusive rights to it, after which their code becomes public domain. While not the only solution, or a universally agreeable one, it’s certainly imaginable. []

My other webl is a blog

Thursday, June 5th, 2008

I wrote a post for SFLC’s blog, responding to a Law.com article on FOSS licensing:

Law.com recently ran a sensationalist piece by Edmund J. Walsh warning of the impending “dangerous real world business dispute” in store for any for-profit company that uses free software. Walsh points to lawsuits filed by SFLC on behalf BusyBox as a source of this danger, and having worked on those lawsuits, I hope I can provide a helpful counterpoint.