source: bookmarks/trunk/BookmarksList.pm @ 69

Last change on this file since 69 was 69, checked in by peter, 11 years ago

remove extraneous blank ?q= parameters when generating links

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