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

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