Annotation of todotxt/Text-Todo/bin/dudelicious.pl, Revision 1.16
1.8 andrew 1: #!/usr/bin/perl
1.1 andrew 2:
1.2 andrew 3: package Dudelicious;
4:
1.10 andrew 5: use 5.010;
1.6 andrew 6: use Data::Dumper;
1.4 andrew 7: use version; our $VERSION = qv('0.1.0');
8:
1.6 andrew 9: BEGIN {
10: use FindBin;
11: use lib "$FindBin::Bin/../lib";
1.8 andrew 12: use lib "$FindBin::Bin/../mojo/lib";
1.6 andrew 13: }
1.2 andrew 14:
1.5 andrew 15: use Carp qw/ carp croak /;
1.6 andrew 16: use Digest::MD5 qw/ md5_hex /;
17: use Text::Todo;
1.5 andrew 18:
1.1 andrew 19: use Mojolicious::Lite;
1.6 andrew 20: use Mojo::JSON;
1.5 andrew 21:
22: app->home->parse( $ENV{DUDELICIOUS_HOME} ) if $ENV{DUDELICIOUS_HOME};
1.1 andrew 23:
1.6 andrew 24: plugin 'json_config' => {
25: file => 'dudelicious.conf',
26: default => { todo_dir => $ENV{DUDELICIOUS_HOME} || '.', },
27: };
1.1 andrew 28:
1.10 andrew 29: app->renderer->add_helper(
30: todo => sub {
1.12 andrew 31: my ($self) = @_;
32: state $todo = Text::Todo->new( $self->stash('config') );
33:
34: my $file = $self->stash('file');
35: if ($file) {
36: $file =~ s/(?:\.txt)?$/\.txt/ixms;
37: $todo->load($file);
38: }
39:
1.10 andrew 40: return $todo;
41: }
42: );
43:
44: app->renderer->add_helper(
45: get_list => sub {
1.12 andrew 46: my ($self) = @_;
1.10 andrew 47:
48: my $line = 1;
49: return [
50: map {
1.11 andrew 51: line => $line++,
52: md5 => md5_hex( $_->text ),
53: text => $_->text,
54: done => $_->done,
1.10 andrew 55: },
56: $self->helper('todo')->list
57: ];
58: }
59: );
60:
1.5 andrew 61: get '/' => sub {
1.1 andrew 62: my $self = shift;
1.5 andrew 63:
1.10 andrew 64: my $dir = $self->helper('todo')->file('todo_dir');
1.5 andrew 65: opendir my $dh, $dir or croak "Unable to opendir $dir: $!";
66: my @files = grep {/\.te?xt$/ixms} readdir $dh;
67: closedir $dh;
68:
69: $self->render( files => \@files, layout => 'todotxt' );
70: } => 'index';
71:
1.9 andrew 72: get '/todotxt' => 'todotxt';
73:
1.5 andrew 74: get '/l/:file' => sub {
1.12 andrew 75: my $self = shift;
76:
1.6 andrew 77: my $format = $self->stash('format') || 'html';
1.5 andrew 78:
1.6 andrew 79: if ( $format eq 'json' ) {
1.12 andrew 80: $self->render_json( $self->helper('get_list') );
1.6 andrew 81: }
82: else {
1.12 andrew 83: $self->render(
84: list => $self->helper('get_list'),
1.16 ! andrew 85: tags => $self->helper('todo')->known_tags,
1.12 andrew 86: layout => 'todotxt'
87: );
1.6 andrew 88: }
1.5 andrew 89: } => 'list';
90:
1.6 andrew 91: get '/l/:file/e/:line' => sub {
1.12 andrew 92: my $self = shift;
93:
1.6 andrew 94: my $format = $self->stash('format') || 'html';
1.12 andrew 95: my $entry = $self->helper('get_list')->[ $self->stash('line') - 1 ];
1.6 andrew 96:
97: if ( $format eq 'json' ) {
1.7 andrew 98: $self->render_json($entry);
1.6 andrew 99: }
100: else {
1.7 andrew 101: $self->render( entry => $entry, layout => 'todotxt' );
1.6 andrew 102: }
1.5 andrew 103: } => 'entry';
1.1 andrew 104:
1.12 andrew 105: get '/l/:file/t' => sub {
106: my $self = shift;
107:
108: my $format = $self->stash('format') || 'html';
109:
110: if ( $format eq 'json' ) {
1.13 andrew 111: $self->render_json( $self->helper('todo')->known_tags );
1.12 andrew 112: }
113: else {
1.13 andrew 114: $self->render(
115: tags => $self->helper('todo')->known_tags,
116: layout => 'todotxt'
117: );
1.12 andrew 118: }
119: } => 'tags';
120:
1.13 andrew 121: get '/l/:file/t/:tag' => sub {
122: my $self = shift;
123:
124: my $format = $self->stash('format') || 'html';
125: my $items = $self->helper('todo')->listtag( $self->stash('tag') );
126:
127: if ( $format eq 'json' ) {
128: $self->render_json($items);
129: }
130: else {
131: $self->render( items => $items, layout => 'todotxt' );
132: }
133: } => 'tag';
134:
1.11 andrew 135: app->start if !caller();
1.5 andrew 136:
1.12 andrew 137: 1;
1.1 andrew 138: __DATA__
139:
1.5 andrew 140: @@ list.txt.ep
1.6 andrew 141: % foreach my $entry (@{ $list }) {
142: %== include 'entry', entry => $entry;
1.5 andrew 143: % }
144:
145: @@ entry.txt.ep
1.6 andrew 146: <%= $entry->{text} %>
1.5 andrew 147:
1.12 andrew 148: @@ tags.txt.ep
1.13 andrew 149: % foreach my $tag (keys %{ $tags }) {
1.15 andrew 150: <%= $tag %>: <%= $tags->{$tag} %>
1.12 andrew 151: % }
152:
1.13 andrew 153: @@ tag.txt.ep
1.15 andrew 154: # <%== $tag %>
1.13 andrew 155: % foreach my $item (@{ $items}) {
156: <%= $item %>
157: % }
158:
1.5 andrew 159: @@ layouts/todotxt.txt.ep
160: %= content
161:
1.1 andrew 162: @@ index.html.ep
1.5 andrew 163: % foreach my $file (@{ $files }) {
1.14 andrew 164: % my ($basename) = $file =~ /^(.*?)(?:\.[^\.]+)?$/xms;
1.16 ! andrew 165: <a href="<%= url_for 'list', format => '' %>/<%= $basename %>"><%= $file %></a><br />
1.5 andrew 166: % }
167:
168: @@ list.html.ep
169: <h1><%= $file %></h1>
1.16 ! andrew 170: % if ( $tags ) {
! 171: % foreach my $tag (keys%{ $tags }) {
! 172: %= include 'tag_menu', tag => $tag
! 173: <br />
! 174: % }
! 175: % }
1.5 andrew 176: <ol>
1.6 andrew 177: % foreach my $entry (@{ $list }) {
1.5 andrew 178: <li>
1.6 andrew 179: %= include 'entry', entry => $entry;
1.5 andrew 180: </li>
181: % }
182: </ol>
1.1 andrew 183:
1.5 andrew 184: @@ entry.html.ep
1.6 andrew 185: <%= $entry->{text} %>
1.5 andrew 186:
1.12 andrew 187: @@ tags.html.ep
188: % foreach my $tag (keys%{ $tags }) {
1.14 andrew 189: <a href="<%= url_for 'tag', format => '' %>/<%= $tag %>"><%= $tag %> == <%= $tags->{$tag} %></a><br />
1.12 andrew 190: % }
191:
1.13 andrew 192: @@ tag.html.ep
193: <h2><%= $tag %></h2>
1.16 ! andrew 194: % foreach my $item (sort @{ $items }) {
1.13 andrew 195: <%= $item %><br />
196: % }
1.12 andrew 197:
1.16 ! andrew 198: @@ tag_menu.html.ep
! 199: % my $items = todo()->listtag($tag);
! 200: <%= $tag %>:
! 201: <select>
! 202: % foreach my $item (sort @{ $items }) {
! 203: <option><%= $item %></option>
! 204: % }
! 205: </select>
! 206:
! 207:
1.5 andrew 208: @@ layouts/todotxt.html.ep
1.1 andrew 209: <!doctype html><html>
1.9 andrew 210: <head>
211: <title>Funky!</title>
212: <link rel="stylesheet" href="<%= url_for 'todotxt', format => 'css' %>">
213: </head>
1.1 andrew 214: <body><%== content %></body>
215: </html>
1.4 andrew 216:
1.9 andrew 217: @@ todotxt.css.ep
218: body {
219: background: LightGoldenRodYellow;
220: color: DarkSlateBlue;
221: }
222:
223: .inplaceeditor-saving {
224: background: url(images/saving.gif) bottom right no-repeat;
225: }
226:
227:
1.4 andrew 228: __END__
229:
230: =head1 NAME
231:
232: dudelicious - A Mojolicous interface to your todotxt files
233:
234: =head1 VERSION
235:
236: Since the $VERSION can't be automatically included,
237: here is the RCS Id instead, you'll have to look up $VERSION.
238:
1.16 ! andrew 239: $Id: dudelicious.pl,v 1.15 2010/05/05 01:43:03 andrew Exp $
1.4 andrew 240:
241: =head1 SYNOPSIS
242:
243: dudelicious daemon
244:
245: Then browse to http://localhost:3000/
246:
247: =head1 DESCRIPTION
248:
249: A Mojolicous web app for access to your todotxt files
250:
251: The modules are there to give more access to my todo.txt file from more
252: places. My goal is a web API for a web interface and then a WebOS version for
253: my Palm Pre.
254:
255: For more information see L<http://todotxt.com>
256:
257: =head1 USAGE
258:
259: See todo.pl -h
260:
261: =head1 OPTIONS
262:
263: See todo.pl -h
264:
265: =head1 REQUIRED ARGUMENTS
266:
267: See todo.pl -h
268:
269: =head1 CONFIGURATION AND ENVIRONMENT
270:
271: =head1 DIAGNOSTICS
272:
273: =head1 DEPENDENCIES
274:
275: Perl Modules:
276:
277: =over
278:
279: =item Text::Todo
280:
281: =item Mojolicous::Lite
282:
283: =item version
284:
285: =back
286:
287:
288: =head1 BUGS AND LIMITATIONS
289:
290: No bugs have been reported.
291:
292: =head1 AUTHOR
293:
294: Andrew Fresh C<< <andrew@cpan.org> >>
295:
296:
297: =head1 LICENSE AND COPYRIGHT
298:
299: Copyright (c) 2010, Andrew Fresh C<< <andrew@cpan.org> >>. All rights reserved.
300:
301: This module is free software; you can redistribute it and/or
302: modify it under the same terms as Perl itself. See L<perlartistic>.
303:
304:
305: =head1 DISCLAIMER OF WARRANTY
306:
307: BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
308: FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
309: OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
310: PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
311: EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
312: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
313: ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
314: YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
315: NECESSARY SERVICING, REPAIR, OR CORRECTION.
316:
317: IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
318: WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
319: REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
320: LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
321: OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
322: THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
323: RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
324: FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
325: SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
326: SUCH DAMAGES.
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>