source: bookmarks/trunk/lib/Bookmarks/Controller.pm @ 98

Last change on this file since 98 was 98, checked in by peter, 9 years ago

only initialize the controller once, and just set the request to a new Plack::Request each time the application is called

File size: 6.6 KB
Line 
1package Bookmarks::Controller;
2
3use Moose;
4
5use Encode;
6use HTTP::Date qw{time2str str2time};
7use JSON;
8use Bookmarks;
9use Bookmarks::List;
10use URI;
11use Template;
12
13has dbname => (
14    is => 'ro',
15    required => 1,
16);
17has bookmarks => (
18    is => 'ro',
19    handles => [qw{get_bookmark}],
20    builder => '_build_bookmarks',
21    lazy => 1,
22);
23has base_uri => (
24    is => 'ro',
25    builder => '_build_base_uri',
26    lazy => 1,
27);
28has request => (
29    is => 'rw',
30);
31
32sub _build_bookmarks {
33    my $self = shift;
34    return Bookmarks->new({
35        dbname   => $self->dbname,
36        base_uri => $self->base_uri,
37    });
38}
39
40sub _build_base_uri {
41    my $self = shift;
42    my $url = $self->request->base;
43
44    $url .= '/' unless $url =~ m{/$};
45    return URI->new($url);
46}
47
48sub find_or_new {
49    my $self = shift;
50
51    my $bookmark = $self->bookmarks->get_bookmark({ uri => $self->request->param('uri') });
52    if ($bookmark) {
53        # redirect to the view of the existing bookmark
54        return [301, [Location => $bookmark->bookmark_uri], []];
55    } else {
56        # bookmark was not found; show the form to create a new bookmark
57        my $template = Template->new;
58        $template->process(
59            'bookmark.tt',
60            { 
61                bookmark => {
62                    uri   => $self->request->param('uri'),
63                    title => $self->request->param('title') || '',
64                },
65            },
66            \my $output,
67        );
68        return [404, ['Content-Type' => 'text/html; charset=UTF-8'], [$output]];
69    }
70}
71
72sub list {
73    my $self = shift;
74
75    # list all the bookmarks
76    my $mtime = $self->bookmarks->get_last_modified_time;
77
78    my $format = $self->request->param('format') || 'html';
79
80    my @tags = grep { $_ ne '' } $self->request->param('tag');
81    my $query = $self->request->param('q');
82    my $limit = $self->request->param('limit');
83    my $offset = $self->request->param('offset');
84
85    my $list = Bookmarks::List->new({
86        bookmarks => $self->bookmarks,
87        search    => $self->bookmarks->search({
88            query  => $query,
89            tags   => \@tags,
90            limit  => $limit,
91            offset => $offset,
92        }),
93    });
94
95    my $as_format = "as_$format";
96    if (!$list->meta->has_method($as_format)) {
97        return [406, ['Content-Type' => 'text/plain; charset=UTF-8'], [qq{"$format" is not a supported format}]];
98    }
99    return $list->$as_format;
100}
101
102sub feed {
103    my $self = shift;
104
105    my $query = $self->request->param('q');
106    my @tags = grep { $_ ne '' } $self->request->param('tag');
107
108    # construct a feed from the most recent 12 bookmarks
109    my $list = Bookmarks::List->new({
110        bookmarks => $self->bookmarks,
111        search    => $self->bookmarks->search({ query => $query, tags => \@tags, limit => 12 }),
112    });
113    return $list->as_atom;
114}
115
116# returns 1 if there is an If-Modified-Since header and it is newer than the given $mtime
117# returns 0 if there is an If-Modified-Since header but the $mtime is newer
118# returns undef if there is no If-Modified-Since header
119sub _request_is_newer_than {
120    my $self = shift;
121    my $mtime = shift;
122
123    # check If-Modified-Since header to return cache response
124    if ($self->request->env->{HTTP_IF_MODIFIED_SINCE}) {
125        my $cache_time = str2time($self->request->env->{HTTP_IF_MODIFIED_SINCE});
126        return $mtime <= $cache_time ? 1 : 0;
127    } else {
128        return;
129    }
130}
131
132sub view {
133    my ($self, $id) = @_;
134
135    my $format = $self->request->param('format') || 'html';
136
137    my $bookmark = $self->get_bookmark({ id => $id });
138    if ($bookmark) {
139        return [304, [], []] if $self->_request_is_newer_than($bookmark->mtime);
140
141        my $last_modified = time2str($bookmark->mtime);
142       
143        if ($format eq 'json') {
144            my $json = decode_utf8(JSON->new->utf8->convert_blessed->encode($bookmark->to_hashref));
145            return [200, ['Content-Type' => 'application/json; charset=UTF-8', 'Last-Modified' => $last_modified], [$json]];
146        } else {
147            # display the bookmark form for this bookmark
148            require File::Basename;
149            my $template = Template->new({ INCLUDE_PATH => File::Basename::dirname($INC{'Bookmarks/Controller.pm'}) });
150            $template->process(
151                'bookmark.tt',
152                { bookmark => $bookmark },
153                \my $output,
154            );
155            return [200, ['Content-Type' => 'text/html; charset=UTF-8', 'Last-Modified' => $last_modified], [$output]];
156        }
157    } else {
158        return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], ["Bookmark $id not found"]];
159    }
160}
161
162sub view_field {
163    my ($self, $id, $field) = @_;
164
165    my $bookmark = $self->get_bookmark({ id => $id });
166    if ($bookmark) {
167        return [304, [], []] if $self->_request_is_newer_than($bookmark->mtime);
168
169        # check whether the requested field is part of the bookmark
170        if (!$bookmark->meta->has_attribute($field)) {
171            return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], [qq{"$field" is not a valid bookmark data field}]];
172        }
173
174        # respond with just the requested field as plain text
175        my $value = $bookmark->$field;
176        my $last_modified = time2str($bookmark->mtime);
177        return [200, ['Content-Type' => 'text/plain; charset=UTF-8', 'Last-Modified' => $last_modified], [ref $value eq 'ARRAY' ? join(' ', @{ $value }) : $value]];
178    } else {
179        return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], ["Bookmark $id not found"]];
180    }
181}
182
183sub create_and_redirect {
184    my $self = shift;
185
186    my $uri   = $self->request->param('uri');
187    my $title = $self->request->param('title');
188    my @tags  = split ' ', $self->request->param('tags');
189
190    my $bookmark = $self->bookmarks->add({
191        uri   => $uri,
192        title => $title,
193        tags  => \@tags,
194    });
195
196    #TODO: not RESTful; the proper RESTful response would be a 201
197    return [303, ['Location' => $bookmark->bookmark_uri->canonical], []];
198}
199
200sub update_and_redirect {
201    my $self = shift;
202    my $id = shift;
203
204    my $bookmark = $self->bookmarks->get_bookmark({ id => $id });
205    if ($bookmark) {
206        # update the URI, title, and tags
207        $bookmark->uri($self->request->param('uri'));
208        $bookmark->title($self->request->param('title'));
209        $bookmark->tags([ split ' ', $self->request->param('tags') || '' ]);
210
211        # write to the database
212        $bookmark->update;
213
214        #TODO: not RESTful; proper response would be a 200
215        return [303, ['Location' => $bookmark->bookmark_uri->canonical], []];
216    } else {
217        return [404, ['Content-Type' => 'text/plain; charset=UTF-8'], ["Bookmark $id not found"]];
218    }
219}
220
2211;
Note: See TracBrowser for help on using the repository browser.