Quick thoughts about the form handling stuff I’ve been working on.  Bear in mind that this is just stuff I’m thinking about, and that if I choose to go ahead with any of this either now or in the future I’ll stay out of everyone’s way.  The basic form handling system is pretty much settled down now and any more work I do to it will be pretty much behind the scenes, generally won’t break existing code, and will be fixed by me in the few cases where it does.

Since my last post I went ahead and implemented the formlite+octopus mashup, OctopoLite (will someone please name this for me before it gets stuck as that? — I hate naming things), and the javascript code for parsing the response object I described there.  Rob Miller’s been a great help here and I’m much happier with the results after he took a look at it and made a few modifications.  This whole time that I’ve been working on the project contents page (which has been much longer than I wanted or expected) I’ve been trying to do it in a way that will be useful for TaskTracker and hopefully for future products as well.  So I want to try to keep OctopoLite as framework-neutral as possible so I can easily use it for TaskTracker as well as for opencore.nui.  But it can’t really be framework-neutral: how to do redirects and the structure of a request object aren’t going to be consistent, for one thing. We also started to put in a mechanism for dealing with status messages, which I don’t think belongs in OctopoLite; I guess it’s okay to assume there’s such a thing as a status message, and Ra’s additions to the code do a nice job of deferring the implementation of the status message to its subclass (or, technically, to its subclass’s other superclass) but still it’s one of those things that just seems wrong to me.

I think the solution is to just subclass OctopoLite. The base class would be framework-neutral, and the subclass wouldn’t be framework-neutral at all; it would be tied to opencore.nui and can extend both OctopoLite and nui’s BaseView.  The base OctopoLite class would become more like an interface.  So I could give OctopoLite unimplemented methods for getting the request and for doing a redirect, and a postprocessing hook for stuff that should happen after the return from the dispatcher like this newly-added finding and formatting of the portal status messages to be returned on an AJAX call. 

Meanwhile, although I’m very happy with the response object Nick and I came up with, it’s a little irritating to have to worry about it in the view code in most cases.  It’s wonderfully flexible (at any rate it can do everything we’ve needed from it so far and more) but to force all of these methods to specify effects and actions, which ought to be pretty standard anyway across OpenPlans and which don’t actually have anything to do with the method call itself, feels off.  SO I’m thinking about having some methods in OctopoLite which will just construct those things for you for a small set of standard actions (like: deletes, updates, messages).  I could either make them plain old methods on OctopoLite that the view classes can call if they so choose

class MyView(NUIOctopoLite):
  @action('delete')
  def delete(self, targets, fields=None):
    ret = {}
    for oid in targets:
      self.objects[oid]._delObj()
      ret[oid] = self.oct_delete(oid)
    return ret

where NUIOctopoLite.oct_delete just looks like

def oct_delete(self, id):
   return {'action': 'delete', 'effect': 'fadeout'}

which solves most of the problems: it encourages a standard set of commands back to the client and it allows the programmer to not worry about the response format.  On the other hand, it’s kind of pointless, isn’t it, and anyway my (more compelling to me) gut semantic issues with it remain; I guess the response constructing functions could be decorator-like instead (and only called when they’re called through the OctopoLite dispatcher) but then the wrapped methods still need to conform to some sort of standard if they’re going to wrap themselves so it’s not gaining much.  I think I just have to accept that, as the methods that communicate with both the underlying server code and the client, I should stop trying to keep them entirely on one side of that divide; as it stands I think it’s pretty good and clean.

The other thing I’m not happy about which I’ll just note now is that all methods that go through OctopoLite now require a certain signature (self, targets, fields).  Since plenty of methods don’t need these at all (fields are just sort of extra data for objects that a lot of actions won’t need, and in some context like joining the site or editing a user’s profile the “target” concept has no meaning either) it’s annoying to have them imposed by OctopoLite.  I thought about setting them on the class instance instead of passing them to the method but that’s very weird so I absolutely refuse.  Is there any other alternative besides inspecting the function signature, which feels excessive?

Next week I’ll clean up the Javascript and implement some of what I talked about here, and fix up the join form (now octopolized) and project contents page (not yet octopolized) for good, and then I’ll probably be done with this for a while.

