App-Todo
view release on metacpan or search on metacpan
bin/todo.pl view on Meta::CPAN
GetOptions(\%args,
"tags=s",
"tag=s@", "group=s",
"priority|pri=s",
"due=s",
"hide=s",
"owner=s",
"help",
"version",
"config=s",)
or pod2usage(2);
$CONFFILE = $args{config} if $args{config};
pod2usage(0) if $args{help};
if ($args{version}) {
version();
exit();
}
setup_config();
# If they don't want color, switch it off
if ( defined $config{color} and $config{color} eq 'off' ) {
*color = *_color_passthru;
*colored = *_colored_passthru;
}
push @{$args{tag}}, split /\s+/, $args{tags} if $args{tags};
if($args{priority}) {
$args{priority} = priority_from_string($args{priority})
unless $args{priority} =~ /^[1-5]$/;
die("Invalid priority: $args{priority}")
unless$args{priority} =~ /^[1-5]$/;
}
$args{owner} ||= "me";
do_login() or die("Bad username/password -- edit $CONFFILE and try again.");
%commands = (
list => \&list_tasks,
ls => \&list_tasks,
add => \&add_task,
do => \&do_task,
done => \&do_task,
del => \&del_task,
rm => \&del_task,
edit => \&edit_task,
tag => \&tag_task,
unaccepted => sub {list_tasks($unaccepted_query)},
accept => \&accept_task,
decline => \&decline_task,
assign => \&assign_task,
requests => sub {list_tasks($requests_query)},
hide => \&hide_task,
comment => \&comment_task,
dl => \&download_textfile,
download => \&download_textfile,
ul => \&upload_textfile,
upload => \&upload_textfile,
bd => \&braindump,
braindump => \&braindump,
editdump => \&editdump,
feedback => \&feedback,
);
$command = shift @ARGV || "list";
$commands{$command} or pod2usage(-message => "Unknown command: $command", -exitval => 2);
$commands{$command}->();
}
=begin comment
=head1 CONFIG FILE
These methods deal with loading the config file, and populating it
with selections read from the terminal on our first run.
=cut
sub setup_config {
check_config_perms() unless($^O eq 'MSWin32');
load_config();
check_config();
}
sub check_config_perms {
return unless -e $CONFFILE;
my @stat = stat($CONFFILE);
my $mode = $stat[2];
if($mode & S_IRGRP || $mode & S_IROTH) {
warn("Config file $CONFFILE is readable by someone other than you, fixing.");
chmod 0600, $CONFFILE;
}
}
sub load_config {
return unless(-e $CONFFILE);
%config = %{LoadFile($CONFFILE) || {}};
my $sid = $config{sid};
if($sid) {
my $uri = URI->new($config{site});
$ua->cookie_jar->set_cookie(0, 'JIFTY_SID_HIVEMINDER',
$sid, '/', $uri->host, $uri->port,
0, 0, undef, 1);
}
if($config{site}) {
# Somehow, localhost gets normalized to localhost.localdomain,
# and messes up HTTP::Cookies when we try to set cookies on
# localhost, since it doesn't send them to
# localhost.localdomain.
$config{site} =~ s/localhost/127.0.0.1/;
}
}
sub check_config {
new_config() unless $config{email};
}
bin/todo.pl view on Meta::CPAN
pod2usage(-message => "You need to specify a texteditor as \$EDITOR or \$VISUAL.",
-exitval => 1
) unless $editor;
my $fh = File::Temp->new( UNLINK => 0 );
my $fn = $fh->filename;
$fh->close;
# Call the editor with the file as the first arg
system( "$editor $fn" );
# Slurp in the content
open (my $file, "<:utf8", $fn) || die("Can't open file '$fn': $!");
my $content;
{
local $/ = undef;
$content = <$file>;
}
close($file);
my $result = call(UpdateTask =>
id => $task,
comment => $content);
result_ok($result,
"Commented on task",
"Your comment is saved in the temporary file $fn.");
unlink $fn;
}
sub get_task_id {
my $action = shift;
my $task = shift @ARGV or pod2usage(-message => "Need a task-id to $action.");
return $locator->decode($task) or die("Invalid task ID");
}
sub download_textfile {
my $query = shift || $default_query;
my $filename = shift || shift @ARGV || 'tasks.txt';
my $tag;
$query .= "/tag/$tag" while $tag = shift @{$args{tag}};
for my $key (qw(group priority due)) {
$query .= "/$key/$args{$key}" if $args{$key};
}
$query .= "/owner/$args{owner}";
my $result = call(DownloadTasks =>
query => $query,
format => 'sync');
# perl automatically does TRT with $filename eq '-'
open (my $file, ">:utf8", $filename) || die("Can't open file '$filename': $!");
print $file $result->{_content}{result};
}
sub upload_textfile {
my $filename = shift || shift @ARGV;
pod2usage(-message => "Need to specify a file to upload.",
-exitval => 1
) unless $filename;
open (my $file, "< $filename");
local $/;
my $content = <$file>;
my $result = call(UploadTasks =>
content => $content,
format => 'sync' );
result_ok( $result, $result->{message},
"Your tasks are saved in the temporary file $filename." );
}
sub braindump {
my $fill_file = shift || sub {};
my $editor = $ENV{EDITOR} || $ENV{VISUAL};
pod2usage(-message => "You need to specify a texteditor as \$EDITOR or \$VISUAL.",
-exitval => 1
) unless $editor;
my $fh = File::Temp->new( UNLINK => 0 );
my $fn = $fh->filename;
$fh->close;
$fill_file->( $fn );
# Call the editor with the file as the first arg
system( "$editor $fn" );
upload_textfile( $fn );
unlink $fn;
}
sub editdump {
my $query = shift || $default_query;
braindump( sub { download_textfile( $query, shift ) } )
}
sub feedback {
my $editor = $ENV{EDITOR} || $ENV{VISUAL};
pod2usage(-message => "You need to specify a texteditor as \$EDITOR or \$VISUAL.",
-exitval => 1
) unless $editor;
my $fh = File::Temp->new( UNLINK => 0 );
my $fn = $fh->filename;
$fh->close;
# Call the editor with the file as the first arg
system( "$editor $fn" );
# Slurp in the content
open (my $file, "<:utf8", $fn) || die("Can't open file '$fn': $!");
my $content;
{
local $/ = undef;
$content = <$file>;
}
close($file);
# Send it in
my $result = call(SendFeedback =>
content => $content);
result_ok($result,
"Sent feedback. Thank you!",
"Your feedback is saved in the temporary file $fn.");
unlink $fn;
}
=head1 Hiveminder API
These functions deal with calling the Hiveminder/Jifty api to communicate
with the server.
=cut
sub do_login {
return 1 if $config{sid};
my $result = call(GeneratePasswordToken =>
address => $config{email});
if ($result->{failure}) {
die $result->{message};
}
my $salt = $result->{_content}{salt};
my $token = $result->{_content}{token};
my $hashed_password = md5_hex($token . ' ' . md5_hex($config{password} . $salt));
$result = call(Login =>
address => $config{email},
bin/todo.pl view on Meta::CPAN
my ($year, $month, $day) = split '-', shift, 3;
my @now = localtime;
if ( $year < $now[5]+1900 ) { return 1 } # Past year
elsif ( $year > $now[5]+1900 ) { return 0 } # Future year
elsif ( $month < $now[4]+1 ) { return 1 } # Equal year, past month
elsif ( $month > $now[4]+1 ) { return 0 } # Equal year, future month
elsif ( $day <= $now[3] ) { return 1 } # Equal year-month, past day or today
else { return 0 } # Equal year-month, future day
}
=head2 supports_color
Tests if the terminal supports color and returns true if so, false otherwise.
If there is no controlling TTY, then color will be disabled.
=end comment
=cut
sub supports_color {
# We're not on a TTY, kill color
return 0 if not -t *STDOUT;
if ( $Config{'osname'} eq 'MSWin32' ) {
eval { require Win32::Console::ANSI; };
return 1 if not $@;
}
else {
return 1 if $ENV{'TERM'} =~ /^(xterm|rxvt|linux|ansi|screen)/;
return 1 if $ENV{'COLORTERM'};
}
return 0;
}
__END__
=head1 SYNOPSIS
todo.pl [options] list [query]
todo.pl [options] add <summary>
todo.pl [options] edit <task-id> [summary]
todo.pl tag <task-id> tag1 tag2
todo.pl done <task-id>
todo.pl del|rm <task-id>
todo.pl [options] unaccepted
todo.pl accept <task-id>
todo.pl decline <task-id>
todo.pl assign <task-id> <email>
todo.pl [options] requests
todo.pl hide <task-id> date
todo.pl comment <task-id>
todo.pl [options] download [file]
todo.pl upload <file>
todo.pl braindump
todo.pl [options] editdump
todo.pl feedback
Options:
--group Operate on tasks in a group
--tag Operate on tasks with a given tag
--pri Operate on tasks with a given priority
--due Operate on tasks due on a given day
--hide Operate on tasks hidden until this day
--owner Operate on tasks with a given owner
todo.pl list
List all tasks in your todo list.
todo.pl list due before today not complete
List tasks that are overdue.
todo.pl list important
Lists tasks specified by the named search 'important'.
For more on named searches, see the CONFIG FILE section of the perldoc
todo.pl --tag home --tag othertag --group personal list
List personal tasks not in a group with tags 'home' and 'othertag'.
todo.pl --tag cli --group hiveminders edit 3G Implement todo.pl
Move task 3G into the hiveminders group, set its tags to
"cli", and change the summary.
todo.pl --tag "" edit 4J
Delete all tags from task 4J
todo.pl tag 4J home
Add the tag 'home' to task 4J
todo.pl braindump
Open up $EDITOR to braindump tasks
todo.pl --tag sometag editdump
Download and edit tasks with tag 'sometag'.
Updates tasks after $EDITOR completes.
todo.pl feedback
Open up $EDITOR to send feedback
=head1 CONFIG FILE
The config file (in C<$ENV{HOME}/.hiveminder>) is YAML and contains the
properties like C<email> and C<password>. If you ever need to reconfigure
todo.pl, you can edit these values or just delete the file and todo.pl will
reconfigure itself automatically.
To turn off colored output all the time, add the following to your config:
color: off
Named searches can be added to the config with a snippet like the following:
( run in 2.013 seconds using v1.01-cache-2.11-cpan-b16cb0d3907 )