diff options
-rwxr-xr-x | editmilestones.cgi | 665 | ||||
-rw-r--r-- | t/008filter.t | 2 | ||||
-rw-r--r-- | template/en/default/admin/milestones/confirm-delete.html.tmpl | 135 | ||||
-rw-r--r-- | template/en/default/admin/milestones/create.html.tmpl | 59 | ||||
-rw-r--r-- | template/en/default/admin/milestones/created.html.tmpl | 44 | ||||
-rw-r--r-- | template/en/default/admin/milestones/deleted.html.tmpl | 69 | ||||
-rw-r--r-- | template/en/default/admin/milestones/edit.html.tmpl | 67 | ||||
-rw-r--r-- | template/en/default/admin/milestones/footer.html.tmpl | 66 | ||||
-rw-r--r-- | template/en/default/admin/milestones/list.html.tmpl | 77 | ||||
-rw-r--r-- | template/en/default/admin/milestones/select-product.html.tmpl | 60 | ||||
-rw-r--r-- | template/en/default/admin/milestones/updated.html.tmpl | 58 | ||||
-rw-r--r-- | template/en/default/filterexceptions.pl | 11 | ||||
-rw-r--r-- | template/en/default/global/user-error.html.tmpl | 37 |
13 files changed, 1028 insertions, 322 deletions
diff --git a/editmilestones.cgi b/editmilestones.cgi index aaec24455..ad07b2d61 100755 --- a/editmilestones.cgi +++ b/editmilestones.cgi @@ -11,6 +11,8 @@ # # Matt Masson <matthew@zeroknowledge.com> # +# Contributors : Gavin Shelley <bugzilla@chimpychompy.org> +# use strict; @@ -22,6 +24,10 @@ require "globals.pl"; use Bugzilla::Constants; use Bugzilla::Config qw(:DEFAULT $datadir); +use vars qw($template $vars); + +my $cgi = Bugzilla->cgi; + # TestProduct: just returns if the specified product does exists # CheckProduct: same check, optionally emit an error text # TestMilestone: just returns if the specified product/version combination exists @@ -29,119 +35,88 @@ use Bugzilla::Config qw(:DEFAULT $datadir); sub TestProduct ($) { - my $prod = shift; + my $product = shift; + + trick_taint($product); # does the product exist? - SendSQL("SELECT name - FROM products - WHERE name=" . SqlQuote($prod)); - return FetchOneColumn(); + my $dbh = Bugzilla->dbh; + my $sth = $dbh->prepare_cached("SELECT name + FROM products + WHERE name = ?"); + $sth->execute($product); + + my ($row) = $sth->fetchrow_array; + + $sth->finish; + + return $row; } sub CheckProduct ($) { - my $prod = shift; + my $product = shift; # do we have a product? - unless ($prod) { - print "Sorry, you haven't specified a product."; - PutTrailer(); + unless ($product) { + &::ThrowUserError('product_not_specified'); exit; } - unless (TestProduct $prod) { - print "Sorry, product '$prod' does not exist."; - PutTrailer(); + # Does it exist in the DB? + unless (TestProduct $product) { + &::ThrowUserError('product_doesnt_exist', + {'product' => $product}); exit; } } sub TestMilestone ($$) { - my ($prod,$mile) = @_; + my ($product, $milestone) = @_; + + my $dbh = Bugzilla->dbh; # does the product exist? - SendSQL("SELECT products.name, value + my $sth = $dbh->prepare_cached(" + SELECT products.name, value FROM milestones, products - WHERE milestones.product_id=products.id AND products.name=" . SqlQuote($prod) . " and value=" . SqlQuote($mile)); - return FetchOneColumn(); -} + WHERE milestones.product_id = products.id + AND products.name = ? + AND value = ?"); -sub CheckMilestone ($$) -{ - my ($prod,$mile) = @_; + trick_taint($product); + trick_taint($milestone); - # do we have the milestone? - unless ($mile) { - print "Sorry, you haven't specified a milestone."; - PutTrailer(); - exit; - } + $sth->execute($product, $milestone); - CheckProduct($prod); - - unless (TestMilestone $prod,$mile) { - print "Sorry, milestone '$mile' for product '$prod' does not exist."; - PutTrailer(); - exit; - } -} + my ($db_milestone) = $sth->fetchrow_array(); + $sth->finish(); -# -# Displays the form to edit a milestone -# + return $db_milestone; +} -sub EmitFormElements ($$$) +sub CheckMilestone ($$) { - my ($product, $milestone, $sortkey) = @_; - - print " <TH ALIGN=\"right\">Milestone:</TH>\n"; - print " <TD><INPUT SIZE=64 MAXLENGTH=64 NAME=\"milestone\" VALUE=\"" . - value_quote($milestone) . "\">\n"; - print "</TR><TR>\n"; - print " <TH ALIGN=\"right\">Sortkey:</TH>\n"; - print " <TD><INPUT SIZE=64 MAXLENGTH=64 NAME=\"sortkey\" VALUE=\"" . - value_quote($sortkey) . "\">\n"; - print " <INPUT TYPE=HIDDEN NAME=\"product\" VALUE=\"" . - value_quote($product) . "\"></TD>\n"; -} + my ($product, $milestone) = @_; + # do we have the milestone and product combination? + unless ($milestone) { + ThrowUserError('milestone_not_specified'); + exit; + } -# -# Displays a text like "a.", "a or b.", "a, b or c.", "a, b, c or d." -# + CheckProduct($product); -sub PutTrailer (@) -{ - my (@links) = ("Back to the <A HREF=\"query.cgi\">query page</A>", @_); - SendSQL("UNLOCK TABLES"); - - my $count = $#links; - my $num = 0; - print "<P>\n"; - foreach (@links) { - print $_; - if ($num == $count) { - print ".\n"; - } - elsif ($num == $count-1) { - print " or "; - } - else { - print ", "; - } - $num++; + unless (TestMilestone $product, $milestone) { + ThrowUserError('milestone_not_valid', + {'product' => $product, + 'milestone' => $milestone}); + exit; } - PutFooter(); } - - - - - - # # Preliminary checks: # @@ -151,10 +126,7 @@ Bugzilla->login(LOGIN_REQUIRED); print Bugzilla->cgi->header(); unless (UserInGroup("editcomponents")) { - PutHeader("Not allowed"); - print "Sorry, you aren't a member of the 'editcomponents' group.\n"; - print "And so, you aren't allowed to add, modify or delete milestones.\n"; - PutTrailer(); + ThrowUserError('auth_cant_edit_milestones'); exit; } @@ -162,44 +134,44 @@ unless (UserInGroup("editcomponents")) { # # often used variables # -my $product = trim($::FORM{product} || ''); -my $milestone = trim($::FORM{milestone} || ''); -my $sortkey = trim($::FORM{sortkey} || '0'); -my $action = trim($::FORM{action} || ''); -my $localtrailer; -if ($milestone) { - $localtrailer = "<A HREF=\"editmilestones.cgi?product=" . url_quote($product) . "\">edit</A> more milestones"; -} else { - $localtrailer = "<A HREF=\"editmilestones.cgi\">edit</A> more milestones"; -} - - +my $product = trim($cgi->param('product') || ''); +my $milestone = trim($cgi->param('milestone') || ''); +my $sortkey = trim($cgi->param('sortkey') || '0'); +my $action = trim($cgi->param('action') || ''); # # product = '' -> Show nice list of milestones # unless ($product) { - PutHeader("Select product"); - - SendSQL("SELECT products.name,products.description - FROM products - GROUP BY products.name - ORDER BY products.name"); - print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0><TR BGCOLOR=\"#6666FF\">\n"; - print " <TH ALIGN=\"left\">Edit milestones of ...</TH>\n"; - print " <TH ALIGN=\"left\">Description</TH>\n"; - print "</TR>"; - while ( MoreSQLData() ) { - my ($product, $description) = FetchSQLData(); - $description ||= "<FONT COLOR=\"red\">missing</FONT>"; - print "<TR>\n"; - print " <TD VALIGN=\"top\"><A HREF=\"editmilestones.cgi?product=", url_quote($product), "\"><B>$product</B></A></TD>\n"; - print " <TD VALIGN=\"top\">$description</TD>\n"; + + my @products = (); + + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare_cached('SELECT products.name, products.description + FROM products + ORDER BY products.name'); + + my $data = $dbh->selectall_arrayref($sth); + + foreach my $aref (@$data) { + + my $prod = {}; + + my ($name, $description) = @$aref; + + $prod->{'name'} = $name; + $prod->{'description'} = $description; + + push(@products, $prod); } - print "</TR></TABLE>\n"; - PutTrailer(); + $vars->{'products'} = \@products; + $template->process("admin/milestones/select-product.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + exit; } @@ -210,37 +182,39 @@ unless ($product) { # unless ($action) { - PutHeader("Select milestone for $product"); + CheckProduct($product); my $product_id = get_product_id($product); + my @milestones = (); + + my $dbh = Bugzilla->dbh; + + my $sth = $dbh->prepare_cached('SELECT value, sortkey + FROM milestones + WHERE product_id = ? + ORDER BY sortkey, value'); + + my $data = $dbh->selectall_arrayref($sth, + undef, + $product_id); + + foreach my $aref (@$data) { - SendSQL("SELECT value,sortkey - FROM milestones - WHERE product_id=$product_id - ORDER BY sortkey,value"); - - print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0><TR BGCOLOR=\"#6666FF\">\n"; - print " <TH ALIGN=\"left\">Edit milestone ...</TH>\n"; - #print " <TH ALIGN=\"left\">Bugs</TH>\n"; - print " <TH ALIGN=\"left\">Sortkey</TH>\n"; - print " <TH ALIGN=\"left\">Action</TH>\n"; - print "</TR>"; - while ( MoreSQLData() ) { - my ($milestone,$sortkey,$bugs) = FetchSQLData(); - $bugs ||= 'none'; - print "<TR>\n"; - print " <TD VALIGN=\"top\"><A HREF=\"editmilestones.cgi?product=", url_quote($product), "&milestone=", url_quote($milestone), "&action=edit\"><B>$milestone</B></A></TD>\n"; - #print " <TD VALIGN=\"top\">$bugs</TD>\n"; - print " <TD VALIGN=\"top\" ALIGN=\"right\">$sortkey</TD>\n"; - print " <TD VALIGN=\"top\"><A HREF=\"editmilestones.cgi?product=", url_quote($product), "&milestone=", url_quote($milestone), "&action=del\"><B>Delete</B></A></TD>\n"; - print "</TR>"; + my $milestone = {}; + my ($name, $sortkey) = @$aref; + + $milestone->{'name'} = $name; + $milestone->{'sortkey'} = $sortkey; + + push(@milestones, $milestone); } - print "<TR>\n"; - print " <TD VALIGN=\"top\" COLSPAN=\"2\">Add a new milestone</TD>\n"; - print " <TD VALIGN=\"top\" ALIGN=\"middle\"><A HREF=\"editmilestones.cgi?product=", url_quote($product) . "&action=add\">Add</A></TD>\n"; - print "</TR></TABLE>\n"; - PutTrailer(); + $vars->{'product'} = $product; + $vars->{'milestones'} = \@milestones; + $template->process("admin/milestones/list.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + exit; } @@ -254,25 +228,15 @@ unless ($action) { # if ($action eq 'add') { - PutHeader("Add milestone for $product"); + CheckProduct($product); my $product_id = get_product_id($product); - #print "This page lets you add a new milestone to a $::bugzilla_name tracked product.\n"; + $vars->{'product'} = $product; + $template->process("admin/milestones/create.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); - print "<FORM METHOD=POST ACTION=editmilestones.cgi>\n"; - print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n"; - - EmitFormElements($product, $milestone, 0); - - print "</TABLE>\n<HR>\n"; - print "<INPUT TYPE=SUBMIT VALUE=\"Add\">\n"; - print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"new\">\n"; - print "</FORM>"; - - my $other = $localtrailer; - $other =~ s/more/other/; - PutTrailer($other); exit; } @@ -283,42 +247,57 @@ if ($action eq 'add') { # if ($action eq 'new') { - PutHeader("Adding new milestone for $product"); + CheckProduct($product); my $product_id = get_product_id($product); # Cleanups and valididy checks - unless ($milestone) { - print "You must enter a text for the new milestone. Please press\n"; - print "<b>Back</b> and try again.\n"; - PutTrailer($localtrailer); + ThrowUserError('milestone_blank_name', + {'name' => $milestone}); + exit; + } + + if (length($milestone) > 20) { + ThrowUserError('milestone_name_too_long', + {'name' => $milestone}); exit; } + + # Need to store in case detaint_natural() clears the sortkey + my $stored_sortkey = $sortkey; if (!detaint_natural($sortkey)) { - print "The sortkey for a milestone must be a number. Please press\n"; - print "<b>Back</b> and try again.\n"; - PutTrailer($localtrailer); + ThrowUserError('milestone_sortkey_invalid', + {'name' => $milestone, + 'sortkey' => $stored_sortkey}); exit; } - if (TestMilestone($product,$milestone)) { - print "The milestone '$milestone' already exists. Please press\n"; - print "<b>Back</b> and try again.\n"; - PutTrailer($localtrailer); + if (TestMilestone($product, $milestone)) { + ThrowUserError('milestone_already_exists', + {'name' => $milestone, + 'product' => $product}); exit; } # Add the new milestone - SendSQL("INSERT INTO milestones ( " . - "value, product_id, sortkey" . - " ) VALUES ( " . - SqlQuote($milestone) . ", $product_id, $sortkey)"); + my $dbh = Bugzilla->dbh; + trick_taint($milestone); + $dbh->do('INSERT INTO milestones ( value, product_id, sortkey ) + VALUES ( ?, ?, ? )', + undef, + $milestone, + $product_id, + $sortkey); # Make versioncache flush unlink "$datadir/versioncache"; - print "OK, done.<p>\n"; - PutTrailer("<A HREF=\"editmilestones.cgi?product=$product&action=add\">add</a> another milestone or $localtrailer"); + $vars->{'name'} = $milestone; + $vars->{'product'} = $product; + $template->process("admin/milestones/created.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + exit; } @@ -332,72 +311,38 @@ if ($action eq 'new') { # if ($action eq 'del') { - PutHeader("Delete milestone of $product"); + CheckMilestone($product, $milestone); my $product_id = get_product_id($product); - SendSQL("SELECT count(bug_id), product_id, target_milestone - FROM bugs - GROUP BY product_id, target_milestone - HAVING product_id=$product_id - AND target_milestone=" . SqlQuote($milestone)); - my $bugs = FetchOneColumn(); - - SendSQL("SELECT defaultmilestone FROM products " . - "WHERE id=$product_id"); - my $defaultmilestone = FetchOneColumn(); - - print "<TABLE BORDER=1 CELLPADDING=4 CELLSPACING=0>\n"; - print "<TR BGCOLOR=\"#6666FF\">\n"; - print " <TH VALIGN=\"top\" ALIGN=\"left\">Part</TH>\n"; - print " <TH VALIGN=\"top\" ALIGN=\"left\">Value</TH>\n"; - - print "</TR><TR>\n"; - print " <TH ALIGN=\"left\" VALIGN=\"top\">Product:</TH>\n"; - print " <TD VALIGN=\"top\">$product</TD>\n"; - print "</TR><TR>\n"; - print " <TH ALIGN=\"left\" VALIGN=\"top\">Milestone:</TH>\n"; - print " <TD VALIGN=\"top\">$milestone</TD>\n"; - print "</TR><TR>\n"; - print " <TH ALIGN=\"left\" VALIGN=\"top\">Bugs:</TH>\n"; - print " <TD VALIGN=\"top\">", $bugs || 'none' , "</TD>\n"; - print "</TR></TABLE>\n"; - - print "<H2>Confirmation</H2>\n"; - - if ($bugs) { - if (!Param("allowbugdeletion")) { - print "Sorry, there are $bugs bugs outstanding for this milestone. -You must reassign those bugs to another milestone before you can delete this -one."; - PutTrailer($localtrailer); - exit; - } - print "<TABLE BORDER=0 CELLPADDING=20 WIDTH=\"70%\" BGCOLOR=\"red\"><TR><TD>\n", - "There are bugs entered for this milestone! When you delete this ", - "milestone, <B><BLINK>all</BLINK></B> stored bugs will be deleted, too. ", - "You could not even see the bug history for this milestone anymore!\n", - "</TD></TR></TABLE>\n"; - } + my $dbh = Bugzilla->dbh; - if ($defaultmilestone eq $milestone) { - print "Sorry; this is the default milestone for this product, and " . - "so it can not be deleted."; - PutTrailer($localtrailer); - exit; - } + my $sth = $dbh->prepare('SELECT count(bug_id), product_id, target_milestone + FROM bugs + GROUP BY product_id, target_milestone + HAVING product_id = ? + AND target_milestone = ?'); + + trick_taint($milestone); + $vars->{'bug_count'} = $dbh->selectrow_array($sth, + undef, + $product_id, + $milestone) || 0; + + $sth = $dbh->prepare('SELECT defaultmilestone + FROM products + WHERE id = ?'); + + $vars->{'default_milestone'} = $dbh->selectrow_array($sth, + undef, + $product_id) || ''; + + $vars->{'name'} = $milestone; + $vars->{'product'} = $product; + $template->process("admin/milestones/confirm-delete.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); - print "<P>Do you really want to delete this milestone?<P>\n"; - print "<FORM METHOD=POST ACTION=editmilestones.cgi>\n"; - print "<INPUT TYPE=SUBMIT VALUE=\"Yes, delete\">\n"; - print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"delete\">\n"; - print "<INPUT TYPE=HIDDEN NAME=\"product\" VALUE=\"" . - value_quote($product) . "\">\n"; - print "<INPUT TYPE=HIDDEN NAME=\"milestone\" VALUE=\"" . - value_quote($milestone) . "\">\n"; - print "</FORM>"; - - PutTrailer($localtrailer); exit; } @@ -408,55 +353,89 @@ one."; # if ($action eq 'delete') { - PutHeader("Deleting milestone of $product"); + CheckMilestone($product,$milestone); my $product_id = get_product_id($product); + my $dbh = Bugzilla->dbh; + # lock the tables before we start to change everything: - SendSQL("LOCK TABLES attachments WRITE, - bugs WRITE, - bugs_activity WRITE, - milestones WRITE, - dependencies WRITE"); + $dbh->do('LOCK TABLES attachments WRITE, + bugs WRITE, + bugs_activity WRITE, + milestones WRITE, + dependencies WRITE'); # According to MySQL doc I cannot do a DELETE x.* FROM x JOIN Y, # so I have to iterate over bugs and delete all the indivial entries # in bugs_activies and attachments. + # Detaint this here, as we may need it if deleting bugs, but will + # definitely need it detainted whhen we actually delete the + # milestone itself + trick_taint($milestone); + if (Param("allowbugdeletion")) { - SendSQL("SELECT bug_id - FROM bugs - WHERE product_id=$product_id - AND target_milestone=" . SqlQuote($milestone)); - while (MoreSQLData()) { - my $bugid = FetchOneColumn(); - - PushGlobalSQLState(); - SendSQL("DELETE FROM attachments WHERE bug_id=$bugid"); - SendSQL("DELETE FROM bugs_activity WHERE bug_id=$bugid"); - SendSQL("DELETE FROM dependencies WHERE blocked=$bugid"); - PopGlobalSQLState(); + my $deleted_bug_count = 0; + + my $sth = $dbh->prepare_cached('SELECT bug_id + FROM bugs + WHERE product_id = ? + AND target_milestone = ?'); + + my $data = $dbh->selectall_arrayref($sth, + undef, + $product_id, + $milestone); + + foreach my $aref (@$data) { + + my ($bugid) = @$aref; + + $dbh->do('DELETE FROM attachments WHERE bug_id = ?', + undef, + $bugid); + $dbh->do('DELETE FROM bugs_activity WHERE bug_id = ?', + undef, + $bugid); + $dbh->do('DELETE FROM dependencies WHERE blocked = ?', + undef, + $bugid); + + $deleted_bug_count++; } - print "Attachments, bug activity and dependencies deleted.<BR>\n"; + $vars->{'deleted_bug_count'} = $deleted_bug_count; # Deleting the rest is easier: - SendSQL("DELETE FROM bugs - WHERE product_id=$product_id - AND target_milestone=" . SqlQuote($milestone)); - print "Bugs deleted.<BR>\n"; + $dbh->do('DELETE FROM bugs + WHERE product_id = ? + AND target_milestone = ?', + undef, + $product_id, + $milestone); } - SendSQL("DELETE FROM milestones - WHERE product_id=$product_id - AND value=" . SqlQuote($milestone)); - print "Milestone deleted.<P>\n"; + $dbh->do('DELETE FROM milestones + WHERE product_id = ? + AND value = ?', + undef, + $product_id, + $milestone); + + $dbh->do('UNLOCK TABLES'); unlink "$datadir/versioncache"; - PutTrailer($localtrailer); + + + $vars->{'name'} = $milestone; + $vars->{'product'} = $product; + $template->process("admin/milestones/deleted.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); exit; } @@ -469,33 +448,31 @@ if ($action eq 'delete') { # if ($action eq 'edit') { - PutHeader("Edit milestone of $product"); - CheckMilestone($product,$milestone); + + CheckMilestone($product, $milestone); my $product_id = get_product_id($product); - SendSQL("SELECT sortkey FROM milestones WHERE product_id=$product_id " . - " AND value = " . SqlQuote($milestone)); - my $sortkey = FetchOneColumn(); + my $dbh = Bugzilla->dbh; - print "<FORM METHOD=POST ACTION=editmilestones.cgi>\n"; - print "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0><TR>\n"; + my $sth = $dbh->prepare_cached('SELECT sortkey + FROM milestones + WHERE product_id = ? + AND value = ?'); - EmitFormElements($product, $milestone, $sortkey); + trick_taint($milestone); - print "</TR></TABLE>\n"; + $vars->{'sortkey'} = $dbh->selectrow_array($sth, + undef, + $product_id, + $milestone) || 0; - print "<INPUT TYPE=HIDDEN NAME=\"milestoneold\" VALUE=\"" . - value_quote($milestone) . "\">\n"; - print "<INPUT TYPE=HIDDEN NAME=\"sortkeyold\" VALUE=\"" . - value_quote($sortkey) . "\">\n"; - print "<INPUT TYPE=HIDDEN NAME=\"action\" VALUE=\"update\">\n"; - print "<INPUT TYPE=SUBMIT VALUE=\"Update\">\n"; + $vars->{'name'} = $milestone; + $vars->{'product'} = $product; - print "</FORM>"; + $template->process("admin/milestones/edit.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); - my $other = $localtrailer; - $other =~ s/more/other/; - PutTrailer($other); exit; } @@ -506,69 +483,117 @@ if ($action eq 'edit') { # if ($action eq 'update') { - PutHeader("Update milestone of $product"); - my $milestoneold = trim($::FORM{milestoneold} || ''); - my $sortkeyold = trim($::FORM{sortkeyold} || '0'); + my $milestoneold = trim($cgi->param('milestoneold') || ''); + my $sortkeyold = trim($cgi->param('sortkeyold') || '0'); - CheckMilestone($product,$milestoneold); + CheckMilestone($product, $milestoneold); my $product_id = get_product_id($product); - SendSQL("LOCK TABLES bugs WRITE, - milestones WRITE, - products WRITE"); + if (length($milestone) > 20) { + ThrowUserError('milestone_name_too_long', + {'name' => $milestone}); + exit; + } + + my $dbh = Bugzilla->dbh; + + $dbh->do("LOCK TABLES bugs WRITE, + milestones WRITE, + products WRITE"); + # Need to store because detaint_natural() will delete this if + # invalid + my $stored_sortkey = $sortkey; if ($sortkey != $sortkeyold) { if (!detaint_natural($sortkey)) { - print "The sortkey for a milestone must be a number. Please press\n"; - print "<b>Back</b> and try again.\n"; - PutTrailer($localtrailer); + + $dbh->do('UNLOCK TABLES'); + ThrowUserError('milestone_sortkey_invalid', + {'name' => $milestone, + 'sortkey' => $stored_sortkey}); + exit; } - SendSQL("UPDATE milestones SET sortkey=$sortkey - WHERE product_id=" . $product_id . " - AND value=" . SqlQuote($milestoneold)); + + trick_taint($milestoneold); + + $dbh->do('UPDATE milestones SET sortkey = ? + WHERE product_id = ? + AND value = ?', + undef, + $sortkey, + $product_id, + $milestoneold); + unlink "$datadir/versioncache"; - print "Updated sortkey.<BR>\n"; + $vars->{'updated_sortkey'} = 1; + $vars->{'sortkey'} = $sortkey; } + if ($milestone ne $milestoneold) { unless ($milestone) { - print "Sorry, I can't delete the milestone text."; - PutTrailer($localtrailer); + $dbh->do('UNLOCK TABLES'); + ThrowUserError('milestone_blank_name'); exit; } - if (TestMilestone($product,$milestone)) { - print "Sorry, milestone '$milestone' is already in use."; - PutTrailer($localtrailer); + if (TestMilestone($product, $milestone)) { + $dbh->do('UNLOCK TABLES'); + ThrowUserError('milestone_already_exists', + {'name' => $milestone, + 'product' => $product}); exit; } - SendSQL("UPDATE bugs - SET target_milestone=" . SqlQuote($milestone) . ", - delta_ts=delta_ts - WHERE target_milestone=" . SqlQuote($milestoneold) . " - AND product_id=$product_id"); - SendSQL("UPDATE milestones - SET value=" . SqlQuote($milestone) . " - WHERE product_id=$product_id - AND value=" . SqlQuote($milestoneold)); - SendSQL("UPDATE products " . - "SET defaultmilestone = " . SqlQuote($milestone) . - " WHERE id = $product_id" . - " AND defaultmilestone = " . SqlQuote($milestoneold)); + + trick_taint($milestone); + trick_taint($milestoneold); + + $dbh->do('UPDATE bugs + SET target_milestone = ?, + delta_ts = delta_ts + WHERE target_milestone = ? + AND product_id = ?', + undef, + $milestone, + $milestoneold, + $product_id); + + $dbh->do("UPDATE milestones + SET value = ? + WHERE product_id = ? + AND value = ?", + undef, + $milestone, + $product_id, + $milestoneold); + + $dbh->do("UPDATE products + SET defaultmilestone = ? + WHERE id = ? + AND defaultmilestone = ?", + undef, + $milestone, + $product_id, + $milestoneold); + unlink "$datadir/versioncache"; - print "Updated milestone.<BR>\n"; + + $vars->{'updated_name'} = 1; } - PutTrailer($localtrailer); + $dbh->do('UNLOCK TABLES'); + + $vars->{'name'} = $milestone; + $vars->{'product'} = $product; + $template->process("admin/milestones/updated.html.tmpl", + $vars) + || ThrowTemplateError($template->error()); + exit; } - # # No valid action found # - -PutHeader("Error"); -print "I don't have a clue what you want.<BR>\n"; - +ThrowUserError('milestone_no_action'); diff --git a/t/008filter.t b/t/008filter.t index 5594a26e7..cc5c63f4b 100644 --- a/t/008filter.t +++ b/t/008filter.t @@ -202,7 +202,7 @@ sub directive_ok { return 1 if $directive =~ /^(time2str|GetBugLink|url)\(/; # Safe Template Toolkit virtual methods - return 1 if $directive =~ /\.((size)$|(push))/; + return 1 if $directive =~ /\.(length$|size$|push\()/; # Special Template Toolkit loop variable return 1 if $directive =~ /^loop\.(index|count)$/; diff --git a/template/en/default/admin/milestones/confirm-delete.html.tmpl b/template/en/default/admin/milestones/confirm-delete.html.tmpl new file mode 100644 index 000000000..893b849ae --- /dev/null +++ b/template/en/default/admin/milestones/confirm-delete.html.tmpl @@ -0,0 +1,135 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # name: string; The name of the milestone + # + # default_milestone: string; The default milestone for the product + # + # bug_count: number; The number of bugs targetted at the milestone + # + # product: string; The name of the product + # + #%] + +[% title = BLOCK %]Delete Milestone of Product '[% product FILTER html %]' + [% END %] + +[% PROCESS global/header.html.tmpl + title = title +%] + +<table border="1" cellpadding="4" cellspacing="0"> +<tr bgcolor="#6666FF"> + <th valign="top" align="left">Field</th> + <th valign="top" align="left">Value</th> +</tr> +<tr> + <td valign="top">Milestone:</td> + <td valign="top">[% name FILTER html %]</td> +</tr> +<tr> + <td valign="top">Milestone of Product:</td> + <td valign="top">[% product FILTER html %]</td> +</tr> +<tr> + <td valign="top">[% terms.Bugs %]:</td> + <td valign="top"> +[% IF bug_count %] + <a title="List of [% terms.bugs %] targetted at milestone ' + [% name FILTER html %]'" + href="buglist.cgi?target_milestone=[% name FILTER url_quote %]&product= + [%- product FILTER url_quote %]">[% bug_count %]</a> +[% ELSE %] + None +[% END %] + </td> +</tr> +</table> + +<h2>Confirmation</h2> + +[% IF bug_count %] + + [% IF !Param("allowbugdeletion") %] + + Sorry, there + + [% IF bug_count > 1 %] + are [% bug_count %] [%+ terms.bugs %] + [% ELSE %] + is [% bug_count %] [%+ terms.bug %] + [% END %] + + outstanding for this milestone. You must move + + [% IF bug_count > 1 %] + those [% terms.bugs %] + [% ELSE %] + that [% terms.bug %] + [% END %] + + to another milestone before you can delete this one. + + [% ELSE %] + + <table border="0" cellpadding="20" width="70%" bgcolor="red"><tr><td> + + There [% IF bug_count > 1 %] + are [% bug_count %] [%+ terms.bugs %] + [% ELSE %] + is 1 [% terms.bug %] + [% END %] + entered for this milestone! When you delete this + milestone, <b><blink>ALL</blink></b> stored [% terms.bugs %] will be deleted, + too. + You could not even see the [% terms.bug %] history for this milestone anymore! + </td></tr></table> + + [% END %] + +[% END %] + +[% IF default_milestone == name %] + + <p>Sorry, but '[% name FILTER html %]' is the default milestone for product ' + [%- product FILTER html %]', and so it can not be deleted. + + [% ELSE %] + + [% IF bug_count == 0 || Param('allowbugdeletion') %] + + <p>Do you really want to delete this milestone?<p> + + <form method="post" action="editmilestones.cgi"> + <input type="submit" value="Yes, delete"> + <input type="hidden" name="action" value="delete"> + <input type="hidden" name="product" value="[% product FILTER html %]"> + <input type="hidden" name="milestone" value="[% name FILTER html %]"> + </form> + + [% END %] + +[% END %] + +[% PROCESS admin/milestones/footer.html.tmpl %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/milestones/create.html.tmpl b/template/en/default/admin/milestones/create.html.tmpl new file mode 100644 index 000000000..ac707c195 --- /dev/null +++ b/template/en/default/admin/milestones/create.html.tmpl @@ -0,0 +1,59 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # product: string; name of the product the milestone is being created for + #%] + +[% title = BLOCK %]Add Milestone to Product '[% product FILTER html %]'[% END %] +[% h2 = BLOCK %]This page allows you to add a new milestone to product + '[% product FILTER html %]'.[% END %] +[% PROCESS global/header.html.tmpl + title = title + h2 = h2 +%] + +<form method="post" action="editmilestones.cgi"> + <table border="0" cellpadding="4" cellspacing="0"> + <tr> + <th align="right"><label for="milestone">Milestone:</label></th> + <td><input id="milestone" size="20" maxlength="20" name="milestone" + value=""></td> + </tr> + <tr> + <th align="right"><label for="sortkey">Sortkey:</label></th> + <td><input id="sortkey" size="20" maxlength="20" name="sortkey" + value=""></td> + </tr> + </table> + <input type="submit" value="Add"> + <input type="hidden" name="action" value="new"> + <input type="hidden" name='product' value="[% product FILTER html %]"> + +</form> + +<p> + +[% PROCESS admin/milestones/footer.html.tmpl + no_add_milestone_link = 1 + %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/milestones/created.html.tmpl b/template/en/default/admin/milestones/created.html.tmpl new file mode 100644 index 000000000..87e08cebe --- /dev/null +++ b/template/en/default/admin/milestones/created.html.tmpl @@ -0,0 +1,44 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # name: string; the name of the newly created milestone + # + # product: string; the name of the product the milestone belongs to + #%] + +[% title = BLOCK %]Adding new Milestone of Product + '[% product FILTER html %]'[% END %] +[% PROCESS global/header.html.tmpl + title = title +%] + +<p>The milestone '<a title="Edit milestone '[% name FILTER html %]' of + product '[% product FILTER html %]'" + href="editmilestones.cgi?action=edit&product= + [%- product FILTER url_quote %]&milestone=[% name FILTER url_quote %]"> + [%- name FILTER html %]</a>' has been created.</p> + +<p> + +[% PROCESS admin/milestones/footer.html.tmpl %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/milestones/deleted.html.tmpl b/template/en/default/admin/milestones/deleted.html.tmpl new file mode 100644 index 000000000..61abb38e5 --- /dev/null +++ b/template/en/default/admin/milestones/deleted.html.tmpl @@ -0,0 +1,69 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # name: string; the name of the deleted milestone. + # + # product: string; the name of the product the milestone belonged to + # + # deleted_bug_count: number; the number of bugs which were deleted + # (if bug deletion is allowed) + #%] + +[% title = BLOCK %]Deleted Milestone '[% name FILTER html %]' of Product + '[% product FILTER html %]'[% END %] +[% PROCESS global/header.html.tmpl + title = title +%] + +<p> +[% IF deleted_bug_count %] + Attachments, [% terms.bug %] activity and dependencies deleted for + [%+ deleted_bug_count %] + [% IF deleted_bug_count > 1 %] + [%+ terms.bugs %] + [% ELSE %] + [%+ terms.bug %] + [% END %]. + + </p><p> + [% deleted_bug_count %] + [% IF deleted_bug_count > 1 %] + [%+ terms.bugs %] + [% ELSE %] + [%+ terms.bug %] + [% END %] + deleted. + +[% ELSE %] + No [% terms.bugs %] were targetted at the milestone. +[% END %] +</p> + +<p>Milestone '[% name FILTER html %]' deleted.</p> + +<p> + +[% PROCESS admin/milestones/footer.html.tmpl + no_edit_milestone_link = 1 + %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/milestones/edit.html.tmpl b/template/en/default/admin/milestones/edit.html.tmpl new file mode 100644 index 000000000..6b5ec8fb0 --- /dev/null +++ b/template/en/default/admin/milestones/edit.html.tmpl @@ -0,0 +1,67 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # name: string; The name of the milestone + # + # sortkey: number; milestone sortkey + # + # product: string; The product the milestone belongs to + #%] + +[% PROCESS global/variables.none.tmpl %] + +[% title = BLOCK %]Edit Milestone '[% name FILTER html %]' of product ' + [%- product FILTER html %]'[% END %] +[% PROCESS global/header.html.tmpl + title = title +%] + +<form method="post" action="editmilestones.cgi"> + <table border="0" cellpadding="4" cellspacing="0"> + + <tr> + <th valign="top"><label for="milestone">Milestone:</label></th> + <td><input id="milestone" size="20" maxlength="20" name="milestone" value=" + [%- name FILTER html %]"></td> + </tr> + <tr> + <th align="right"><label for="sortkey">Sortkey:</label></th> + <td><input id="sortkey" size="20" maxlength="20" name="sortkey" value=" + [%- sortkey FILTER html %]"></td> + </tr> + + </table> + + <input type="hidden" name="milestoneold" value="[% name FILTER html %]"> + <input type="hidden" name="sortkeyold" value="[% sortkey FILTER html %]"> + <input type="hidden" name="action" value="update"> + <input type="hidden" name="product" value="[% product FILTER html %]"> + <input type="submit" value="Update"> + +</form> + +<p> + +[% PROCESS admin/milestones/footer.html.tmpl + no_edit_milestone_link = 1 %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/milestones/footer.html.tmpl b/template/en/default/admin/milestones/footer.html.tmpl new file mode 100644 index 000000000..8980d642e --- /dev/null +++ b/template/en/default/admin/milestones/footer.html.tmpl @@ -0,0 +1,66 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # name: string; the name of the milestone + # + # product: string; the name of the product which the milestone + # belongs/belonged to + # + # no_XXX_link: boolean; if defined, then don't show the corresponding + # link. Supported parameters are: + # + # no_edit_milestone_link + # no_edit_other_milestones_link + # no_add_milestone_link + #%] + +<p> + +<hr> + +[% UNLESS no_add_milestone_link %] + <a title="Add a milestone to product '[% product FILTER html %]'" + href="editmilestones.cgi?action=add&product= + [%- product FILTER url_quote %]">Add</a> a milestone. +[% END %] + +[% IF name && !no_edit_milestone_link %] + Edit milestone <a + title="Edit Milestone '[% name FILTER html %]' of product ' + [%- product FILTER html %]'" + href="editmilestones.cgi?action=edit&product= + [%- product FILTER url_quote %]&milestone=[% name FILTER url_quote %]"> + '[% name FILTER html %]'</a>. +[% END %] + +[% UNLESS no_edit_other_milestones_link %] + Edit other milestones of product <a + href="editmilestones.cgi?product= + [%- product FILTER url_quote %]">'[% product FILTER html %]'</a>. + +[% END %] + + Edit product <a + href="editproducts.cgi?action=edit&product= + [%- product FILTER url_quote %]">'[% product FILTER html %]'</a>. + +</p> diff --git a/template/en/default/admin/milestones/list.html.tmpl b/template/en/default/admin/milestones/list.html.tmpl new file mode 100644 index 000000000..160dcd6da --- /dev/null +++ b/template/en/default/admin/milestones/list.html.tmpl @@ -0,0 +1,77 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # milestones: array of hashes having the following properties: + # - name: string; The name of the milestone. + # - sortkey: number; The sortkey used to order the milestone. + # + # product: string; the name of the product we are editing milestones for + #%] + +[% USE Bugzilla %] +[% cgi = Bugzilla.cgi %] + +[% PROCESS global/variables.none.tmpl %] + +[% title = BLOCK %]Select milestone of product + '[% product FILTER html %]'[% END %] +[% PROCESS global/header.html.tmpl + title = title +%] + +[% edit_contentlink = BLOCK %]editmilestones.cgi?action=edit&product= + [%- product FILTER url_quote %]&milestone=%%name%%[% END %] +[% delete_contentlink = BLOCK %]editmilestones.cgi?action=del&product= + [%- product FILTER url_quote %]&milestone=%%name%%[% END %] + + +[% columns = [ + { + name => "name" + heading => "Edit milestone..." + contentlink => edit_contentlink + }, + { + name => "sortkey" + heading => "Sortkey" + }, + ] +%] + +[% columns.push({ + heading => "Action" + content => "Delete" + contentlink => delete_contentlink + }) %] + +[% PROCESS admin/table.html.tmpl + columns = columns + data = milestones +%] + +<p> + +[% PROCESS admin/milestones/footer.html.tmpl + no_edit_other_milestones_link = 1 + %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/milestones/select-product.html.tmpl b/template/en/default/admin/milestones/select-product.html.tmpl new file mode 100644 index 000000000..b1eee5ecf --- /dev/null +++ b/template/en/default/admin/milestones/select-product.html.tmpl @@ -0,0 +1,60 @@ +[%# 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): Gavin Shelley (bugzilla@chimpychompy.org) + # + #%] + +[%# INTERFACE: + # products: array of hashes having the following properties: + # - name: string; The name of the product. + # - description: string; The description of the product. + #%] + +[% USE Bugzilla %] +[% cgi = Bugzilla.cgi %] + +[% PROCESS global/variables.none.tmpl %] + +[% PROCESS global/header.html.tmpl + title = "Edit milestones for which product?" +%] + +[% bug_count_contentlink = BLOCK %]buglist.cgi?target_milestone=%%name%%&product= + [%- product FILTER url_quote %][% END %] + +[% columns = [ + { + name => "name" + heading => "Edit milestones of..." + contentlink => "editmilestones.cgi?product=%%name%%" + }, + { + name => "description" + heading => "Description" + allow_html_content => 1 + } + ] +%] + +[% PROCESS admin/table.html.tmpl + columns = columns + data = products +%] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/admin/milestones/updated.html.tmpl b/template/en/default/admin/milestones/updated.html.tmpl new file mode 100644 index 000000000..bfc09e210 --- /dev/null +++ b/template/en/default/admin/milestones/updated.html.tmpl @@ -0,0 +1,58 @@ +[%# 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): Gavin Shelley <bugzilla@chimpychompy.org> + #%] + +[%# INTERFACE: + # + # 'updated_XXX' variables are booleans, and are defined if the + # 'XXX' field was updated during the edit just being handled. + # Variables called just 'XXX' are strings, and are the _new_ contents + # of the fields. + # + # name & updated_name: the name of the milestone + # + # sortkey & updated_sortkey: the milestone sortkey + # + # product: string; the name of the product the milestone belongs to + #%] + +[% title = BLOCK %]Updating Milestone '[% name FILTER html %]' of Product + '[% product FILTER html %]'[% END %] +[% PROCESS global/header.html.tmpl + title = title +%] + +[% IF updated_name %] + <p>Updated Milestone name to: '[% name FILTER html %]'.</p> +[% END %] + +[% IF updated_sortkey %] + <p>Updated Milestone sortkey to: '[% sortkey FILTER html %]'.</p> +[% END %] + +[% UNLESS updated_sortkey || updated_name %] + <p>Nothing changed for milestone '[% name FILTER html %]'. +[% END %] + +<p> + +[% PROCESS admin/milestones/footer.html.tmpl %] + +[% PROCESS global/footer.html.tmpl %] diff --git a/template/en/default/filterexceptions.pl b/template/en/default/filterexceptions.pl index 9c8cd3b5b..8d03d1aae 100644 --- a/template/en/default/filterexceptions.pl +++ b/template/en/default/filterexceptions.pl @@ -30,7 +30,8 @@ # Values always used for numbers - [% (i|j|k|n|count) %] # Params - [% Param(... # Safe functions - [% (time2str|GetBugLink)... -# Safe vmethods - [% foo.size %] +# Safe vmethods - [% foo.size %] [% foo.length %] +# [% foo.push() %] # TT loop variables - [% loop.count %] # Already-filtered stuff - [% wibble FILTER html %] # where the filter is one of html|csv|js|url_quote|quoteUrls|time|uri|xml|none @@ -559,6 +560,14 @@ 'cgi.query_string' ], +'admin/milestones/confirm-delete.html.tmpl' => [ + 'bug_count' +], + +'admin/milestones/deleted.html.tmpl' => [ + 'deleted_bug_count' +], + 'account/login.html.tmpl' => [ 'target', ], diff --git a/template/en/default/global/user-error.html.tmpl b/template/en/default/global/user-error.html.tmpl index 7638806cf..27e9ec863 100644 --- a/template/en/default/global/user-error.html.tmpl +++ b/template/en/default/global/user-error.html.tmpl @@ -181,6 +181,11 @@ Sorry, you aren't a member of the 'editcomponents' group, and so you aren't allowed to add, modify or delete components. + [% ELSIF error == "auth_cant_edit_milestones" %] + [% title = "Access Denied" %] + Sorry, you aren't a member of the 'editcomponents' group, and so + you aren't allowed to add, modify or delete milestones. + [% ELSIF error == "component_already_exists" %] [% title = "Component Already Exists" %] A component with the name '[% name FILTER html %]' already exists. @@ -553,6 +558,33 @@ [% title = "Login Name Required" %] You must enter a login name when using your login as a pronoun. + [% ELSIF error == "milestone_already_exists" %] + [% title = "Milestone Already Exists" %] + The milestone '[% name FILTER html %]' already exists for product ' + [%- product FILTER html %]'. + + [% ELSIF error == "milestone_blank_name" %] + [% title = "Blank Milestone Name Not Allowed" %] + You must enter a name for this milestone. + + [% ELSIF error == "milestone_name_too_long" %] + [% title = "Milestone Name Is Too Long" %] + The name of a milestone is limited to 20 characters. + '[% name FILTER html %]' is too long ([% name.length %] characters). + + [% ELSIF error == "milestone_no_action" %] + [% title = "No valid action specified" %] + No valid action was specified when trying to edit milestones. + + [% ELSIF error == "milestone_not_specified" %] + [% title = "No Milestone Specified" %] + No milestone specified when trying to edit milestones. + + [% ELSIF error == "milestone_not_valid" %] + [% title = "Specified Milestone Does Not Exist" %] + The milestone '[% milestone FILTER html %]' for product + '[% product FILTER html %]' does not exist. + [% ELSIF error == "milestone_required" %] [% title = "Milestone Required" %] You must determine a target milestone for [% terms.bug %] @@ -560,6 +592,11 @@ if you are going to accept it. Part of accepting [% terms.abug %] is giving an estimate of when it will be fixed. + [% ELSIF error == "milestone_sortkey_invalid" %] + [% title = "Invalid Milestone Sortkey" %] + The sortkey '[% sortkey FILTER html %]' for milestone ' + [% name FILTER html %]' is not a valid (positive) number. + [% ELSIF error == "misarranged_dates" %] [% title = "Misarranged Dates" %] Your start date ([% datefrom FILTER html %]) is after |