Filed June 29th, 2007 under Uncategorized

For the last two weeks my goal was to try to work through as much of the WordPress integration work as possible.

Unfortunately I didn’t really get anywhere with that.  I spent about a day starting to create the skeleton of a WordPress featurelet, which mostly involved trying to refactor out a common base for all featurelets that talk to an external application: TaskTracker, WordPress, whatever else we may add on.  I didn’t get nearly as far with that as I wanted; it’s good enough for WordPress, but I’d like to revisit it before we have a third case for it.

Then, though, I got pulled back into NUI, and spent far too long doing far too little.  I got things done and learned a huge amount (about Zope testing layers, about Zope event subscribers, about portal_catalog) but, when I stepped back and thought about what I had done, I realized almost all of it was minute enough that I should have given up after half a day.  On the bright side, Mouna assures me that the hardest and most trivial thing I accomplished, the mailing list thread count I added to portal_catalog, will be useful for the Listen NUI screens, so that’ll be nice if it’s true.

Between adding AJAX resorting by column to the project contents page, adding AJAX field validation for the sitewide join form, and trying to explain the formhandling system Nick and I had worked out, I also spent quite a lot of time this iteration looking at Octopus and FormLite, the two “major” (very relatively speaking) form handlers we have in opencore.nui.

The former I mentioned briefly in my last blog post; developed for the project contents page, but since used (and modified to be helpful for) a TaskTracker widget and the team management page, it basically just takes a request structured in a particular way, passes it on to a method on your view, and returns either the result of that operation (for an asynchronous request) or a redirect to HTTP_REFERER (for a synchronous request).

The latter is a lightweight little delegation system Whit wrote; have your View class extend opencore.nui.formhandlers.FormLite, decorate a couple of methods with @action(’unique_label’), and FormLite’s handle_request method will call the action decorated with whatever label it first finds in the request.

David tried to use Octopus during the iteration, and was annoyed to see that he had to format his request a particular way which meant he couldn’t just trigger the Ploneish object.validate() using the untouched request fields.  So I wrote a @deoctopize function which takes an octopus-structured request and constructs a Ploneish request from it for each of the objects that is referred to in the request.

Meanwhile for the join form, once I added validation as well as creation submissions, I decided to use FormLite so I could have separate createMember() and validate() methods which were easily testable individually.

While I was working on that join form I added a couple of things to FormLite to make it more useful for me.

First, I was annoyed that, to use FormLite, I had to bind separate views for the join form’s rendering (bound to a template) and its delegated formhandling (bound to an attribute on the view class).  This actually took me quite a while to figure out, since I didn’t really understand how Zope views work, but the short story was that if I called a method on a view which was bound to a template, my AJAX friendly response would be clobbered by a rerendering of that template.

So my solution was to add a concept of a default action to FormLite — that is, if you decorate a single method with @action(’unique_label’, default=True), that method will be called as a fallback if no other method labels are found in the request.  With that done, I added to my JoinView class a default action that simply renders the form template, bound my sole view to the handle_request attribute (from FormLite, so it delegates) on the view class, and (to avoid infinite recursion) deleted the TAL call to view.handle_request() at the top of the page template.  Though there are still a few hitches, I like this convention quite a lot; rather than having a form rendering method which explicitly calls a particular “work-doing” method on the view, it feels a lot more intuitive and natural to explicitly define delegatable actions to an implicit delegator method which is called every time a request is made to the form’s sole URL.

Next, I was annoyed by some tests.  I wanted my validate() method to return a list of error messages, but I had to format them a particular way for the Javascript which was not ideal for running a test.  I knew that FormLite left its delegatees untouched so that they were still directly callable, so I decided to add in a way to decorate a method *only* if it’s called by the FormLite delegator and *not* if it’s called directly — that way it would return the Javascript-friendly structure if it was ever triggered from a ‘real’ request, and it would return the Python doctest-friendly structure if it was just triggered directly in a test.  So I added another option to the @action decorator, ‘apply’, which takes either one or a tuple of references to decorators to be applied to the delegatee.  So for the user this might look like

@action('my_action', apply=(jsonify, post_only))
def my_action(self): return an_obj


