Index: /trunk/Bookmark.pm
===================================================================
--- /trunk/Bookmark.pm	(revision 44)
+++ /trunk/Bookmark.pm	(revision 45)
@@ -37,4 +37,60 @@
 }
 
+sub update {
+    my $self = shift;
+    # TODO: store the dbh somewhere better, or use a more generic "Bookmark Store" object
+    my $dbh = shift;
+    my $mtime = time;
+
+    my $changed_uri = 0;
+    my $sth_current = $dbh->prepare('select uri from bookmarks where id = ?');
+    $sth_current->execute($self->id);
+    my ($stored_uri) = $sth_current->fetchrow_array;
+
+    if ($stored_uri ne $self->uri) {
+        # the URI has changed
+        my $sth_update_uri = $dbh->prepare('update resources set uri = ? where uri = ?');
+        $sth_update_uri->execute($self->uri, $stored_uri);
+        $changed_uri++;
+    }
+
+    # update the title
+    # TODO: only do this if the title has changed
+    # TODO: should we update mtime if the title changes?
+    my $sth_update = $dbh->prepare('update resources set title = ? where uri = ?');
+    $sth_update->execute($self->title, $self->uri);
+
+    # update the tags
+    my $changed_tags = 0;
+    my %new_tags = map { $_ => 1 } @{ $self->tags };
+    my $sth_delete_tag = $dbh->prepare('delete from tags where uri = ? and tag = ?');
+    my $sth_insert_tag = $dbh->prepare('insert into tags (uri, tag) values (?, ?)');
+    my $sth_current_tags = $dbh->prepare('select tag from tags where uri = ?');
+    $sth_current_tags->execute($self->uri);
+    while (my ($tag) = $sth_current_tags->fetchrow_array) {
+        if (!$new_tags{$tag}) {
+            # if a current tag is not in the new tags, remove it from the database
+            $sth_delete_tag->execute($self->uri, $tag);
+            $changed_tags++;
+        } else {
+            # if a new tag is already in the database, remove it from the list of tags to add
+            delete $new_tags{$tag};
+        }
+    }
+    for my $tag (keys %new_tags) {
+        $sth_insert_tag->execute($self->uri, $tag);
+        $changed_tags++;
+    }
+
+    if ($changed_uri or $changed_tags) {
+        # update the mtime if the bookmark already existed but the tags were changed
+        my $sth_update = $dbh->prepare('update bookmarks set mtime = ? where uri = ?');
+        $sth_update->execute($mtime, $self->uri);
+    }
+
+    # return the bookmark
+    return $self;
+}
+
 # module return
 1;
Index: /trunk/BookmarkApp.pm
===================================================================
--- /trunk/BookmarkApp.pm	(revision 44)
+++ /trunk/BookmarkApp.pm	(revision 45)
@@ -20,4 +20,5 @@
         view
         view_field
+        create
         edit
     }]);
@@ -361,10 +362,7 @@
 }
 
-#TODO: split this into edit and create methods
-sub edit {
+sub create {
     my $self = shift;
     my $q = $self->query;
-    #TODO: get the bookmark based on the id and edit it directly?
-    #TODO: deal with changing URIs
     my $uri = $q->param('uri');
     my $title = $q->param('title');
@@ -392,3 +390,32 @@
 }
 
+sub edit {
+    my $self = shift;
+    my $q = $self->query;
+    my $id = $self->param('id');
+
+    my $bookmark = $self->_bookmarks->get_bookmark({ id => $id });
+    if ($bookmark) {
+        # update the URI, title, and tags
+        $bookmark->uri($q->param('uri'));
+        $bookmark->title($q->param('title'));
+        $bookmark->tags([ split(' ', $q->param('tags')) ]);
+        $bookmark->update($self->_bookmarks->dbh);
+
+        # return to the form
+        $self->header_type('redirect');
+        $self->header_props(
+            -uri => $bookmark->bookmark_uri->canonical,
+            -status => 303,
+        );
+    } else {
+        $self->header_props(
+            -type    => 'text/html',
+            -charset => 'UTF-8',
+            -status  => 404,
+        );
+        return "Bookmark $id Not Found";
+    }
+}
+
 1;
Index: /trunk/BookmarkApp/Dispatch.pm
===================================================================
--- /trunk/BookmarkApp/Dispatch.pm	(revision 44)
+++ /trunk/BookmarkApp/Dispatch.pm	(revision 45)
@@ -13,5 +13,6 @@
             ':id[get]'        => { app => 'BookmarkApp', rm => 'view' },
             ':id/:field[get]' => { app => 'BookmarkApp', rm => 'view_field' },
-            ':id?[post]'      => { app => 'BookmarkApp', rm => 'edit' },
+            '[post]'          => { app => 'BookmarkApp', rm => 'create' },
+            ':id[post]'       => { app => 'BookmarkApp', rm => 'edit' },
         ],
     };
Index: /trunk/TODO
===================================================================
--- /trunk/TODO	(revision 44)
+++ /trunk/TODO	(revision 45)
@@ -1,4 +1,3 @@
     - import/export of bookmark data
-    - allow changing of URLs of resources
     - tool to scan database for broken URLs
     - machine tags
Index: /trunk/bookmark.tt
===================================================================
--- /trunk/bookmark.tt	(revision 44)
+++ /trunk/bookmark.tt	(revision 45)
@@ -36,6 +36,5 @@
             <th>URI:</th>
             <td>
-              <input type="text" name="uri" value="[% uri | html %]" size="80" 
-              [% IF exists %]readonly="readonly"[% END%]/>
+              <input type="text" name="uri" value="[% uri | html %]" size="80"/>
             </td>
           </tr>
