mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Bug 24789 [E|A|R] Add Estimated, Actual, Remaining Time Fields
patch by jeff.hedlund@matrixsi.com 2xr=joel,justdave
This commit is contained in:
parent
a3ea9803b0
commit
dd8abb1dbb
@ -149,6 +149,11 @@ sub init {
|
||||
push(@specialchart, ["keywords", $t, $F{'keywords'}]);
|
||||
}
|
||||
|
||||
if (lsearch($fieldsref, "(SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id)) AS actual_time") != -1) {
|
||||
push(@supptables, "longdescs AS ldtime");
|
||||
push(@wherepart, "ldtime.bug_id = bugs.bug_id");
|
||||
}
|
||||
|
||||
foreach my $id ("1", "2") {
|
||||
if (!defined ($F{"email$id"})) {
|
||||
next;
|
||||
@ -323,6 +328,62 @@ sub init {
|
||||
push(@wherepart, "$table.bug_id = bugs.bug_id");
|
||||
$f = "$table.thetext";
|
||||
},
|
||||
"^work_time,changedby" => sub {
|
||||
my $table = "longdescs_$chartid";
|
||||
push(@supptables, "longdescs $table");
|
||||
push(@wherepart, "$table.bug_id = bugs.bug_id");
|
||||
my $id = &::DBNameToIdAndCheck($v);
|
||||
$term = "(($table.who = $id";
|
||||
$term .= ") AND ($table.work_time <> 0))";
|
||||
},
|
||||
"^work_time,changedbefore" => sub {
|
||||
my $table = "longdescs_$chartid";
|
||||
push(@supptables, "longdescs $table");
|
||||
push(@wherepart, "$table.bug_id = bugs.bug_id");
|
||||
$term = "(($table.bug_when < " . &::SqlQuote(SqlifyDate($v));
|
||||
$term .= ") AND ($table.work_time <> 0))";
|
||||
},
|
||||
"^work_time,changedafter" => sub {
|
||||
my $table = "longdescs_$chartid";
|
||||
push(@supptables, "longdescs $table");
|
||||
push(@wherepart, "$table.bug_id = bugs.bug_id");
|
||||
$term = "(($table.bug_when > " . &::SqlQuote(SqlifyDate($v));
|
||||
$term .= ") AND ($table.work_time <> 0))";
|
||||
},
|
||||
"^work_time," => sub {
|
||||
my $table = "longdescs_$chartid";
|
||||
push(@supptables, "longdescs $table");
|
||||
push(@wherepart, "$table.bug_id = bugs.bug_id");
|
||||
$f = "$table.work_time";
|
||||
},
|
||||
"^percentage_complete," => sub {
|
||||
my $oper;
|
||||
if ($t eq "equals") {
|
||||
$oper = "=";
|
||||
} elsif ($t eq "greaterthan") {
|
||||
$oper = ">";
|
||||
} elsif ($t eq "lessthan") {
|
||||
$oper = "<";
|
||||
} elsif ($t eq "notequal") {
|
||||
$oper = "<>";
|
||||
} elsif ($t eq "regexp") {
|
||||
$oper = "REGEXP";
|
||||
} elsif ($t eq "notregexp") {
|
||||
$oper = "NOT REGEXP";
|
||||
} else {
|
||||
$oper = "noop";
|
||||
}
|
||||
if ($oper ne "noop") {
|
||||
my $table = "longdescs_$chartid";
|
||||
push(@supptables, "longdescs $table");
|
||||
push(@wherepart, "$table.bug_id = bugs.bug_id");
|
||||
my $field = "(100*((SUM($table.work_time)*COUNT(DISTINCT $table.bug_when)/COUNT(bugs.bug_id))/((SUM($table.work_time)*COUNT(DISTINCT $table.bug_when)/COUNT(bugs.bug_id))+bugs.remaining_time))) AS percentage_complete_$table";
|
||||
push(@fields, $field);
|
||||
push(@having,
|
||||
"percentage_complete_$table $oper " . &::SqlQuote($v));
|
||||
}
|
||||
$term = "0=0";
|
||||
},
|
||||
"^bug_group,(?!changed)" => sub {
|
||||
push(@supptables, "LEFT JOIN bug_group_map bug_group_map_$chartid ON bugs.bug_id = bug_group_map_$chartid.bug_id");
|
||||
|
||||
|
@ -956,6 +956,7 @@ sub GetBugActivity {
|
||||
|
||||
my $query = "
|
||||
SELECT IFNULL(fielddefs.description, bugs_activity.fieldid),
|
||||
fielddefs.name,
|
||||
bugs_activity.attach_id,
|
||||
bugs_activity.bug_when,
|
||||
bugs_activity.removed, bugs_activity.added,
|
||||
@ -974,41 +975,59 @@ sub GetBugActivity {
|
||||
my $changes = [];
|
||||
my $incomplete_data = 0;
|
||||
|
||||
while (my ($field, $attachid, $when, $removed, $added, $who)
|
||||
while (my ($field, $fieldname, $attachid, $when, $removed, $added, $who)
|
||||
= FetchSQLData())
|
||||
{
|
||||
my %change;
|
||||
my $activity_visible = 1;
|
||||
|
||||
# This gets replaced with a hyperlink in the template.
|
||||
$field =~ s/^Attachment// if $attachid;
|
||||
# check if the user should see this field's activity
|
||||
if ($fieldname eq 'remaining_time' ||
|
||||
$fieldname eq 'estimated_time' ||
|
||||
$fieldname eq 'work_time') {
|
||||
|
||||
# Check for the results of an old Bugzilla data corruption bug
|
||||
$incomplete_data = 1 if ($added =~ /^\?/ || $removed =~ /^\?/);
|
||||
if (!UserInGroup(Param('timetrackinggroup'))) {
|
||||
$activity_visible = 0;
|
||||
} else {
|
||||
$activity_visible = 1;
|
||||
}
|
||||
} else {
|
||||
$activity_visible = 1;
|
||||
}
|
||||
|
||||
if ($activity_visible) {
|
||||
# This gets replaced with a hyperlink in the template.
|
||||
$field =~ s/^Attachment// if $attachid;
|
||||
|
||||
# Check for the results of an old Bugzilla data corruption bug
|
||||
$incomplete_data = 1 if ($added =~ /^\?/ || $removed =~ /^\?/);
|
||||
|
||||
# An operation, done by 'who' at time 'when', has a number of
|
||||
# 'changes' associated with it.
|
||||
# If this is the start of a new operation, store the data from the
|
||||
# previous one, and set up the new one.
|
||||
if ($operation->{'who'}
|
||||
&& ($who ne $operation->{'who'}
|
||||
|| $when ne $operation->{'when'}))
|
||||
{
|
||||
$operation->{'changes'} = $changes;
|
||||
push (@operations, $operation);
|
||||
# An operation, done by 'who' at time 'when', has a number of
|
||||
# 'changes' associated with it.
|
||||
# If this is the start of a new operation, store the data from the
|
||||
# previous one, and set up the new one.
|
||||
if ($operation->{'who'}
|
||||
&& ($who ne $operation->{'who'}
|
||||
|| $when ne $operation->{'when'}))
|
||||
{
|
||||
$operation->{'changes'} = $changes;
|
||||
push (@operations, $operation);
|
||||
|
||||
# Create new empty anonymous data structures.
|
||||
$operation = {};
|
||||
$changes = [];
|
||||
}
|
||||
# Create new empty anonymous data structures.
|
||||
$operation = {};
|
||||
$changes = [];
|
||||
}
|
||||
|
||||
$operation->{'who'} = $who;
|
||||
$operation->{'when'} = $when;
|
||||
$operation->{'who'} = $who;
|
||||
$operation->{'when'} = $when;
|
||||
|
||||
$change{'field'} = $field;
|
||||
$change{'attachid'} = $attachid;
|
||||
$change{'removed'} = $removed;
|
||||
$change{'added'} = $added;
|
||||
push (@$changes, \%change);
|
||||
$change{'field'} = $field;
|
||||
$change{'fieldname'} = $fieldname;
|
||||
$change{'attachid'} = $attachid;
|
||||
$change{'removed'} = $removed;
|
||||
$change{'added'} = $added;
|
||||
push (@$changes, \%change);
|
||||
}
|
||||
}
|
||||
|
||||
if ($operation->{'who'}) {
|
||||
|
@ -86,7 +86,8 @@ sub show_bug {
|
||||
reporter, bug_file_loc, short_desc, target_milestone,
|
||||
qa_contact, status_whiteboard,
|
||||
date_format(creation_ts,'%Y-%m-%d %H:%i'),
|
||||
delta_ts, sum(votes.count), delta_ts calc_disp_date
|
||||
delta_ts, sum(votes.count), delta_ts calc_disp_date,
|
||||
estimated_time, remaining_time
|
||||
FROM bugs LEFT JOIN votes USING(bug_id), products, components
|
||||
WHERE bugs.bug_id = $id
|
||||
AND bugs.product_id = products.id
|
||||
@ -110,7 +111,8 @@ sub show_bug {
|
||||
"priority", "bug_severity", "component_id", "component",
|
||||
"assigned_to", "reporter", "bug_file_loc", "short_desc",
|
||||
"target_milestone", "qa_contact", "status_whiteboard",
|
||||
"creation_ts", "delta_ts", "votes", "calc_disp_date")
|
||||
"creation_ts", "delta_ts", "votes", "calc_disp_date",
|
||||
"estimated_time", "remaining_time")
|
||||
{
|
||||
$value = shift(@row);
|
||||
if ($field eq "calc_disp_date") {
|
||||
@ -233,6 +235,14 @@ sub show_bug {
|
||||
push(@list, $i);
|
||||
}
|
||||
|
||||
if (UserInGroup(Param("timetrackinggroup"))) {
|
||||
|
||||
SendSQL("SELECT SUM(work_time)
|
||||
FROM longdescs WHERE longdescs.bug_id=$id");
|
||||
$bug{'actual_time'} = FetchSQLData();
|
||||
|
||||
}
|
||||
|
||||
$bug{'dependson'} = \@list;
|
||||
|
||||
my @list2;
|
||||
|
@ -382,8 +382,10 @@ DefineColumn("os" , "bugs.op_sys" , "OS"
|
||||
DefineColumn("target_milestone" , "bugs.target_milestone" , "Target Milestone" );
|
||||
DefineColumn("votes" , "bugs.votes" , "Votes" );
|
||||
DefineColumn("keywords" , "bugs.keywords" , "Keywords" );
|
||||
|
||||
|
||||
DefineColumn("estimated_time" , "bugs.estimated_time" , "Estimated Hours" );
|
||||
DefineColumn("remaining_time" , "bugs.remaining_time" , "Remaining Hours" );
|
||||
DefineColumn("actual_time" , "(SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id)) AS actual_time", "Actual Hours");
|
||||
DefineColumn("percentage_complete","(100*((SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id))/((SUM(ldtime.work_time)*COUNT(DISTINCT ldtime.bug_when)/COUNT(bugs.bug_id))+bugs.remaining_time))) AS percentage_complete", "% Complete");
|
||||
################################################################################
|
||||
# Display Column Determination
|
||||
################################################################################
|
||||
@ -430,6 +432,14 @@ if (trim($::FORM{'votes'}) && !grep($_ eq 'votes', @displaycolumns)) {
|
||||
push(@displaycolumns, 'votes');
|
||||
}
|
||||
|
||||
# Remove the timetracking columns if they are not a part of the group
|
||||
# (happens if a user had access to time tracking and it was revoked/disabled)
|
||||
if (!UserInGroup(Param("timetrackinggroup"))) {
|
||||
@displaycolumns = grep($_ ne 'estimated_time', @displaycolumns);
|
||||
@displaycolumns = grep($_ ne 'remaining_time', @displaycolumns);
|
||||
@displaycolumns = grep($_ ne 'actual_time', @displaycolumns);
|
||||
@displaycolumns = grep($_ ne 'percentage_complete', @displaycolumns);
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Select Column Determination
|
||||
@ -440,6 +450,12 @@ if (trim($::FORM{'votes'}) && !grep($_ eq 'votes', @displaycolumns)) {
|
||||
# The bug ID is always selected because bug IDs are always displayed
|
||||
my @selectcolumns = ("id");
|
||||
|
||||
# remaining and actual_time are required for precentage_complete calculation:
|
||||
if (lsearch(\@displaycolumns, "percentage_complete")) {
|
||||
push (@selectcolumns, "remaining_time");
|
||||
push (@selectcolumns, "actual_time");
|
||||
}
|
||||
|
||||
# Display columns are selected because otherwise we could not display them.
|
||||
push (@selectcolumns, @displaycolumns);
|
||||
|
||||
@ -459,6 +475,10 @@ if ($dotweak) {
|
||||
# Convert the list of columns being selected into a list of column names.
|
||||
my @selectnames = map($columns->{$_}->{'name'}, @selectcolumns);
|
||||
|
||||
# Remove columns with no names, such as percentage_complete
|
||||
# (or a removed *_time column due to permissions)
|
||||
@selectnames = grep($_ ne '', @selectnames);
|
||||
|
||||
# Generate the basic SQL query that will be used to generate the bug list.
|
||||
my $search = new Bugzilla::Search('fields' => \@selectnames,
|
||||
'url' => $::buffer);
|
||||
@ -538,6 +558,15 @@ if ($order) {
|
||||
# sort order was given
|
||||
$db_order =~ s/bugs.votes\s*(,|$)/bugs.votes desc$1/i;
|
||||
|
||||
# the 'actual_time' field is defined as an aggregate function, but
|
||||
# for order we just need the column name 'actual_time'
|
||||
my $aggregate_search = quotemeta($columns->{'actual_time'}->{'name'});
|
||||
$db_order =~ s/$aggregate_search/actual_time/g;
|
||||
|
||||
# the 'percentage_complete' field is defined as an aggregate too
|
||||
$aggregate_search = quotemeta($columns->{'percentage_complete'}->{'name'});
|
||||
$db_order =~ s/$aggregate_search/percentage_complete/g;
|
||||
|
||||
$query .= " ORDER BY $db_order ";
|
||||
}
|
||||
|
||||
|
@ -1438,6 +1438,8 @@ $table{bugs} =
|
||||
everconfirmed tinyint not null,
|
||||
reporter_accessible tinyint not null default 1,
|
||||
cclist_accessible tinyint not null default 1,
|
||||
estimated_time decimal(5,2) not null default 0,
|
||||
remaining_time decimal(5,2) not null default 0,
|
||||
alias varchar(20),
|
||||
|
||||
index (assigned_to),
|
||||
@ -1478,6 +1480,7 @@ $table{longdescs} =
|
||||
'bug_id mediumint not null,
|
||||
who mediumint not null,
|
||||
bug_when datetime not null,
|
||||
work_time decimal(5,2) not null default 0,
|
||||
thetext mediumtext,
|
||||
isprivate tinyint not null default 0,
|
||||
index(bug_id),
|
||||
@ -1853,6 +1856,8 @@ AddFDef("everconfirmed", "Ever Confirmed", 0);
|
||||
AddFDef("reporter_accessible", "Reporter Accessible", 0);
|
||||
AddFDef("cclist_accessible", "CC Accessible", 0);
|
||||
AddFDef("bug_group", "Group", 0);
|
||||
AddFDef("estimated_time", "Estimated Hours", 1);
|
||||
AddFDef("remaining_time", "Remaining Hours", 0);
|
||||
|
||||
# Oops. Bug 163299
|
||||
$dbh->do("DELETE FROM fielddefs WHERE name='cc_accessible'");
|
||||
@ -1860,6 +1865,8 @@ $dbh->do("DELETE FROM fielddefs WHERE name='cc_accessible'");
|
||||
AddFDef("flagtypes.name", "Flag", 0);
|
||||
AddFDef("requesters.login_name", "Flag Requester", 0);
|
||||
AddFDef("setters.login_name", "Flag Setter", 0);
|
||||
AddFDef("work_time", "Hours Worked", 0);
|
||||
AddFDef("percentage_complete", "Percentage Complete", 0);
|
||||
|
||||
###########################################################################
|
||||
# Detect changed local settings
|
||||
@ -2903,6 +2910,11 @@ if (GetFieldDef("bugs","qacontact_accessible")) {
|
||||
DropField("bugs", "assignee_accessible");
|
||||
}
|
||||
|
||||
# 2002-02-20 jeff.hedlund@matrixsi.com - bug 24789 time tracking
|
||||
AddField("longdescs", "work_time", "decimal(5,2) not null default 0");
|
||||
AddField("bugs", "estimated_time", "decimal(5,2) not null default 0");
|
||||
AddField("bugs", "remaining_time", "decimal(5,2) not null default 0");
|
||||
|
||||
# 2002-03-15 bbaetz@student.usyd.edu.au - bug 129466
|
||||
# 2002-05-13 preed@sigkill.com - bug 129446 patch backported to the
|
||||
# BUGZILLA-2_14_1-BRANCH as a security blocker for the 2.14.2 release
|
||||
|
@ -58,6 +58,11 @@ if (@::legal_keywords) {
|
||||
push(@masterlist, "keywords");
|
||||
}
|
||||
|
||||
if (UserInGroup(Param("timetrackinggroup"))) {
|
||||
push(@masterlist, ("estimated_time", "remaining_time", "actual_time",
|
||||
"percentage_complete"));
|
||||
}
|
||||
|
||||
push(@masterlist, ("summary", "summaryfull"));
|
||||
|
||||
$vars->{'masterlist'} = \@masterlist;
|
||||
|
@ -861,6 +861,14 @@ Reason: %reason%
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'timetrackinggroup',
|
||||
desc => 'The name of the group of users who can see/change time tracking ' .
|
||||
'information.',
|
||||
type => 't',
|
||||
default => ''
|
||||
},
|
||||
|
||||
{
|
||||
name => 'loginnetmask',
|
||||
desc => 'The number of bits for the netmask used if a user chooses to ' .
|
||||
|
@ -296,7 +296,8 @@ sub FetchOneColumn {
|
||||
"status", "resolution", "summary");
|
||||
|
||||
sub AppendComment {
|
||||
my ($bugid, $who, $comment, $isprivate, $timestamp) = @_;
|
||||
my ($bugid, $who, $comment, $isprivate, $timestamp, $work_time) = @_;
|
||||
$work_time ||= 0;
|
||||
|
||||
# Use the date/time we were given if possible (allowing calling code
|
||||
# to synchronize the comment's timestamp with those of other records).
|
||||
@ -304,15 +305,26 @@ sub AppendComment {
|
||||
|
||||
$comment =~ s/\r\n/\n/g; # Get rid of windows-style line endings.
|
||||
$comment =~ s/\r/\n/g; # Get rid of mac-style line endings.
|
||||
if ($comment =~ /^\s*$/) { # Nothin' but whitespace.
|
||||
|
||||
# allowing negatives though so people can back out errors in time reporting
|
||||
if (defined $work_time) {
|
||||
# regexp verifies one or more digits, optionally followed by a period and
|
||||
# zero or more digits, OR we have a period followed by one or more digits
|
||||
if ($work_time !~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/) {
|
||||
ThrowUserError("need_numeric_value");
|
||||
return;
|
||||
}
|
||||
} else { $work_time = 0 };
|
||||
|
||||
if ($comment =~ /^\s*$/) { # Nothin' but whitespace
|
||||
return;
|
||||
}
|
||||
|
||||
my $whoid = DBNameToIdAndCheck($who);
|
||||
my $privacyval = $isprivate ? 1 : 0 ;
|
||||
SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext, isprivate) " .
|
||||
SendSQL("INSERT INTO longdescs (bug_id, who, bug_when, thetext, isprivate, work_time) " .
|
||||
"VALUES($bugid, $whoid, $timestamp, " . SqlQuote($comment) . ", " .
|
||||
$privacyval . ")");
|
||||
$privacyval . ", " . SqlQuote($work_time) . ")");
|
||||
|
||||
SendSQL("UPDATE bugs SET delta_ts = now() WHERE bug_id = $bugid");
|
||||
}
|
||||
@ -1104,7 +1116,7 @@ sub GetLongDescriptionAsText {
|
||||
$query .= "ORDER BY longdescs.bug_when";
|
||||
SendSQL($query);
|
||||
while (MoreSQLData()) {
|
||||
my ($who, $when, $text, $isprivate) = (FetchSQLData());
|
||||
my ($who, $when, $text, $isprivate, $work_time) = (FetchSQLData());
|
||||
if ($count) {
|
||||
$result .= "\n\n------- Additional Comments From $who".Param('emailsuffix')." ".
|
||||
time2str("%Y-%m-%d %H:%M", str2time($when)) . " -------\n";
|
||||
@ -1124,7 +1136,7 @@ sub GetComments {
|
||||
my @comments;
|
||||
SendSQL("SELECT profiles.realname, profiles.login_name,
|
||||
date_format(longdescs.bug_when,'%Y-%m-%d %H:%i'),
|
||||
longdescs.thetext,
|
||||
longdescs.thetext, longdescs.work_time,
|
||||
isprivate,
|
||||
date_format(longdescs.bug_when,'%Y%m%d%H%i%s')
|
||||
FROM longdescs, profiles
|
||||
@ -1134,7 +1146,8 @@ sub GetComments {
|
||||
|
||||
while (MoreSQLData()) {
|
||||
my %comment;
|
||||
($comment{'name'}, $comment{'email'}, $comment{'time'}, $comment{'body'},
|
||||
($comment{'name'}, $comment{'email'}, $comment{'time'},
|
||||
$comment{'body'}, $comment{'work_time'},
|
||||
$comment{'isprivate'}, $comment{'when'}) = FetchSQLData();
|
||||
|
||||
$comment{'email'} .= Param('emailsuffix');
|
||||
@ -1490,6 +1503,20 @@ sub PerformSubsts {
|
||||
return $str;
|
||||
}
|
||||
|
||||
sub FormatTimeUnit {
|
||||
# Returns a number with 2 digit precision, unless the last digit is a 0
|
||||
# then it returns only 1 digit precision
|
||||
my ($time) = (@_);
|
||||
|
||||
my $newtime = sprintf("%.2f", $time);
|
||||
|
||||
if ($newtime =~ /0\Z/) {
|
||||
$newtime = sprintf("%.1f", $time);
|
||||
}
|
||||
|
||||
return $newtime;
|
||||
|
||||
}
|
||||
###############################################################################
|
||||
# Global Templatization Code
|
||||
|
||||
|
@ -56,7 +56,9 @@ my $generic_query = "
|
||||
bugs.target_milestone,
|
||||
bugs.qa_contact,
|
||||
bugs.status_whiteboard,
|
||||
bugs.keywords
|
||||
bugs.keywords,
|
||||
bugs.estimated_time,
|
||||
bugs.remaining_time
|
||||
FROM bugs,profiles assign,profiles report, products, components
|
||||
WHERE assign.userid = bugs.assigned_to AND report.userid = bugs.reporter
|
||||
AND bugs.product_id=products.id AND bugs.component_id=components.id";
|
||||
@ -79,7 +81,8 @@ foreach my $bug_id (split(/[:,]/, $buglist)) {
|
||||
"op_sys", "bug_status", "resolution", "priority",
|
||||
"bug_severity", "component", "assigned_to", "reporter",
|
||||
"bug_file_loc", "short_desc", "target_milestone",
|
||||
"qa_contact", "status_whiteboard", "keywords")
|
||||
"qa_contact", "status_whiteboard", "keywords",
|
||||
"estimated_time", "remaining_time")
|
||||
{
|
||||
$bug{$field} = shift @row;
|
||||
}
|
||||
@ -91,6 +94,12 @@ foreach my $bug_id (split(/[:,]/, $buglist)) {
|
||||
|
||||
push (@bugs, \%bug);
|
||||
}
|
||||
|
||||
if (UserInGroup(Param("timetrackinggroup"))) {
|
||||
SendSQL("SELECT SUM(work_time) FROM longdescs WHERE bug_id=$bug_id");
|
||||
|
||||
$bug{'actual_time'} = FetchSQLData();
|
||||
}
|
||||
}
|
||||
|
||||
# Add the list of bug hashes to the variables
|
||||
|
@ -233,7 +233,8 @@ if ($::FORM{'keywords'} && UserInGroup("editbugs")) {
|
||||
|
||||
# Build up SQL string to add bug.
|
||||
my $sql = "INSERT INTO bugs " .
|
||||
"(" . join(",", @used_fields) . ", reporter, creation_ts) " .
|
||||
"(" . join(",", @used_fields) . ", reporter, creation_ts, " .
|
||||
"estimated_time, remaining_time) " .
|
||||
"VALUES (";
|
||||
|
||||
foreach my $field (@used_fields) {
|
||||
@ -246,7 +247,23 @@ $comment = trim($comment);
|
||||
# OK except for the fact that it causes e-mail to be suppressed.
|
||||
$comment = $comment ? $comment : " ";
|
||||
|
||||
$sql .= "$::userid, now() )";
|
||||
$sql .= "$::userid, now(), ";
|
||||
|
||||
# Time Tracking
|
||||
if (UserInGroup(Param("timetrackinggroup")) &&
|
||||
defined $::FORM{'estimated_time'}) {
|
||||
|
||||
my $est_time = $::FORM{'estimated_time'};
|
||||
if ($est_time =~ /^(?:\d+(?:\.\d*)?|\.\d+)$/) {
|
||||
$sql .= SqlQuote($est_time) . "," . SqlQuote($est_time);
|
||||
} else {
|
||||
$vars->{'field'} = "estimated_time";
|
||||
ThrowUserError("need_positive_number");
|
||||
}
|
||||
} else {
|
||||
$sql .= "0, 0";
|
||||
}
|
||||
$sql .= ")";
|
||||
|
||||
# Groups
|
||||
my @groupstoadd = ();
|
||||
|
@ -703,6 +703,25 @@ if (defined $::FORM{'qa_contact'}) {
|
||||
}
|
||||
}
|
||||
|
||||
# jeff.hedlund@matrixsi.com time tracking data processing:
|
||||
foreach my $field ("estimated_time", "remaining_time") {
|
||||
|
||||
if (defined $::FORM{$field}) {
|
||||
my $er_time = trim($::FORM{$field});
|
||||
if ($er_time ne $::FORM{'dontchange'}) {
|
||||
if ($er_time > 99999.99) {
|
||||
ThrowUserError("value_out_of_range", {variable => $field});
|
||||
}
|
||||
if ($er_time =~ /^(?:\d+(?:\.\d*)?|\.\d+)$/) {
|
||||
DoComma();
|
||||
$::query .= "$field = " . SqlQuote($er_time);
|
||||
} else {
|
||||
$vars->{'field'} = $field;
|
||||
ThrowUserError("need_positive_number");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If the user is submitting changes from show_bug.cgi for a single bug,
|
||||
# and that bug is restricted to a group, process the checkboxes that
|
||||
@ -808,6 +827,12 @@ SWITCH: for ($::FORM{'knob'}) {
|
||||
last SWITCH;
|
||||
};
|
||||
/^resolve$/ && CheckonComment( "resolve" ) && do {
|
||||
if (UserInGroup(Param('timetrackinggroup'))) {
|
||||
if (defined $::FORM{'remaining_time'} &&
|
||||
$::FORM{'remaining_time'} > 0) {
|
||||
ThrowUserError("resolving_remaining_time");
|
||||
}
|
||||
}
|
||||
# Check here, because its the only place we require the resolution
|
||||
CheckFormField(\%::FORM, 'resolution', \@::settable_resolution);
|
||||
ChangeStatus('RESOLVED');
|
||||
@ -1170,6 +1195,26 @@ foreach my $id (@idlist) {
|
||||
}
|
||||
}
|
||||
|
||||
SendSQL("select now()");
|
||||
$timestamp = FetchOneColumn();
|
||||
|
||||
if ($::FORM{'work_time'} > 99999.99) {
|
||||
ThrowUserError("value_out_of_range", {variable => 'work_time'});
|
||||
}
|
||||
if (defined $::FORM{'comment'} || defined $::FORM{'work_time'}) {
|
||||
if ($::FORM{'work_time'} != 0 &&
|
||||
(!defined $::FORM{'comment'} || $::FORM{'comment'} =~ /^\s*$/)) {
|
||||
|
||||
ThrowUserError('comment_required');
|
||||
} else {
|
||||
AppendComment($id, $::COOKIE{'Bugzilla_login'}, $::FORM{'comment'},
|
||||
$::FORM{'commentprivacy'}, $timestamp, $::FORM{'work_time'});
|
||||
if ($::FORM{'work_time'} != 0) {
|
||||
LogActivityEntry($id, "work_time", "", $::FORM{'work_time'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (@::legal_keywords) {
|
||||
# There are three kinds of "keywordsaction": makeexact, add, delete.
|
||||
# For makeexact, we delete everything, and then add our things.
|
||||
@ -1229,17 +1274,11 @@ foreach my $id (@idlist) {
|
||||
SendSQL("DELETE FROM bug_group_map
|
||||
WHERE bug_id = $id AND group_id = $grouptodel");
|
||||
}
|
||||
SendSQL("select now()");
|
||||
$timestamp = FetchOneColumn();
|
||||
|
||||
my $groupDelNames = join(',', @groupDelNames);
|
||||
my $groupAddNames = join(',', @groupAddNames);
|
||||
|
||||
LogActivityEntry($id, "bug_group", $groupDelNames, $groupAddNames);
|
||||
if (defined $::FORM{'comment'}) {
|
||||
AppendComment($id, $::COOKIE{'Bugzilla_login'}, $::FORM{'comment'},
|
||||
$::FORM{'commentprivacy'}, $timestamp);
|
||||
}
|
||||
|
||||
my $removedCcString = "";
|
||||
if (defined $::FORM{newcc} || defined $::FORM{removecc} || defined $::FORM{masscc}) {
|
||||
|
@ -129,12 +129,13 @@ sub ProcessOneBug {
|
||||
if ($values{'qa_contact'}) {
|
||||
$values{'qa_contact'} = DBID_to_name($values{'qa_contact'});
|
||||
}
|
||||
$values{'estimated_time'} = FormatTimeUnit($values{'estimated_time'});
|
||||
|
||||
my @diffs;
|
||||
|
||||
|
||||
SendSQL("SELECT profiles.login_name, fielddefs.description, " .
|
||||
" bug_when, removed, added, attach_id " .
|
||||
" bug_when, removed, added, attach_id, fielddefs.name " .
|
||||
"FROM bugs_activity, fielddefs, profiles " .
|
||||
"WHERE bug_id = $id " .
|
||||
" AND fielddefs.fieldid = bugs_activity.fieldid " .
|
||||
@ -150,21 +151,32 @@ sub ProcessOneBug {
|
||||
}
|
||||
|
||||
my $difftext = "";
|
||||
my $diffheader = "";
|
||||
my $diffpart = {};
|
||||
my @diffparts;
|
||||
my $lastwho = "";
|
||||
foreach my $ref (@diffs) {
|
||||
my ($who, $what, $when, $old, $new, $attachid) = (@$ref);
|
||||
my ($who, $what, $when, $old, $new, $attachid, $fieldname) = (@$ref);
|
||||
$diffpart = {};
|
||||
if ($who ne $lastwho) {
|
||||
$lastwho = $who;
|
||||
$difftext .= "\n$who" . Param('emailsuffix') . " changed:\n\n";
|
||||
$difftext .= FormatTriple("What ", "Removed", "Added");
|
||||
$difftext .= ('-' x 76) . "\n";
|
||||
$diffheader = "\n$who" . Param('emailsuffix') . " changed:\n\n";
|
||||
$diffheader .= FormatTriple("What ", "Removed", "Added");
|
||||
$diffheader .= ('-' x 76) . "\n";
|
||||
}
|
||||
$what =~ s/^Attachment/Attachment #$attachid/ if $attachid;
|
||||
$difftext .= FormatTriple($what, $old, $new);
|
||||
if( $fieldname eq 'estimated_time' ||
|
||||
$fieldname eq 'remaining_time' ) {
|
||||
$old = FormatTimeUnit($old);
|
||||
$new = FormatTimeUnit($new);
|
||||
}
|
||||
$difftext = FormatTriple($what, $old, $new);
|
||||
$diffpart->{'header'} = $diffheader;
|
||||
$diffpart->{'fieldname'} = $fieldname;
|
||||
$diffpart->{'text'} = $difftext;
|
||||
push(@diffparts, $diffpart);
|
||||
}
|
||||
|
||||
$difftext = trim($difftext);
|
||||
|
||||
|
||||
my $deptext = "";
|
||||
|
||||
@ -220,7 +232,9 @@ sub ProcessOneBug {
|
||||
$deptext = trim($deptext);
|
||||
|
||||
if ($deptext) {
|
||||
$difftext = trim($difftext . "\n\n" . $deptext);
|
||||
#$difftext = trim($difftext . "\n\n" . $deptext);
|
||||
$diffpart->{'text'} = trim("\n\n" . $deptext);
|
||||
push(@diffparts, $diffpart);
|
||||
}
|
||||
|
||||
|
||||
@ -301,9 +315,9 @@ sub ProcessOneBug {
|
||||
if ( !defined(NewProcessOnePerson($person, $count, \@headerlist,
|
||||
\@reasons, \%values,
|
||||
\%defmailhead,
|
||||
\%fielddescription, $difftext,
|
||||
$newcomments, $anyprivate,
|
||||
$start, $id,
|
||||
\%fielddescription, \@diffparts,
|
||||
$newcomments,
|
||||
$anyprivate, $start, $id,
|
||||
\@depbugs)))
|
||||
{
|
||||
|
||||
@ -613,14 +627,16 @@ sub filterEmailGroup ($$$) {
|
||||
}
|
||||
|
||||
sub NewProcessOnePerson ($$$$$$$$$$$$$) {
|
||||
my ($person, $count, $hlRef, $reasonsRef, $valueRef, $dmhRef, $fdRef, $difftext,
|
||||
$newcomments, $anyprivate, $start, $id, $depbugsRef) = @_;
|
||||
my ($person, $count, $hlRef, $reasonsRef, $valueRef, $dmhRef, $fdRef,
|
||||
$diffRef, $newcomments, $anyprivate, $start,
|
||||
$id, $depbugsRef) = @_;
|
||||
|
||||
my %values = %$valueRef;
|
||||
my @headerlist = @$hlRef;
|
||||
my @reasons = @$reasonsRef;
|
||||
my %defmailhead = %$dmhRef;
|
||||
my %fielddescription = %$fdRef;
|
||||
my @diffparts = @$diffRef;
|
||||
my @depbugs = @$depbugsRef;
|
||||
|
||||
if ($seen{$person}) {
|
||||
@ -680,10 +696,41 @@ sub NewProcessOnePerson ($$$$$$$$$$$$$) {
|
||||
if (! $value) {
|
||||
next;
|
||||
}
|
||||
my $desc = $fielddescription{$f};
|
||||
$head .= FormatDouble($desc, $value);
|
||||
# Don't send estimated_time if user not in the group, or not enabled
|
||||
if ($f ne 'estimated_time' ||
|
||||
UserInGroup(Param('timetrackinggroup'), $userid)) {
|
||||
|
||||
my $desc = $fielddescription{$f};
|
||||
$head .= FormatDouble($desc, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Build difftext (the actions) by verifying the user should see them
|
||||
my $difftext = "";
|
||||
my $diffheader = "";
|
||||
my $add_diff;
|
||||
foreach my $diff (@diffparts) {
|
||||
|
||||
$add_diff = 0;
|
||||
|
||||
if ($diff->{'fieldname'} eq 'estimated_time' ||
|
||||
$diff->{'fieldname'} eq 'remaining_time' ||
|
||||
$diff->{'fieldname'} eq 'work_time') {
|
||||
if (UserInGroup(Param("timetrackinggroup"), $userid)) {
|
||||
$add_diff = 1;
|
||||
}
|
||||
} else {
|
||||
$add_diff = 1;
|
||||
}
|
||||
if ($add_diff) {
|
||||
if ($diffheader ne $diff->{'header'}) {
|
||||
$diffheader = $diff->{'header'};
|
||||
$difftext .= $diffheader;
|
||||
}
|
||||
$difftext .= $diff->{'text'};
|
||||
}
|
||||
}
|
||||
|
||||
if ($difftext eq "" && $newcomments eq "") {
|
||||
# Whoops, no differences!
|
||||
|
@ -281,7 +281,16 @@ shift @::legal_resolution;
|
||||
# Another hack - this array contains "" for some reason. See bug 106589.
|
||||
$vars->{'resolution'} = \@::legal_resolution;
|
||||
|
||||
$vars->{'chfield'} = ["[Bug creation]", @::log_columns];
|
||||
my @chfields = @::log_columns;
|
||||
push @chfields, "[Bug creation]";
|
||||
if (UserInGroup(Param('timetrackinggroup'))) {
|
||||
push @chfields, "work_time";
|
||||
} else {
|
||||
@chfields = grep($_ ne "estimated_time", @chfields);
|
||||
@chfields = grep($_ ne "remaining_time", @chfields);
|
||||
}
|
||||
@chfields = (sort(@chfields));
|
||||
$vars->{'chfield'} = \@chfields;
|
||||
$vars->{'bug_status'} = \@::legal_bug_status;
|
||||
$vars->{'rep_platform'} = \@::legal_platform;
|
||||
$vars->{'op_sys'} = \@::legal_opsys;
|
||||
@ -295,6 +304,13 @@ push(@fields, { name => "noop", description => "---" });
|
||||
SendSQL("SELECT name, description FROM fielddefs ORDER BY sortkey");
|
||||
while (MoreSQLData()) {
|
||||
my ($name, $description) = FetchSQLData();
|
||||
if (($name eq "estimated_time" ||
|
||||
$name eq "remaining_time" ||
|
||||
$name eq "work_time" ||
|
||||
$name eq "percentage_complete" ) &&
|
||||
(!UserInGroup(Param('timetrackinggroup')))) {
|
||||
next;
|
||||
}
|
||||
push(@fields, { name => $name, description => $description });
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
# incomplete_data: boolean. True if some of the data is incomplete (because
|
||||
# it was affected by an old Bugzilla bug.)
|
||||
#%]
|
||||
|
||||
[% PROCESS bug/time.html.tmpl %]
|
||||
|
||||
[% IF incomplete_data %]
|
||||
<p>
|
||||
@ -72,14 +74,26 @@
|
||||
</td>
|
||||
<td>
|
||||
[% IF change.removed %]
|
||||
[% change.removed FILTER html %]
|
||||
[% IF change.fieldname == 'estimated_time' ||
|
||||
change.fieldname == 'remaining_time' ||
|
||||
change.fieldname == 'work_time' %]
|
||||
[% PROCESS formattimeunit time_unit=change.removed %]
|
||||
[% ELSE %]
|
||||
[% change.removed FILTER html %]
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
|
||||
[% END %]
|
||||
</td>
|
||||
<td>
|
||||
[% IF change.added %]
|
||||
[% change.added FILTER html %]
|
||||
[% IF change.fieldname == 'estimated_time' ||
|
||||
change.fieldname == 'remaining_time' ||
|
||||
change.fieldname == 'work_time' %]
|
||||
[% PROCESS formattimeunit time_unit=change.added %]
|
||||
[% ELSE %]
|
||||
[% change.added FILTER html %]
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
|
||||
[% END %]
|
||||
|
@ -30,6 +30,7 @@
|
||||
[% count = count + 1 %]
|
||||
[% END %]
|
||||
|
||||
[% PROCESS bug/time.html.tmpl %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Block for individual comments #%]
|
||||
@ -43,9 +44,11 @@
|
||||
<i>------- Additional Comment
|
||||
<a name="c[% count %]" href="#c[% count %]">#[% count %]</a> From
|
||||
<a href="mailto:[% comment.email FILTER html %]">[% comment.name FILTER html %]</a>
|
||||
[%+ comment.time %] -------
|
||||
[%+ comment.time %]
|
||||
-------
|
||||
</i>
|
||||
[% END %]
|
||||
|
||||
[% IF mode == "edit" && isinsider %]
|
||||
<i>
|
||||
<input type=hidden name="oisprivate-[% count %]"
|
||||
@ -55,7 +58,12 @@
|
||||
[% " checked=\"checked\"" IF comment.isprivate %]> Private
|
||||
</i>
|
||||
[% END %]
|
||||
|
||||
[% IF UserInGroup(Param('timetrackinggroup')) &&
|
||||
(comment.work_time > 0 || comment.work_time < 0) %]
|
||||
<br>
|
||||
Additional hours worked:
|
||||
[% PROCESS formattimeunit time_unit=comment.work_time %]
|
||||
[% END %]
|
||||
[%# Don't indent the <pre> block, since then the spaces are displayed in the
|
||||
# generated HTML
|
||||
#%]
|
||||
|
@ -155,6 +155,20 @@
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
|
||||
[% IF UserInGroup(Param('timetrackinggroup')) %]
|
||||
<tr>
|
||||
<td align="right"><strong>Estimated Hours:</strong></td>
|
||||
<td colspan="3">
|
||||
<input name="estimated_time" size="6" maxlength="6" value="0.0"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td colspan="3"></td>
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
<tr>
|
||||
<td align="right"><strong>URL:</strong></td>
|
||||
<td colspan="3">
|
||||
|
@ -32,6 +32,29 @@
|
||||
[% END %]
|
||||
|
||||
[% PROCESS bug/navigate.html.tmpl %]
|
||||
[% PROCESS bug/time.html.tmpl %]
|
||||
|
||||
<script type="text/javascript" language="JavaScript">
|
||||
<!--
|
||||
var fRemainingTime = [% bug.remaining_time %]; // holds the original value
|
||||
function adjustRemainingTime() {
|
||||
// subtracts time spent from remaining time
|
||||
var new_time;
|
||||
|
||||
new_time =
|
||||
fRemainingTime - document.changeform.work_time.value;
|
||||
// get upto 2 decimal places
|
||||
document.changeform.remaining_time.value =
|
||||
Math.round(new_time * 100)/100;
|
||||
}
|
||||
|
||||
function updateRemainingTime() {
|
||||
// if the remaining time is changed manually, update fRemainingTime
|
||||
fRemainingTime = document.changeform.remaining_time.value;
|
||||
}
|
||||
|
||||
//-->
|
||||
</script>
|
||||
|
||||
<hr>
|
||||
|
||||
@ -264,6 +287,62 @@
|
||||
</tr>
|
||||
[% END %]
|
||||
</table>
|
||||
|
||||
[% IF UserInGroup(Param('timetrackinggroup')) %]
|
||||
<br>
|
||||
<table cellpadding=0 cellspacing=0 border=1>
|
||||
<tr>
|
||||
<th width="16.6%" align="center" bgcolor="#cccccc">
|
||||
Orig. Est.
|
||||
</th>
|
||||
<th width="16.6%" align="center" bgcolor="#cccccc">
|
||||
Current Est.
|
||||
</th>
|
||||
<th width="16.6%" align="center" bgcolor="#cccccc">
|
||||
Hours Worked
|
||||
</th>
|
||||
<th width="16.6%" align="center" bgcolor="#cccccc">
|
||||
Hours Left
|
||||
</th>
|
||||
<th width="16.6%" align="center" bgcolor="#cccccc">
|
||||
%Complete
|
||||
</th>
|
||||
<th width="16.6%" align="center" bgcolor="#cccccc">
|
||||
Gain
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<input name="estimated_time"
|
||||
value="[% PROCESS formattimeunit
|
||||
time_unit=bug.estimated_time %]"
|
||||
size="6" maxlength="6">
|
||||
</td>
|
||||
<td align="center">
|
||||
[% PROCESS formattimeunit
|
||||
time_unit=(bug.actual_time + bug.remaining_time) %]
|
||||
</td>
|
||||
<td align="center">
|
||||
[% PROCESS formattimeunit time_unit=bug.actual_time %] +
|
||||
<input name="work_time" value="0" size="3" maxlength="6"
|
||||
onChange="adjustRemainingTime();">
|
||||
</td>
|
||||
<td align="center">
|
||||
<input name="remaining_time"
|
||||
value="[% PROCESS formattimeunit
|
||||
time_unit=bug.remaining_time %]"
|
||||
size="6" maxlength="6" onChange="updateRemainingTime();">
|
||||
</td>
|
||||
<td align="center">
|
||||
[% PROCESS calculatepercentage act=bug.actual_time
|
||||
rem=bug.remaining_time %]
|
||||
</td>
|
||||
<td align="center">
|
||||
[% PROCESS formattimeunit time_unit=bug.estimated_time - (bug.actual_time + bug.remaining_time) %]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
[% END %]
|
||||
|
||||
[%# *** Attachments *** %]
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
title = "Full Text Bug Listing"
|
||||
style_urls = [ "css/show_multiple.css" ]
|
||||
%]
|
||||
[% PROCESS bug/time.html.tmpl %]
|
||||
[% IF bugs.first %]
|
||||
[% FOREACH bug = bugs %]
|
||||
[% PROCESS bug_display %]
|
||||
@ -34,6 +35,7 @@
|
||||
</p>
|
||||
[% END %]
|
||||
|
||||
|
||||
[% PROCESS global/footer.html.tmpl %]
|
||||
|
||||
|
||||
@ -130,6 +132,32 @@
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
[% IF UserInGroup(Param("timetrackinggroup")) %]
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<b>Orig. Est.:</b>
|
||||
[% PROCESS formattimeunit time_unit=bug.estimated_time %]
|
||||
|
||||
<b>Current Est.:</b>
|
||||
[% PROCESS formattimeunit
|
||||
time_unit=(bug.remaining_time + bug.actual_time) %]
|
||||
|
||||
<b>Hours Worked:</b>
|
||||
[% PROCESS formattimeunit time_unit=bug.actual_time %]
|
||||
<b>Hours Left:</b>
|
||||
[% PROCESS formattimeunit time_unit=bug.remaining_time %]
|
||||
|
||||
<b>Percentage Complete:</b>
|
||||
[% PROCESS calculatepercentage act=bug.actual_time
|
||||
rem=bug.remaining_time %]
|
||||
<b>Gain</b>
|
||||
[% PROCESS formattimeunit
|
||||
time_unit=bug.estimated_time - (bug.actual_time + bug.remaining_time) %]
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<b>Description:</b>
|
||||
|
48
webtools/bugzilla/template/en/default/bug/time.html.tmpl
Normal file
48
webtools/bugzilla/template/en/default/bug/time.html.tmpl
Normal file
@ -0,0 +1,48 @@
|
||||
<!-- 1.0@bugzilla.org -->
|
||||
[%# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla Bug Tracking System.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape Communications
|
||||
# Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s): Jeff Hedlund <jeff.hedlund@matrixsi.com>
|
||||
#
|
||||
#%]
|
||||
|
||||
[% BLOCK formattimeunit %]
|
||||
[%# INTERFACE:
|
||||
# time_unit: the number converting, converts to 2 decimal places
|
||||
# unless the last character is a 0, then it truncates to
|
||||
# 1 decimal place
|
||||
#%]
|
||||
[% time_unit = time_unit FILTER format('%.2f') %]
|
||||
[% IF time_unit.match('0\Z') %]
|
||||
[% time_unit FILTER format('%.1f') %]
|
||||
[% ELSE %]
|
||||
[% time_unit FILTER format('%.2f') %]
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% BLOCK calculatepercentage %]
|
||||
[%# INTERFACE:
|
||||
# act: actual time
|
||||
# rem: remaining time
|
||||
# %]
|
||||
[% IF (act + rem) > 0 %]
|
||||
[% (act / (act + rem)) * 100
|
||||
FILTER format("%d") %]
|
||||
[% ELSE %]
|
||||
0
|
||||
[% END %]
|
||||
[% END %]
|
@ -330,8 +330,16 @@
|
||||
|
||||
[% ELSIF error == "need_component" %]
|
||||
[% title = "Component Required" %]
|
||||
You must specify a component to help determine the new owner of these bugs.
|
||||
|
||||
You must specify a component to help determine the new owner of these bugs.
|
||||
|
||||
[% ELSIF error == "need_numeric_value" %]
|
||||
[% title = "Numeric Value Required" %]
|
||||
Hours requires a numeric value.
|
||||
|
||||
[% ELSIF error == "need_positive_number" %]
|
||||
[% title = "Positive Number Required" %]
|
||||
[% field %] requires a positive number.
|
||||
|
||||
[% ELSIF error == "need_product" %]
|
||||
[% title = "Product Required" %]
|
||||
You must specify a product to help determine the new owner of these bugs.
|
||||
@ -445,7 +453,7 @@
|
||||
[% ELSIF error == "report_access_denied" %]
|
||||
[% title = "Access Denied" %]
|
||||
You do not have the permissions necessary to view reports for this product.
|
||||
|
||||
|
||||
[% ELSIF error == "requestee_too_short" %]
|
||||
[% title = "Requestee Name Too Short" %]
|
||||
One or two characters match too many users, so please enter at least
|
||||
@ -470,6 +478,11 @@
|
||||
[% ELSIF error == "require_summary" %]
|
||||
[% title = "Summary Needed" %]
|
||||
You must enter a summary for this bug.
|
||||
|
||||
[% ELSIF error == "resolving_remaining_time" %]
|
||||
[% title = "Trying to Resolve with Hours Remaining" %]
|
||||
You cannot resolve a bug with hours still remaining. Set
|
||||
Remaining Hours to zero if you want to resolve the bug.
|
||||
|
||||
[% ELSIF error == "sanity_check_access_denied" %]
|
||||
[% title = "Access Denied" %]
|
||||
@ -521,6 +534,10 @@
|
||||
[% title = "Wrong Token" %]
|
||||
That token cannot be used to change your email address.
|
||||
|
||||
[% ELSIF error == "value_out_of_range" %]
|
||||
[% title = "Value Out Of Range" %]
|
||||
Value is out of range for field [% variable %].
|
||||
|
||||
[% ELSIF error == "z_axis_defined_with_no_x_axis" %]
|
||||
[% title = "Nonsensical Options" %]
|
||||
You've defined a field for multiple tables without having defined
|
||||
|
@ -116,6 +116,25 @@
|
||||
|
||||
</tr>
|
||||
|
||||
[% IF UserInGroup(Param("timetrackinggroup")) %]
|
||||
<tr>
|
||||
<th><label for="estimated_time">Estimated Hours:</label></th>
|
||||
<td>
|
||||
<input id="estimated_time"
|
||||
name="estimated_time"
|
||||
value="[% dontchange FILTER html %]"
|
||||
size="6">
|
||||
</td>
|
||||
<th><label for="remaining_time">Remaining Hours:</label></th>
|
||||
<td>
|
||||
<input id="remaining_time"
|
||||
name="remaining_time"
|
||||
value="[% dontchange FILTER html %]"
|
||||
size="6">
|
||||
</td>
|
||||
</tr>
|
||||
[% END %]
|
||||
|
||||
[% IF Param("useqacontact") %]
|
||||
<tr>
|
||||
<th><label for="qa_contact">QA Contact:</label></th>
|
||||
|
@ -49,11 +49,14 @@
|
||||
"version" => { maxlength => 5 , title => "Vers" } ,
|
||||
"os" => { maxlength => 4 } ,
|
||||
"target_milestone" => { title => "TargetM" } ,
|
||||
"percentage_complete" => { format_value => "%d %%" } ,
|
||||
}
|
||||
%]
|
||||
|
||||
[% qorder = order FILTER url_quote IF order %]
|
||||
|
||||
[% PROCESS bug/time.html.tmpl %]
|
||||
|
||||
[%############################################################################%]
|
||||
[%# Table Header #%]
|
||||
[%############################################################################%]
|
||||
@ -132,7 +135,15 @@
|
||||
[% FOREACH column = displaycolumns %]
|
||||
<td>
|
||||
[% '<nobr>' IF NOT abbrev.$column.wrap %]
|
||||
[%- bug.$column.truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html -%]
|
||||
[% IF abbrev.$column.format_value %]
|
||||
[%- bug.$column FILTER format(abbrev.$column.format_value) FILTER html -%]
|
||||
[% ELSIF column == 'actual_time' ||
|
||||
column == 'remaining_time' ||
|
||||
column == 'estimated_time' %]
|
||||
[% PROCESS formattimeunit time_unit=bug.$column %]
|
||||
[% ELSE %]
|
||||
[%- bug.$column.truncate(abbrev.$column.maxlength, abbrev.$column.ellipsis) FILTER html -%]
|
||||
[% END %]
|
||||
[%- '</nobr>' IF NOT abbrev.$column.wrap %]
|
||||
</td>
|
||||
[% END %]
|
||||
|
Loading…
Reference in New Issue
Block a user