source: bookmarks/trunk/BookmarksList.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.8 KB
Line 
1package BookmarksList;
2
3use Moose;
4
5use Encode;
6use HTTP::Date qw{time2iso time2isoz};
7
8has bookmarks => (is => 'ro');
9has query  => (is => 'ro');
10has tags   => (
11    is => 'ro',
12    default => sub { [] },
13);
14has limit  => (is => 'ro');
15has offset => (is => 'ro');
16has results => ( is => 'ro' );
17has title => (
18    is => 'ro',
19    builder => '_build_title',
20    lazy => 1,
21);
22
23sub _get_list_links {
24    my $self = shift;
25    my ($self_type, $query) = @_;
26    my @links = (
27        {
28            text => 'JSON',
29            type => 'application/json',
30            query => {
31                %$query,
32                format => 'json',
33            },
34        },
35        {
36            text => 'XBEL',
37            type => 'application/xml',
38            query => {
39                %$query,
40                format => 'xbel',
41            },
42        },
43        {
44            text => 'Atom',
45            type => 'application/atom+xml',
46            path => 'feed',
47            query => {
48                %$query,
49            },
50        },
51        {
52            text => 'CSV',
53            type => 'text/csv',
54            query => {
55                %$query,
56                format => 'csv',
57            },
58        },
59        {
60            text => 'URI List',
61            type => 'text/uri-list',
62            query => {
63                %$query,
64                format => 'text',
65            },
66        },
67        {
68            text => 'HTML',
69            type => 'text/html',
70            query => {
71                %$query,
72            },
73        },
74    );
75
76    for my $link (@links) {
77        $link->{rel}  = $link->{type} eq $self_type ? 'self' : 'alternate';
78        $link->{href} = URI->new_abs($link->{path} || '', $self->bookmarks->base_uri);
79        $link->{href}->query_form($link->{query});
80    }
81
82    return @links;
83}
84
85sub _build_title {
86    my $self = shift;
87    return 'Bookmarks' . (@{ $self->tags } ? " tagged as " . join(' & ', @{ $self->tags }) : '') . ($self->query ? " matching '" . $self->query . "'" : '');
88}
89
90sub as_json {
91    my $self = shift;
92    require JSON;
93    my $json = decode_utf8(
94        JSON->new->utf8->convert_blessed->encode({
95            bookmarks => $self->results,
96        })
97    );
98    return [200, ['Content-Type' => 'application/json; charset=UTF-8'], [$json]];
99}
100
101sub as_xbel {
102    my $self = shift;
103    require XML::XBEL;
104    #TODO: conditional support; if XML::XBEL is not present, return a 5xx response
105
106    my $xbel = XML::XBEL->new;
107
108    $xbel->new_document({
109        title => $self->title,
110    });
111
112    for my $bookmark (@{ $self->results }) {
113        my $cdatetime = time2isoz $bookmark->ctime;
114        my $mdatetime = time2isoz $bookmark->mtime;
115        # make the timestamps W3C-correct
116        s/ /T/ foreach ($cdatetime, $mdatetime);
117
118        $xbel->add_bookmark({
119            href     => $bookmark->uri,
120            title    => $bookmark->title,
121            desc     => 'Tags: ' . join(', ', @{ $bookmark->tags }),
122            added    => $cdatetime,
123            #XXX: are we sure that modified is the mtime of the bookmark or the resource?
124            modified => $mdatetime,
125        });
126    }
127
128    return [200, ['Content-Type' => 'application/xml; charset=UTF-8'], [$xbel->toString]];
129}
130
131sub as_text {
132    my $self = shift;
133    my $text = join '', 
134        map {
135            sprintf "# %s\n# Tags: %s\n%s\n",
136            $_->title,
137            join(', ', @{ $_->tags }), 
138            $_->uri
139        } @{ $self->results };
140    return [200, ['Content-Type' => 'text/uri-list; charset=UTF-8'], [$text]];
141}
142
143sub as_csv {
144    my $self = shift;
145    require Text::CSV::Encoded;
146    my $csv = Text::CSV::Encoded->new({ encoding_out => 'utf8' });
147    my $text = qq{id,uri,title,tags,ctime,mtime\n};
148    for my $bookmark (@{ $self->results }) {
149        my $success = $csv->combine(
150            $bookmark->id,
151            $bookmark->uri,
152            $bookmark->title,
153            join(' ', @{ $bookmark->tags }),
154            $bookmark->ctime,
155            $bookmark->mtime,
156        );
157        $text .= $csv->string . "\n" if $success;
158    }
159
160    # include the local timestamp in the attchment filename
161    my $dt = time2iso;
162    $dt =~ s/[^\d]//g;
163
164    my $filename = sprintf(
165        'bookmarks-%s-%s.csv',
166        join('_', @{ $self->tags }),
167        $dt,
168    );
169
170    return [200, ['Content-Type' => 'text/csv; charset=UTF-8', 'Content-Disposition' => sprintf('attachement; filename="%s"', $filename)], [$text]];
171}
172
173sub as_html {
174    my $self = shift;
175
176    require Template;
177    my $template = Template->new;
178
179    my @all_tags = $self->bookmarks->get_tags({ selected => @{ $self->tags }[0] });
180    my @cotags = $self->bookmarks->get_cotags({
181        query  => $self->query,
182        tag    => $self->tags,
183    });
184
185    $template->process(
186        'list.tt',
187        {
188            base_url     => $self->bookmarks->base_uri,
189            title        => $self->title,
190            query        => $self->query,
191            selected_tag => @{ $self->tags }[0],
192            search_tags  => $self->tags,
193            links        => [ $self->_get_list_links('text/html', { q => $self->query, tag => $self->tags }) ],
194            all_tags     => \@all_tags,
195            cotags       => \@cotags,
196            resources    => $self->results,
197        },
198        \my $output,
199    );
200    return [200, ['Content-Type' => 'text/html; charset=UTF-8'], [$output]];
201}
202
203sub as_atom {
204    my $self = shift;
205
206    require XML::Atom;
207    $XML::Atom::DefaultVersion = "1.0";
208
209    require XML::Atom::Feed;
210    require XML::Atom::Entry;
211    require XML::Atom::Link;
212    require XML::Atom::Category;
213
214    my $feed = XML::Atom::Feed->new;
215    $feed->title($self->title);
216
217    for my $link ($self->_get_list_links('application/atom+xml', { q => $self->query, tag => $self->tags })) {
218        my $atom_link = XML::Atom::Link->new;
219        $atom_link->type($link->{type});
220        $atom_link->rel($link->{rel});
221        $atom_link->href($link->{href}->canonical);
222        $feed->add_link($atom_link);
223    }
224
225    for my $bookmark (@{ $self->results }) {
226        my $entry = XML::Atom::Entry->new;
227        $entry->id($bookmark->bookmark_uri->canonical);
228        $entry->title($bookmark->title);
229       
230        my $link = XML::Atom::Link->new;
231        $link->href($bookmark->uri);
232        $entry->add_link($link);
233       
234        $entry->summary('Tags: ' . join(', ', @{ $bookmark->tags }));
235
236        my $cdatetime = time2isoz $bookmark->ctime;
237        my $mdatetime = time2isoz $bookmark->mtime;
238        # make the timestamp W3C-correct
239        s/ /T/ foreach ($cdatetime, $mdatetime);
240        $entry->published($cdatetime);
241        $entry->updated($mdatetime);
242       
243        for my $tag (@{ $bookmark->tags }) {
244            my $category = XML::Atom::Category->new;
245            $category->term($tag);
246            $entry->add_category($category);
247        }
248
249        $feed->add_entry($entry);
250    }
251
252    return [200, ['Content-Type' => 'application/atom+xml; charset=UTF-8'], [$feed->as_xml]];
253}
254
2551;
Note: See TracBrowser for help on using the repository browser.