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 )