THE CURRENT STRATEGY The URL strategy we've been basically using is to put the nouns in the path info and the verbs in the query string. The heirarchy of the path info mirrors the heirarchy of objects in the system. It works like this in the Problem.pm content generator (I think): /webwork/$courseID/$setID/$problemID?submitAnswer=Submit+Answer WeBWorK contains courses, courses contain sets, sets contain problems. This is reflected in the path info. The presence of a parameter named submitAnswer in the query string causes Problem.pm to record the answers given later in the query string. The set and problem to which the answers are recorded is given in the path info. Each content generator has a default action that is performed if no verbs are given in the query string. These default actions are non-destructive, i.e. "view" or "list". ADVANTAGES This scheme has the advantage of allowing a user (usually a professor) to easily reference a "location" within the system, without needing to worry about the relativly complicated syntax of the query string. For example, a professor could, after making a change to a problem, instruct students via email to "visit http://courses.webwork.rochester.edu/mth161/4/7/ to view the updated problem". This is certainly more manageable than a scheme in which the verb is in the path info and the nouns are in the query string (as in "http://courses.webwork.edu/viewProblem?course=mth161&set=4&problem=7"). It also allows us to, as we strive to reduce the amount of state that is passed through each request, pare the URL down so that very little query string remains. For example, the session key could be kept in a cookie, and the rest of the session data could be kept in the key table of the database. The only thing that would be left in the query string would be the verb and (I suppose) various "adverbs" and "adjectives". PROBLEMS Currently the dispatch() function uses only the path info to decide which content generator to invoke. This means that unless we wish to have the problem viewer and the problem editor be implemented in the same content generator, it would be impossible to have these two URIs: /webwork/$courseID/$setID/$problemID/?action=view /webwork/$courseID/$setID/$problemID/?action=edit This, I think, can be solved by allowing the dispatch() function to inspect the query string in a limited fashion. If it were allowed to check for an "action" parameter, and a standard set of actions were defined, the combination of an action (in the query string) and a type of object (in the path info) could be used to select a content generator. A table would be needed to noun/action pairs to content generators and vice versa. For example: Problem + view <=> WeBWorK::ContentGenerator::Problem Problem + edit <=> WeBWorK::ContentGenerator::Instructor::PGProblemEditor However, there are some tricky implementation problems with this approach. The value of a parameter triggered by a submit button (generated with the INPUT element) is the same as the text that is displayed on the button. This puts the desire for good UI at odds with having easily manageable action identifiers. We'd like to have a button named "Download Hardcopy for Selected Sets", but have the action identifier be "makeHardcopy" or something. The BUTTON element promises to solve this, but browser support is very broken for both the presentation and behavior of this element. Another problem with this scheme is that sometimes the nouns needed are not known when the form action is decided at page generation time. For example, the URI for creating a new set might be something like: /webwork/$courseID/instructor/sets/$setID?action=create The set name ($setID) would have be supplied by a text field on the form, and therefore would not be known at the time that the form was generated. One solution to this would be to name the "slots" in the path info, and have the dispatcher substitute in values from the query string before handing the request off to the content generator. A redirect to the URI with the values substituted in could fix the user experience somewhat. For example, the dispatcher could convert the URI: /webwork/mth161/instructor/sets/?action=create&setID=newIntegrals to: /webwork/mth161/instructor/sets/newIntegrals/?action=create In the example, the value of the parameter named "setID" was placed in the "setID" slot in the path info, and the parameter was deleted. -------------------------------------------------------------------------------- (+) = list THE CURRENT URI HEIRARCHY $webworkURIRoot $courseID test $setID $problemID hardcopy $setID login logout options feedback instructor [some crap] PROPOSED URI HEIRARCHY $webworkURIRoot $courseID -- lists sets test -- prints debugging information $setID -- lists problems in a set $problemID -- displays problem (interactive mode) hardcopy -- prompts user for which sets/users to generate $setID (+) -- generates hardcopy for specified sets login -- prompts for login information logout -- deletes session information, displays logout message options -- allows user to change email address and password feedback -- allows user to send feedback to professor instructor -- gives user a choice between user list and set list sets -- lists/edits (global) sets $setID -- displays/edits (global) set data problems -- lists (global) problems for a set $problemID -- displays/edits (global) set data users -- lists/edits assigned users $userID (+) -- displays/edits user-specific set data problems -- lists/edits assigned problems $problemID -- displays/edits user-specific problem data users -- lists/edits users $userID -- displays/edits user data sets -- lists/edits assigned sets $setID -- displays/edits user-specific set data problems -- lists/edits assigned problems $problemID -- displays/edits user-specific problem data