for an action which should always return an_obj when it’s called directly in tests, but, when delegated to, will treat it as a call to

@post_only
@jsonify
def my_action(self): return an_obj


(As a side bonus, this syntax avoids having to worry about the FormLite quirk whereby a function’s outermost decorator must have a __name__ identical to the innermost function.)

This, incidentally — applying decorators to methods at runtime — was harder than I expected, because the innermost method was bound to the view class and the decorated methods were not, so the decorated methods had to be called as method(view) but the innermost decorator, internally, had to call bound_method().  I got around it by, instead of passing bound_method to the decorators, passing bound_method.im_func, which is an *unbound* reference to the function.

By this point, I realized, I was very close to having pushed FormLite and Octopus close enough to each other that it was time to merge them — FormLite could delegate to any action, Octopus could always delegate to a single location but had enough information to figure out what action to use, and both FormLite and Octopus could inspect or modify the internal method’s response to the client (through apply in the former case, checking for mode==async and either returning the response or a redirect in the latter case).

So here’s a first stab at combining Octopus and FormLite into OctoLite.

A view with a form is structured like the join form.  That is, there is a single view for the form, bound to the view class’s handle_request attribute; it is not bound to a template.  The view class has a render() method which calls self.form_template() where form_template is a ZopeTwoPageTemplateFile class attribute which contains the form ZPT.  This could be the view class’s responsibility to define, or it could be defined in an OctoLite class which the view class extends and leave as the view class’s sole responsibility defining self.form_template.

The view class also contains one or more methods decorated with @action: form_submission and validation, for example; or delete_objects, update_objects, and validate_objects; etc.

OctoLite.handle_request combines the handle_request method of FormLite with the pre-call form processing in the octopus decorator.  First it calls Octopus.handle_request, which processes values from the form to determine what action has been taken, on what objects, and with what parameters.  It then triggers the FormLite delegation method on that action (first, perhaps, deoctopizing the request form, though there are some issues here).  The delegated method returns, and Octopus kicks in again, either returning that value for an asynchronous request, or doing a redirect to some URL for a synchronous request.  Instead of HTTP_REFERER, though, this redirect URL should be specified by the delegatee, since 1) HTTP_REFERER won’t always be there and 2) some delegatees might want to do unpredictable things, like redirect to a different page.

So, that’s the first pass through.  It’s very straightforward, though I think it’s important that the pieces each continue to work on their own, at least for backwards compatibility until we move over all the forms.  (I have a hunch that it’s worth keeping them discrete for other reasons, too, though.)

Some questions to address in later passes:

1) Does the redirect URL come back in the return value of the delegatee, or is it stored in an attribute on the delegatee method itself?  I prefer the latter — it feels more explicit and less obtrusive to the programmer — but this might be difficult in cases where a single method needs to redirect to multiple places conditionally e.g. on success or failure — this concern is probably a deal-breaker for the attribute approach.

2) What happens to the concept of ‘apply’?  After all the post-call thing that Octopus does is essentially a case identical to something like an @action(apply=conditionally_redirect).  Is there something redundant here?  Is the apply thing unuseful?

