source: bookmarks/trunk/BookmarkController.pm @ 68

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