summaryrefslogtreecommitdiff
path: root/lib/CIL/Utils.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/CIL/Utils.pm')
-rw-r--r--lib/CIL/Utils.pm388
1 files changed, 386 insertions, 2 deletions
diff --git a/lib/CIL/Utils.pm b/lib/CIL/Utils.pm
index 47f7ada..cad802f 100644
--- a/lib/CIL/Utils.pm
+++ b/lib/CIL/Utils.pm
@@ -29,11 +29,13 @@ use File::Temp qw(tempfile);
use Email::Find;
use POSIX qw(getpgrp tcgetpgrp);
use Fcntl qw(:DEFAULT :flock);
+use Digest::MD5 qw(md5_hex);
## ----------------------------------------------------------------------------
# setup some globals
my $editor = $ENV{EDITOR} || 'vi';
+my $y = 'y';
## ----------------------------------------------------------------------------
@@ -76,7 +78,7 @@ sub parse_from_lines {
$data->{$key} = $value;
}
}
-
+
# now read everything that's left into the $last_field field (if there is one)
$data->{$last_field} = join("\n", @lines)
if defined $last_field;
@@ -124,6 +126,9 @@ sub write_cil_file {
write_file($filename, $lines);
}
+## ----------------------------------------------------------------------------
+# input
+
# this method based on Term::CallEditor(v0.11)'s solicit method
# original: Copyright 2004 by Jeremy Mates
# copied under the terms of the GPL
@@ -149,7 +154,8 @@ sub solicit {
flock $fh, LOCK_UN;
# run the editor
- my $status = system($editor, $filename);
+ my @editor_args = split(/\s+/, $editor);
+ my $status = system(@editor_args, $filename);
# check its return value
if ( $status != 0 ) {
@@ -183,6 +189,379 @@ sub ensure_interactive {
return;
}
+sub add_issue_loop {
+ my ($class, $cil, undef, $issue) = @_;
+
+ my $edit = $y;
+
+ # keep going until we get a valid issue or we want to quit
+ while ( $edit eq $y ) {
+ # read in the new issue text
+ my $fh = $class->solicit( $issue->as_output );
+ $issue = CIL::Issue->new_from_fh( 'tmp', $fh );
+
+ # check if the issue is valid
+ if ( $issue->is_valid($cil) ) {
+ $edit = 'n';
+ }
+ else {
+ $class->msg($_) foreach @{ $issue->errors };
+ $edit = ans('Would you like to re-edit (y/n): ');
+ }
+ }
+
+ # if the issue is still invalid, they quit without correcting it
+ return unless $issue->is_valid( $cil );
+
+ # we've got the issue, so let's name it
+ my $unique_str = time . $issue->Inserted . $issue->Summary . $issue->Description;
+ $issue->set_name( substr(md5_hex($unique_str), 0, 8) );
+ $issue->save($cil);
+
+ # should probably be run from with $cil
+ $cil->run_hook('issue_post_save', $issue);
+
+ $class->display_issue($cil, $issue);
+
+ return $issue;
+}
+
+sub add_comment_loop {
+ my ($class, $cil, undef, $issue, $comment) = @_;
+
+ my $edit = $y;
+
+ # keep going until we get a valid issue or we want to quit
+ while ( $edit eq $y ) {
+ # read in the new comment text
+ my $fh = CIL::Utils->solicit( $comment->as_output );
+ $comment = CIL::Comment->new_from_fh( 'tmp', $fh );
+
+ # check if the comment is valid
+ if ( $comment->is_valid($cil) ) {
+ $edit = 'n';
+ }
+ else {
+ $class->msg($_) foreach @{ $issue->errors };
+ $edit = $class->ans('Would you like to re-edit (y/n): ');
+ }
+ }
+
+ # if the comment is still invalid, they quit without correcting it
+ return unless $comment->is_valid( $cil );
+
+ # we've got the comment, so let's name it
+ my $unique_str = time . $comment->Inserted . $issue->Description;
+ $comment->set_name( substr(md5_hex($unique_str), 0, 8) );
+
+ # finally, save it
+ $comment->save($cil);
+
+ # add the comment to the issue, update it's timestamp and save it out
+ $issue->add_comment( $comment );
+ $issue->save($cil);
+ $class->display_issue_full($cil, $issue);
+
+ return $comment;
+}
+
+## ----------------------------------------------------------------------------
+# loading
+
+sub load_issue_fuzzy {
+ my ($class, $cil, $partial_name) = @_;
+
+ my $issues = $cil->list_issues_fuzzy( $partial_name );
+ unless ( defined $issues ) {
+ $class->fatal("Couldn't find any issues using '$partial_name'");
+ }
+
+ if ( @$issues > 1 ) {
+ $class->fatal('found multiple issues which match that name: ' . join(' ', map { $_->{name} } @$issues));
+ }
+
+ my $issue_name = $issues->[0]->{name};
+ my $issue = CIL::Issue->new_from_name($cil, $issue_name);
+ unless ( defined $issue ) {
+ $class->fatal("Couldn't load issue '$issue_name'");
+ }
+
+ return $issue;
+}
+
+sub load_comment_fuzzy {
+ my ($class, $cil, $partial_name) = @_;
+
+ my $comments = $cil->list_comments_fuzzy( $partial_name );
+ unless ( defined $comments ) {
+ $class->fatal("Couldn't find any comments using '$partial_name'");
+ }
+
+ if ( @$comments > 1 ) {
+ $class->fatal('found multiple comments which match that name: ' . join(' ', map { $_->{name} } @$comments));
+ }
+
+ my $comment_name = $comments->[0]->{name};
+ my $comment = CIL::comment->new_from_name($cil, $comment_name);
+ unless ( defined $comment ) {
+ $class->fatal("Couldn't load comment '$comment_name'");
+ }
+
+ return $comment;
+}
+
+sub load_attachment_fuzzy {
+ my ($class, $cil, $partial_name) = @_;
+
+ my $attachments = $cil->list_attachments_fuzzy( $partial_name );
+ unless ( defined $attachments ) {
+ $class->fatal("Couldn't find any attachments using '$partial_name'");
+ }
+
+ if ( @$attachments > 1 ) {
+ $class->fatal('found multiple attachments which match that name: ' . join(' ', map { $_->{name} } @$attachments));
+ }
+
+ my $attachment_name = $attachments->[0]->{name};
+ my $attachment = CIL::Attachment->new_from_name($cil, $attachment_name);
+ unless ( defined $attachment ) {
+ $class->fatal("Couldn't load attachment '$partial_name'");
+ }
+
+ return $attachment;
+}
+
+## ----------------------------------------------------------------------------
+# display
+
+sub display_issue {
+ my ($class, $cil, $issue) = @_;
+
+ $class->separator();
+ $class->title( 'Issue ' . $issue->name() );
+ $class->field( 'Summary', $issue->Summary() );
+ $class->field( 'Status', $issue->Status() );
+ $class->field( 'CreatedBy', $issue->CreatedBy() );
+ $class->field( 'AssignedTo', $issue->AssignedTo() );
+ $class->field( 'Label', $_ )
+ foreach sort @{$issue->LabelList()};
+ $class->field( 'Comment', $_ )
+ foreach sort @{$issue->CommentList()};
+ $class->field( 'Attachment', $_ )
+ foreach sort @{$issue->AttachmentList()};
+ $class->field( 'DependsOn', $_ )
+ foreach sort @{$issue->DependsOnList()};
+ $class->field( 'Precedes', $_ )
+ foreach sort @{$issue->PrecedesList()};
+ $class->field( 'Inserted', $issue->Inserted() );
+ $class->field( 'Updated', $issue->Inserted() );
+ $class->text('Description', $issue->Description());
+ $class->separator();
+}
+
+sub display_issue_full {
+ my ($class, $cil, $issue) = @_;
+
+ $class->separator();
+ $class->title( 'Issue ' . $issue->name() );
+ $class->field( 'Summary', $issue->Summary() );
+ $class->field( 'Status', $issue->Status() );
+ $class->field( 'CreatedBy', $issue->CreatedBy() );
+ $class->field( 'AssignedTo', $issue->AssignedTo() );
+ $class->field( 'Label', $_ )
+ foreach sort @{$issue->Label()};
+ $class->field( 'DependsOn', $_ )
+ foreach sort @{$issue->DependsOnList()};
+ $class->field( 'Precedes', $_ )
+ foreach sort @{$issue->PrecedesList()};
+ $class->field( 'Inserted', $issue->Inserted() );
+ $class->field( 'Updated', $issue->Updated() );
+ $class->text('Description', $issue->Description());
+
+ my $comments = $cil->get_comments_for( $issue );
+ foreach my $comment ( @$comments ) {
+ $class->display_comment( $cil, $comment );
+ }
+
+ my $attachments = $cil->get_attachments_for( $issue );
+ foreach my $attachment ( @$attachments ) {
+ $class->display_attachment( $cil, $attachment );
+ $class->msg();
+ }
+
+ $class->separator();
+}
+
+sub display_comment {
+ my ($class, $cil, $comment) = @_;
+
+ $class->title( 'Comment ' . $comment->name() );
+ $class->field( 'CreatedBy', $comment->CreatedBy() );
+ $class->field( 'Inserted', $comment->Inserted() );
+ $class->field( 'Updated', $comment->Inserted() );
+ $class->text('Description', $comment->Description());
+}
+
+sub display_attachment {
+ my ($class, $cil, $attachment) = @_;
+
+ $class->title( 'Attachment ' . $attachment->name() );
+ $class->field( 'Filename', $attachment->Filename() );
+ $class->field( 'CreatedBy', $attachment->CreatedBy() );
+ $class->field( 'Inserted', $attachment->Inserted() );
+ $class->field( 'Updated', $attachment->Inserted() );
+}
+
+sub filter_issues {
+ my ($class, $cil, $issues, $args) = @_;
+
+ # don't filter if we haven't been given anything
+ return $issues unless defined $args;
+ return $issues unless %$args;
+
+ # check that they aren't filtering on both --assigned-to and --is-mine
+ if ( defined $args->{a} and defined $args->{'is-mine'} ) {
+ $class->fatal("the --assigned-to and --is-mine filters are mutually exclusive");
+ }
+
+ # take a copy of the whole lot first (so we don't destroy the input list)
+ my @new_issues = @$issues;
+
+ # firstly, get out the Statuses we want
+ if ( defined $args->{s} ) {
+ @new_issues = grep { $_->Status eq $args->{s} } @new_issues;
+ }
+
+ # then see if we want a particular label (could be a bit nicer)
+ if ( defined $args->{l} ) {
+ my @tmp;
+ foreach my $issue ( @new_issues ) {
+ push @tmp, $issue
+ if grep { $_ eq $args->{l} } @{$issue->LabelList};
+ }
+ @new_issues = @tmp;
+ }
+
+ # filter out dependent on open/closed
+ if ( defined $args->{'is-open'} ) {
+ # just get the open issues
+ @new_issues = grep { $_->is_open($cil) } @new_issues;
+ }
+ if ( defined $args->{'is-closed'} ) {
+ # just get the closed issues
+ @new_issues = grep { $_->is_closed($cil) } @new_issues;
+ }
+
+ # filter out 'created by'
+ if ( defined $args->{c} ) {
+ @new_issues = grep { $args->{c} eq $_->created_by_email } @new_issues;
+ }
+
+ # filter out 'assigned to'
+ $args->{a} = $cil->UserEmail
+ if defined $args->{'is-mine'};
+ if ( defined $args->{a} ) {
+ @new_issues = grep { $args->{a} eq $_->assigned_to_email } @new_issues;
+ }
+
+ return \@new_issues;
+}
+
+sub separator {
+ my ($class) = @_;
+ $class->msg('=' x 79);
+}
+
+sub msg {
+ my ($class, $msg) = @_;
+ print ( defined $msg ? $msg : '' );
+ print "\n";
+}
+
+sub display_issue_summary {
+ my ($class, $issue) = @_;
+
+ my $msg = $issue->name();
+ $msg .= " ";
+ $msg .= $issue->Status();
+ $msg .= (' ' x ( 13 - length $issue->Status() ));
+ $msg .= $issue->Summary();
+
+ $class->msg($msg);
+}
+
+sub display_issue_headers {
+ my ($class, $issue) = @_;
+
+ $class->title( 'Issue ' . $issue->name() );
+ $class->field( 'Summary', $issue->Summary() );
+ $class->field( 'CreatedBy', $issue->CreatedBy() );
+ $class->field( 'AssignedTo', $issue->AssignedTo() );
+ $class->field( 'Inserted', $issue->Inserted() );
+ $class->field( 'Status', $issue->Status() );
+ $class->field( 'Labels', join(' ', @{$issue->LabelList()}) );
+ $class->field( 'DependsOn', join(' ', @{$issue->DependsOnList()}) );
+ $class->field( 'Precedes', join(' ', @{$issue->PrecedesList()}) );
+}
+
+sub title {
+ my ($class, $title) = @_;
+ my $msg = "--- $title ";
+ $msg .= '-' x (74 - length($title));
+ $class->msg($msg);
+}
+
+sub field {
+ my ($class, $field, $value) = @_;
+ my $msg = "$field";
+ $msg .= " " x (12 - length($field));
+ $class->msg("$msg: " . (defined $value ? $value : '') );
+}
+
+sub text {
+ my ($class, $field, $value) = @_;
+ $class->msg();
+ $class->msg($value);
+ $class->msg();
+}
+
+## ----------------------------------------------------------------------------
+# system
+
+sub check_paths {
+ my ($class, $cil) = @_;
+
+ # make sure an issue directory is available
+ unless ( $cil->dir_exists($cil->IssueDir) ) {
+ $class->fatal("couldn't find '" . $cil->IssueDir . "' directory");
+ }
+}
+
+sub ans {
+ my ($msg) = @_;
+ print $msg;
+ my $ans = <STDIN>;
+ chomp $ans;
+ print "\n";
+ return $ans;
+}
+
+sub err {
+ my ($class, $msg) = @_;
+ print STDERR ( defined $msg ? $msg : '' );
+ print STDERR "\n";
+}
+
+sub fatal {
+ my ($class, $msg) = @_;
+ chomp $msg;
+ print STDERR $msg, "\n";
+ exit 2;
+}
+
+## ----------------------------------------------------------------------------
+# helpers
+
sub extract_email_address {
my ($class, $text) = @_;
@@ -198,6 +577,11 @@ sub extract_email_address {
return $email_address;
}
+sub user {
+ my ($class, $cil) = @_;
+ return $cil->UserName . ' <' . $cil->UserEmail . '>';
+}
+
## ----------------------------------------------------------------------------
1;
## ----------------------------------------------------------------------------