# Type: Input # Category: Issue # Description: Shows list of issues # Author: Andriy Lesyuk new Orangutan::Context( response => sub { my ($context, $user, $message) = @_; my @result = ( ); # Remove ending $message =~ s/\?*!*\.*$//; my @regexps = ( '(?:(?:Give|Show)(?: me)? )?(?:(?:my|([^ ]+?)(?:\'s)?) )?todo(?: list)?', 'What (?:should|will|must) (?:I|([^ ]+)) (?:do|work on)(?: next)?', '(?:(?:Give|show)(?: me)? )?list of', 'Are there(?: any)?', 'What are', '(?:(?:Show|list)(?: me)? )?' ); # Check basic request for (my $i = 0; $i < scalar @regexps; $i++) { if ($message =~ /^$regexps[$i] */i) { if ($i < 2) { push(@result, 1); push(@result, $1 || 0); } else { push(@result, 2); push(@result, undef); } $message =~ s/^$regexps[$i] *//i; last; } } if (scalar @result > 0) { # Extract/remove project my $regexp = ' *(?:on|for|at|in|of|under) ["\']?(.+?)["\']? *'; if ($message =~ /\b$regexp$/i) { my $project = $main::query->ProjectExists($1); if (defined($project) && (ref $project eq 'HASH')) { push(@result, $project); $message =~ s/\b$regexp$//i; } else { push(@result, undef); } } else { push(@result, undef); } # Add "empty" elements push(@result, undef); # issue push(@result, undef); # tracker push(@result, undef); # status push(@result, undef); # priority if ($result[0] == 2) { my %options = ( ); # Should we include bugs assigned to other users? $regexp = ' *all *'; if ($message =~ s/^$regexp\b//i) { $options{'all'} = 1; } @regexps = ( '\b *(?:assigned to|of) (?:me|([^ ]+)) *$', '^ *(?:my|([^ ]+?)(?:\'s)?) *\b' ); # Extract/remove username for (my $i = 0; $i < scalar @regexps; $i++) { if ($message =~ /$regexps[$i]/i) { if (defined($1)) { my $id = $main::query->UserExists($1); if ($id) { $result[1] = $id; $message =~ s/$regexps[$i]//i; } } else { $result[1] = 0; $message =~ s/$regexps[$i]//i; } last; } } # Check for actual and overdue $regexp = ' *(actual|delayed|overdue) *'; if ($message =~ s/^$regexp\b//i) { if (lc($1) eq 'actual') { $options{'actual'} = 1; } else { $options{'overdue'} = 1; } } $regexp = ' *overtimed *'; if ($message =~ s/^$regexp\b//i) { $options{'overtimed'} = 1; } # Extract/remove unassigned if (!defined($result[1])) { @regexps = ( '\b *not assigned to anyone *$', '^ *unassigned *\b' ); for (my $i = 0; $i < scalar @regexps; $i++) { if ($message =~ /$regexps[$i]/i) { $result[1] = ''; $message =~ s/$regexps[$i]//i; last; } } } # Extract/remove priority my $priorities = $main::config->Get('redmine', '_priorities_regexp'); if ($priorities) { $regexp = ' *with ('.$priorities.') priority *'; if ($message =~ /\b$regexp$/i) { my $priorityid = $main::config->PriorityID($1); if (defined($priorityid)) { $result[6] = $priorityid; } $message =~ s/\b$regexp$//i; } } # Extract/remove status $regexp = 'open|closed'; my $statuses = $main::config->Get('redmine', '_statuses_regexp'); if ($statuses) { $regexp .= "|$statuses"; } if (($message =~ /^ *($regexp) *\b/i) || ($message =~ /\b *($regexp) *$/i)) { if (lc($1) eq 'closed') { $result[5] = ''; } elsif (lc($1) ne 'open') { my $statusid = $main::config->StatusID($1); if (defined($statusid)) { $result[5] = $statusid; } } $message =~ s/\b *(?:$regexp) *\b//i; } # Extract/remove priority if (!defined($result[6])) { $regexp = ' *('.$priorities.') *'; if ($message =~ /^$regexp\b/i) { my $priorityid = $main::config->PriorityID($1); if (defined($priorityid)) { $result[6] = $priorityid; } $message =~ s/^$regexp\b//i; } } my $trackers = $main::config->Get('redmine', '_trackers_regexp'); # Extract/remove issue id if ($trackers) { $regexp = " *(?:of|under) (?:(?:the )?(?:issue|$trackers) )?(?:#|No\.? ?)?([0-9]+) *"; } else { $regexp = ' *(?:of|under) (?:(?:the )?issue )?(?:#|No\.? ?)?([0-9]+) *'; } if ($message =~ /\b$regexp$/i) { $result[3] = $1; $message =~ s/\b$regexp$//i; } # Extract/remove tracker if ($trackers) { $regexp = " *(?:(?:sub)?issue|($trackers))e?s *"; } else { $regexp = ' *(?:sub)?issues *'; } if ($message =~ /^$regexp$/i) { my $trackerid = $main::config->TrackerID($1); if (defined($trackerid)) { $result[4] = $trackerid; } $message =~ s/^$regexp$//i; push(@result, %options); } else { # Tracker is required @result = ( ); } } if ($message !~ /^ *$/) { @result = ( ); } } return @result; }, handler => sub { my ($context, $user, $item, $userid, $project, $issueid, $tracker, $status, $priority, %options) = @_; if (defined($userid)) { if (($item == 1) && ($userid)) { $userid = $main::query->UserExists($userid); if (!$userid) { $user->SendMessage([ 'There is no such user.', "Can't find such user.", 'User does not exist.' ]); return; } } if ($userid eq '') { $userid = undef; } elsif ($userid == 0) { $userid = $user->GetUserID; } elsif (!$user->IsCreator && !$user->IsManager) { $user->SendMessage([ "Well... I'm not smart enough to be able to check if you have a permission to see issues of another user...", 'Currently I cannot show issues of another user. Please use Redmine!', 'This can be done only in Redmine...' ]); return; } } else { $userid = ''; } if (defined($issueid)) { my $parent = $main::query->GetIssue($issueid); if (!$parent) { my $format = Orangutan::Context::Random( 'There is no issue with number #%d...', 'Issue #%d does not exist.', 'No such issue!' ); $user->SendMessage(sprintf($format, $issueid)); return; } } if (defined($project)) { if (!$user->IsCreator && !$user->IsManager && !$main::query->Can('view_issues', $user, $project->{'id'})) { $user->SendMessage([ "You think I'm so stupid to let you see issues of this project?..", 'Redmine would not let you see issues of this project... Me too!', 'You have no permissions to see issues of this project...', 'You are not a member of this project!' ]); return; } } if ($main::query->GetIssuesEx($user, $userid, (defined($project)) ? $project->{'id'} : undef, (defined($project) && defined($project->{'version'})) ? $project->{'ver_id'} : undef, $issueid, $tracker, $status, $priority, %options)) { my $message; if ($main::query->Rows == 1) { $message = Orangutan::Context::Random( 'There is only one issue', 'I found only one issue', 'The issue is' ); } else { my $format = Orangutan::Context::Random( 'Here they go (%d issues)', 'They are (%d issues)', 'I found %d issues' ); $message = sprintf($format, $main::query->Rows); } $message .= ":\n"; my @issues = ( ); while (defined(my $issue = $main::query->Next)) { utf8::decode($issue->{'subject'}); utf8::decode($issue->{'project_name'}); $issue->{'status_name'} =~ tr/[A-Z]/[a-z]/; $issue->{'priority_name'} =~ tr/[A-Z]/[a-z]/; push(@issues, $issue); } my $i = $#issues; while ($i >= 0) { if ($issues[$i]->{'parent_id'} && !defined($issues[$i]->{'moved'})) { my $p = undef; for (my $j = $#issues; $j >= 0; $j--) { if ($i == $j) { next; } elsif (($issues[$i]->{'lft'} > $issues[$j]->{'lft'}) && ($issues[$i]->{'lft'} < $issues[$j]->{'rgt'})) { $p = $j + 1; } elsif (defined($p)) { last; } } if (defined($p) && ($i != $p)) { my $l = $i + 1; while ($l < scalar @issues) { if (($issues[$l]->{'lft'} < $issues[$i]->{'lft'}) || ($issues[$l]->{'lft'} > $issues[$i]->{'rgt'})) { last; } $l++; } $l--; $issues[$i]->{'moved'} = 1; if (($l - $i) > 0) { splice(@issues, $p, 0, @issues[$i..$l]); } else { splice(@issues, $p, 0, $issues[$i]); } if ($p > $i) { splice(@issues, $i, ($l - $i) + 1); } else { splice(@issues, $i + (($l - $i) + 1), ($l - $i) + 1); $i += ($l - $i); next; } } } $i--; } my $sub = "\xE2\x94\x94\xE2\x86\x92"; my $subsub = "\xE2\x94\x94\xE2\x94\x84\xE2\x86\x92"; utf8::decode($sub); utf8::decode($subsub); my @path = ( ); for (my $i = 0; $i < scalar @issues; $i++) { if ($i > 0) { if (($issues[$i]->{'lft'} > $issues[$i-1]->{'lft'}) && ($issues[$i]->{'lft'} < $issues[$i-1]->{'rgt'})) { push(@path, $i - 1); if ($issues[$i]->{'parent_id'} != $issues[$i-1]->{'id'}) { push(@path, undef); } } else { my $j = $#path; while ($j >= 0) { if (defined($path[$j]) && ($issues[$i]->{'lft'} > $issues[$path[$j]]->{'lft'}) && ($issues[$i]->{'lft'} < $issues[$path[$j]]->{'rgt'})) { last; } $j--; } $j++; if (($j >= 0) && ($j <= $#path)) { splice(@path, $j); } if (($j > 0) && ($issues[$i]->{'parent_id'} != $issues[$path[$j-1]]->{'id'})) { push(@path, undef); } } } $message .= ' '; for (my $j = 0; $j < scalar @path; $j++) { if (defined($path[$j])) { if ((($j + 1) < scalar @path) && !defined($path[$j+1])) { $message .= $subsub.' '; } else { $message .= $sub.' '; } } } $message .= sprintf("#%d) ", $issues[$i]->{'id'}); $message .= $issues[$i]->{'tracker_name'}; $message .= ' ('.$issues[$i]->{'status_name'}.'): '; $message .= $issues[$i]->{'subject'}; $message .= ' ['.$issues[$i]->{'priority_name'}.']'; if (!defined($project) || ($project->{'id'} != $issues[$i]->{'project_id'})) { $message .= ' ('.$issues[$i]->{'project_name'}.')'; } $message .= "\n"; } $user->SendMessage($message); } else { $user->SendMessage([ 'Cannot find anything...', 'No issues found.', 'No issues!' ]); } }, help => { title => 'How can I list issues?', question => '^(?:How )?(?:(?:can|do) I |to )(?:list|know) issues(?: of (?:(?:the|some) )?project)?\?*!*\.*$', answer => "Do not remember the issue ID? ". "Not sure what issue should be used? ". "Just ask Orangutan to list issues. ". "You can list all issues, issues of some project, only bugs, only new issues etc.\n". "The syntax of the request is:\n". "Show [all] [actual|overdue] [overtimed] [unassigned] s ". "[under ] [with priority] on \n". "Options are:\n". " o If \"all\" is specified Orangutan will list issues assigned to other users as well\n". " o If \"actual\" is specified Orangutan lists only actual issues\n". " o If \"overdue\" is specified Orangutan lists overdue issues\n". " o If \"overtimed\" is specified Orangutan lists issues with spent time greater than estimated hours\n". " o If \"unassigned\" is specified Orangutan lists only issues not assigned to anyone\n". "Some samples:\n". " o Are there any new tasks on Kayako?\n". " o What are issues with high priority?\n". " o Give me list of urgent issues\n". " o What should I do next?\n". " o Show subissues of #63\n". " o Show unassigned bugs\n". " o Show all open bugs\n". " o Show my todo list\n". "By default (e.g. on \"Show issues\") Orangutan shows issues assigned to user and unassigned issues...", weight => 840 }, weight => 92 ); # kate: syntax perl