source: bookmarks/trunk/BookmarkApp.pm @ 15

Last change on this file since 15 was 15, checked in by peter, 11 years ago
  • Bookmarks uses Moose instead of Class::Accessor
  • if not dbh is specified in the Bookmarks constructor, it can use a dbname parameter to create a SQLite DBH
  • the SQLite DBH created form dbname has the foreign_keys pragma set
  • added foreign key constraints to the bookmarks.sql table definitions for bookmarks and tags
  • added a required --file option to bkmk to specify the database file to use
  • added a load command to bkmk that loads bookmarks dumped as YAML using bkmk list
  • Bookmarks::add() can take mtime and id parameters (useful for reconstructing a database from the YAML dump of bkmk list)
  • BookmarkApp and bkmk no longer use DBI directly; just pass a dbname to the Bookmarks constructor
  • changed the default database for BookmarkApp to fk.db (schema from this revision's updated bookmarks.sql, with foreign keys)
File size: 6.1 KB
Line 
1package BookmarkApp;
2use strict;
3use warnings;
4use base qw{CGI::Application};
5
6use CGI::Application::Plugin::TT;
7
8use Encode;
9use JSON;
10use Bookmarks;
11
12use URI;
13my $base_uri = URI->new;
14$base_uri->scheme('http');
15$base_uri->host($ENV{SERVER_NAME});
16$base_uri->port($ENV{SERVER_PORT});
17$base_uri->path($ENV{SCRIPT_NAME} . '/');
18
19my $dbname = 'fk.db';
20my $bookmarks = Bookmarks->new({
21    dbname   => $dbname,
22    base_uri => $base_uri->canonical,
23});
24
25sub setup {
26    my $self = shift;
27    $self->mode_param(path_info => 1);
28    $self->run_modes([qw{
29        list
30        feed
31        view
32        edit
33    }]);
34}
35
36sub list {
37    my $self = shift;
38    my $q = $self->query;
39
40    # check for a uri param, and if there is one present,
41    # see if a bookmark for that URI already exists
42    if (defined(my $uri = $q->param('uri'))) {
43        my $bookmark = $bookmarks->get_bookmark({ uri => $uri });
44        if ($bookmark) {
45            # redirect to the view of the existing bookmark
46            $self->header_type('redirect');
47            $self->header_props(
48                -uri => $q->url . '/' . $bookmark->{id},
49            );
50            return;
51        } else {
52            # bookmark was not found; show the form to create a new bookmark
53            $bookmark->{uri} = $uri;
54            $bookmark->{title} = $q->param('title');
55            $self->header_props(
56                -type    => 'text/html',
57                -charset => 'UTF-8',
58                -status  => 404,
59            );
60            return $self->tt_process(
61                'bookmark.tt',
62                $bookmark,
63            );
64        }
65    }
66
67    # list all the bookmarks
68    my $format = $q->param('format') || 'html';
69    my $tag = $q->param('tag');
70    my $limit = $q->param('limit');
71    my $offset = $q->param('offset');
72    my @resources = $bookmarks->get_resources({
73        tag    => $tag,
74        limit  => $limit,
75        offset => $offset,
76    });
77    my @all_tags = $bookmarks->get_tags({ selected => $tag });
78    my @cotags = $bookmarks->get_cotags({ tag => $tag });
79
80    if ($format eq 'json') {
81        $self->header_props(
82            -type    => 'application/json',
83            -charset => 'UTF-8',
84        );
85        return decode_utf8(
86            encode_json({
87                bookmarks => \@resources,
88            })
89        );
90    } else {
91        $self->header_props(
92            -type    => 'text/html',
93            -charset => 'UTF-8',
94        );
95
96        # set the base URL, adding a trailing slash if needed
97        my $base_url = $q->url;
98        $base_url .= '/' if $base_url =~ m{/bookmarks$};
99
100        return $self->tt_process(
101            'list.tt',
102            {
103                base_url     => $base_url,
104                selected_tag => $tag,
105                tags         => \@all_tags,
106                cotags       => \@cotags,
107                resources    => \@resources,
108            },
109        );
110    }
111}
112
113sub feed {
114    my $self = shift;
115    my $q = $self->query;
116
117    my $tag = $q->param('tag');
118
119    require XML::Atom::Feed;
120    require XML::Atom::Entry;
121    require XML::Atom::Link;
122
123    my $feed = XML::Atom::Feed->new;
124    $feed->title('Bookmarks');
125    $feed->id($base_uri . 'feed');
126
127    # construct a feed from the most recent 12 bookmarks
128    for my $bookmark ($bookmarks->get_resources({ tag => $tag, limit => 12 })) {
129        my $entry = XML::Atom::Entry->new;
130        $entry->id($bookmark->{bookmark_uri});
131        $entry->title($bookmark->{title});
132        my $link = XML::Atom::Link->new;
133        $link->href($bookmark->{uri});
134        $entry->add_link($link);
135        $entry->summary('Tags: ' . join(', ', @{ $bookmark->{tags} }));
136        $feed->add_entry($entry);
137    }
138
139    $self->header_props(
140        -type => 'application/atom+xml',
141        -charset => 'UTF-8',
142    );
143    return $feed->as_xml;
144}
145
146sub view {
147    my $self = shift;
148    my $id = $self->param('id');
149    my $field = $self->param('field');
150    my $format = $self->query->param('format') || 'html';
151
152    my $bookmark = $bookmarks->get_bookmark({ id => $id });
153    if ($bookmark) {
154        if ($field) {
155            # respond with just the requested field as plain text
156            $self->header_props(
157                -type    => 'text/plain',
158                -charset => 'UTF-8',
159            );
160            my $value = $bookmark->{$field};
161            return ref $value eq 'ARRAY' ? join(',', @{ $value }) : $value;
162        } else {
163            if ($format eq 'json') {
164                $self->header_props(
165                    -type    => 'application/json',
166                    -charset => 'UTF-8',
167                );
168                return decode_utf8(encode_json($bookmark));
169            } else {
170                # display the bookmark form for this bookmark
171                $bookmark->{exists} = 1;
172                $bookmark->{created} = "Created " . localtime($bookmark->{ctime});
173                $bookmark->{created} .= '; Updated ' . localtime($bookmark->{mtime}) unless $bookmark->{ctime} == $bookmark->{mtime};
174                $self->header_props(
175                    -type    => 'text/html',
176                    -charset => 'UTF-8',
177                );
178                return $self->tt_process(
179                    'bookmark.tt',
180                    $bookmark,
181                );
182            }
183        }
184    } else {
185        $self->header_props(
186            -type    => 'text/html',
187            -charset => 'UTF-8',
188            -status  => 404,
189        );
190        return "Bookmark $id Not Found";
191    }
192}
193
194#TODO: split this into edit and create methods
195sub edit {
196    my $self = shift;
197    my $q = $self->query;
198    #TODO: get the bookmark based on the id and edit it directly?
199    #TODO: deal with changing URIs
200    my $uri = $q->param('uri');
201    my $title = $q->param('title');
202    my @tags = split ' ', $q->param('tags');
203    $bookmarks->add({
204        uri   => $uri,
205        title => $title,
206        tags  => \@tags,
207    });
208
209=begin
210
211    my $location = URI->new($q->url);
212    $location->query_form(uri => $uri) if defined $q->url_param('uri');
213    $location->fragment('updated');
214
215=cut
216
217    # return to the form
218    $self->header_type('redirect');
219    $self->header_props(
220        -uri => $ENV{REQUEST_URI},
221        -status => 303,
222    );
223}
224
2251;
Note: See TracBrowser for help on using the repository browser.