3) Even further separation of underlying test-friendly behavior and particular AJAX-friendly data structures.  As we flesh out the Javascript half of this story, the data expected back from the server gets pushed into an increasingly convoluted structure which provides a decreasing ratio of information directly related to the core behavior versus information useful only for AJAX rendering purposes.  It may be that the core delegatee should be responsible only for a very small amount of data expressed as naturally as possible, while the AJAX-friendly representation is defined only once and the data converted in a single decorator `apply’ed to all delegatees.

In this last I mentioned fleshing out the Javascript half of the story.  Nick and I started thinking about this today, in response to a few new use cases discovered by us and Rob Miller.  Currently, the system is very dumb: if you make a request with an action called “delete”, the server is expected to send back a JSON array of DOM elements to delete; if you make a request with an action called “update”, the server is expected to send back a JSON dictionary keyed on DOM elements to delete with values being HTML snippets to insert in place of the keys; if you make a request with any other action, the client doesn’t render any effects on success.  Rob wanted to be able to define his own action names, though — which will also be necessary for any remotely powerful FormLite form; and Nick and I wanted to be able to specify which HTML snippets on a “update” callback should blink/highlight when they appear and which shouldn’t; and both Rob and I wanted to send back text messages to insert into particular DOM nodes.  So I think I will specify that any server response is expected to have something like the following format, in JSON:

Return a dictionary whose KEYS are ids of existing DOM elements on the page, and whose VALUES are a JSON object (with all fields optional):

class ActionInfo {
  string html;   # a string which can be evaluated as HTML and rendered into DOM elements (quoted HTML or plain text)
  string effect; # a comma-separated string of JavaScript effects to apply to the element referred to in the id key, like fadeOut, highlight, or blink (we need to define a fixed set of these or something)
  string action; # a string indicating what action to take on the DOM element specified in the id key. This will probably end up being some large subset of the commands in the Deliverance specification, actually: certainly 'replace', 'copy', 'append', 'drop', and maybe 'prepend' too..
}


So a sample response might look like

{'el1': {'html': "Changes saved!", 'action': "copy", 'effect': "blink"}, 'el2': {'action': "delete"} }

I’m hoping to implement this tomorrow; you can probably see, though, why I’m concerned to move this response-generating code out into, if possible, a single unobtrusive function.

Filed June 28th, 2007 under Uncategorized

Just wanted to add that the various revelations detailed in my previous blog post have a couple of implications for the TaskTracker refactoring I’m so looking forward to.  I’m going to refer to specific points in the code, here, so my apologies if this means nothing at all to you — that means you, entire-world-sans-David-and-possibly-Ian.  Don’t worry, I don’t think any of this happens in the first couple of refactoring passes: I think those will be, first, for cleaning up the XHTML, CSS, and Javascript; and second, for deleting unused or unuseful functions and moving things out of tasktracker.lib.helpers into more appropriate places like tasktracker.lib.base and tasktracker.templates.

But, anyway, I think I understand what I (may have) meant in my diagram when I made a distinction between the TaskTracker ‘core’ and ‘outer layer’; the outer layer is in fact the TaskTracker-facing opencore integration layer, which currently consists of the poorly named CookieAuth middleware in tasktracker.lib.

There’s more than just authentication in there though; there’s already environment translation, user data, project security information. So

one thing to do is to rename the damn thing. Another is, in the longer term, to move the all OpenPlans-specific logic — opencore-imposed security restrictions, external security roles, projects, and so on — out of the core altogether and into this integration layer.

At this point I suspect it stops being just middleware; actually I think it already should have: as it is we’re horribly abusing the middleware concept with most of what’s currently in tasktracker.lib.cookieauth, like UserMapper and project_info. Authentication remains in middleware, obviously, but only as one piece of an integration package, which I imagine might ought not live in TaskTracker at all; call it opencore.tasktracker or tasktracker.opencore or something and let it live elsewhere.

Meanwhile it will be worth figuring out what parts of this opencore.tasktracker package are implementing necessary behavior (like the authentication middleware) and what’s just extending TaskTracker or imposing restrictions on it (like projects, if they end up being able to move out to here altogether; or like the one-to-many tasklist:task relationship, if the decision-makers at OpenPlans really want to keep this).  Then we know exactly what we also need to put into the TestingEnv and/or a standalone production environment in TaskTracker itself, and then it will actually be reasonable to say that the TaskTracker software can actually be used by anyone outside of the opencore world.

Filed June 10th, 2007 under TaskTracker

I haven’t written one of these in … um, in a while. So here’s what I’ve been doing and thinking about:

We deployed TaskTracker. I’m very happy with how this went; we ended up deploying about a week later than we intended to, but that extra week let us test and fix a lot of important bugs and inconsistencies. In the weeks (is it a month already?) since TaskTracker was deployed I’ve been fixing little bugs that I knew about but didn’t have a chance to look at before deployment and bugs that users have reported, including at least one fairly critical one (no unicode support). There are quite a few bugs remaining, but of the ones I’m aware of, none are critical, and most are user-facing bugs which will be easier to solve later, when we tear down and redo the XHTML, CSS and Javascript in an intelligent and consistent way, rather than trying to search through and patch up the existing code.

I’m very happy with the deployment process, but that doesn’t mean I’m very happy with the product; I have a few major complaints which I want to work on addressing as I have time. I created a diagram illustrating some of my complaints:

The two big things to note on this diagram are the relative size and position of the Tasks bubble and the external clients trying to reach TaskTracker: their number, their success, and the routes they take.

TaskTracker currently feels to me like it’s giving, at best, equal emphasis to tasklists and tasks, at worst greater emphasis on tasklists: tasks belong exclusively to single tasklists; tasks store information about their siblings in a tasklist; tasks are viewed and created within the context of a tasklist. This feels (and has always felt) completely wrong to me.

Tasks, after all, are what TaskTracker tracks. Tasks are what you want to get done. Tasklists are just a potentially convenient way of grouping tasks together. But they’re only one way of grouping tasks. Groupings of existing tasks by owner, by creator, by status — by any arbitrary field or set of fields — can be just as useful, as can overlapping tasklists. Just about every tracking product I know of features this; it’s a good idea, even if (and I’m not sure this is true) it might make it harder to provide a clean and simple UI.  In addition, the fact that tasks can have an unlimited number of generations of subtasks makes the task/tasklist distinction curiously arbitrary. When a task has fifteen subtasks, in what sense is it not a tasklist? When you just want to throw up a task or two that you need to get done during the day, why do you need an enclosing tasklist; why can’t the tasks just stand on their own?

The only tricky difference between a task and a tasklist is security — and even that isn’t entirely clear-cut since tasks can be private and transfer their privacy to their descendants — and I’m sure we can come up with a coherent way to do security without firm tasklists. Every other piece of information that lives in a tasklist more properly belongs either in tasks (custom statuses) or in any arbitrary view (displayed fields — which sort of includes custom statuses too, since we do have a distinction between “status” and “resolution” even if the distinction is less obvious, and makes more sense, than in trac).

My other gripe is, fundamentally, that we designed TaskTracker explicitly for OpenPlans.org instead of as a standalone web service which is used on the OpenPlans website, a recent realization which I’ll discuss further at the end of this post .  This means that OpenPlans-specific concepts and logic have bled all over TaskTracker where they don’t belong and where they artificially restrict TaskTracker’s functionality. 

For example, note that in my diagram TaskTracker currently only exposes its information in one way, through its human-readable AJAX web interface; if any other client tries to get information out of, or issue commands to, TaskTracker, it just can’t do it.  This isn’t a major problem: it’s just a matter of finding use cases, adding interfaces, and refactoring, but I’ll be much happier with TaskTracker when we address it. 

As another example, TaskTracker is very project-centric: tasklists belong to projects, and a lot of critical and decentralized security rules and other logic care about projects.  This is crucial for integration with OpenPlans.org, since you don’t want to be able to go to http://www.openplans.org/projects/bob-project/tasks/joe-tasklist if joe-tasklist isn’t affiliated with bob-project.  But it’s entirely non-crucial for TaskTracker, since projects mean absolutely nothing to TaskTracker, and it will almost certainly cause headaches when we add views for “all my tasks” or when we add “calls to action.”

Anyway, after deploying TaskTracker, I started working on the new OpenPlans UI, AKA nui. First Jeff and I were working on account screens: login, join, and resetting password. My first serious work in Zope was a lot less painful than I had feared; I think this is largely because we were writing views, which, broadly, make sense to me after using Pylons for eight months to write my first-ever webapp, instead of using skins, which I vaguely understand how to manipulate but which, even so, make no sense to me at all. Jeff and I got most of the basic stuff working by the end of the iteration, though we left a lot of minor pieces, testing, and cleanup undone, and we didn’t get around to our lowest-priority task, the project contents screen.

For the iteration after that, Jeff was working on the aforementioned minor pieces and cleanup and I was working on the project contents screen. Jeff’s piece in particular turned out to be a lot harder than we expected, as he’s described in his recent blog post. Mine, meanwhile, wasn’t too bad, although I spent almost half the iteration just digging around trying to figure out how to fetch, delete and rename objects, which are needed for the project contents screen.

Once I got this information, though, everything went remarkably smoothly, thanks to the multi-part form handling system that Nick and I worked out while I was digging around the code for a week. The contents page allows you to perform several operations on either a single object or an arbitrary set of objects, and it should work both asynchronously and synchronously without Javascript. We both wanted to make our method for solving this problem as general as we could: the contents page might add more actions or item types in the future, and a good solution to this problem could probably be imported into TaskTracker to make its AJAX live-edit multipart forms much more elegant. The system we developed contains three parts: a fairly specific markup/form convention for the form, a little bit of Javascript, and a server-side function, octopus_form_handler, which decorates a form handling function to parse the request according to the convention and return an appropriate response for both AJAX and standard requests.

So, this worked really well, and we were able to make a project contents page containing all wiki pages, file attachments and images, and mailing lists with very little hassle. The mockup also called for a list of the project’s tasklists, though. There were two ways to do this: by exposing a widget in TaskTracker to be transcluded into the contents page; or by querying TaskTracker for tasklist data, building a widget in opencore, and sending user requests to opencore which then makes internal requests to TaskTracker.

Initially I thought I should take the second approach, mostly as an excuse to give TaskTracker the RESTful interface I’ve been wanting to give it. After considerable discussion, though, I decided to take the first approach, for two reasons: one, for the second approach to work TaskTracker would have to send, and opencore would have to understand, data about whether the current user has permission to update and delete each tasklist, which sounded ugly and silly; and two, this widget would be useful outside of the opencore.nui project contents page — in TaskTracker itself, for example, or as an embeddable widget on unaffiliated websites.

On Friday, then, I ported our multi-form system to TaskTracker — a very simple process of writing Myghty templates out of the ZPTs, including the exact same javascript file, and changing a few lines in octopus_form_handler to reflect Pylons’ request and redirect syntax — and in about two hours (more than half of which was spent debugging my Myghty) I had a tasklist-managing widget that was structured and worked exactly the same way as the ones I had in opencore.nui for wiki pages, attachments, and mailing lists. I’m really quite thrilled with this success, because it suggests that the system Nick and I developed really *is* generally useful; the inconsistent request and redirect syntax between Zope and Pylons is annoying, but even so I’m wondering if it’s worth extracting all the relevant Javascript and Python code and putting them in their own package somewhere outside of opencore and TaskTracker instead of duplicating the code.  On the other hand, it’s only about thirty lines.

Anyway, now, on Monday, I just have to figure out how to transclude that widget (where, exactly, does Transcluder go in our stack, and in what form?) and the project contents page will be complete, except for some minor issues: elegant error handling, for example; possibly pagination; and only exposing the action buttons to users who are permitted to take those actions.

While thinking about my dissatisfaction with TaskTracker and about transluding TaskTracker widgets into nui, I changed my mind completely about the opencore naming issue. That is, is “opencore” the Stuff We Do In Zope, or is “opencore” the Entire Codebase That Runs OpenPlans.org? When the question was first raised, I thought it should be the latter, since the name implies centrality and we’re trying to move away from a Zope-centric architecture.

What hadn’t really sunk in at the time was that the products we develop (Listen, Wicked, TaskTracker, etc) are all really intended to stand alone. I suspect I missed this major point when developing TaskTracker (which, as I said, I think is a root cause of many of my complaints about that product) in large part because I had an outsider’s perspective, working as I was on a Zope-external product whose use case was to be integrated with the OpenPlans website; on the other hand, the fact that all of our Zope-internal products happen to run on the same Zope instance also made it much harder for me to realize this.

But the point is that — at least according to my recently-adopted personal nomenclature, if not the official one, which I’m still unclear of — “opencore” is neither the Stuff We Do In Zope *or* the Entire Codebase That Runs OpenPlans.org: it’s the software that _integrates_ all of our various standalone products and presents them, unified and consistent, on the OpenPlans website. This makes sense: opencore is the core of OpenPlans.org and any other website that uses it; it’s what makes OpenPlans.org exist and be usable; but it’s *not* most of the things that OpenPlans.org provides: those are independent products and services, which may or may not be initiatives of The Open Planning Project, but which are *not* initiatives of OpenPlans. I get this, and it makes a huge amount of sense to me, so I hope it’s right.

So can I get my separate tasktracker trac instance now?

Filed June 10th, 2007 under TaskTracker, Architecture, OpenCore, OpenPlans