From 29fe9ea7b41a7363d9a8c02598e371bc817f2d3c Mon Sep 17 00:00:00 2001 From: "gerv%gerv.net" <> Date: Thu, 28 Feb 2002 08:13:56 +0000 Subject: Bug 117060 - templatise userprefs.cgi. We also get a nice new set of tabs and a properly-tabulated email prefs section. --- userprefs.cgi | 696 +++++++++++++++++----------------------------------------- 1 file changed, 196 insertions(+), 500 deletions(-) (limited to 'userprefs.cgi') diff --git a/userprefs.cgi b/userprefs.cgi index eb823326a..d86282d08 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -18,6 +18,7 @@ # Alan Raetz # David Miller # Christopher Aillon +# Gervase Markham use diagnostics; use strict; @@ -28,7 +29,7 @@ require "CGI.pl"; use RelationSet; -# Shut up misguided -w warnings about "used only once". "use vars" just +# Shut up misguided -w warnings about "used only once". "use vars" just # doesn't work for me. sub sillyness { my $zz; @@ -36,618 +37,313 @@ sub sillyness { $zz = $::usergroupset; } +# Use global template variables. +use vars qw($template $vars); + my $userid; -my $showNewEmailTech; - -# Note the use of arrays instead of hashes: we want the items -# displayed in the same order as they appear in the array. - -my @emailGroups = ( - 'Owner', 'the Bug Owner', - 'Reporter', 'the Reporter', - 'QAcontact', 'the QA contact', - 'CClist', 'on the CC list', - 'Voter', 'a Voter' - ); - -my @emailFlags = ( - 'Removeme', 'When I\'m added to or removed from this capacity', - 'Comments', 'New Comments', - 'Attachments', 'New Attachments', - 'Status', 'Priority, status, severity, and milestone changes', - 'Resolved', 'When the bug is resolved or verified', - 'Keywords', 'Keywords field changes', - 'CC', 'CC field changes', - 'Other', 'Any field not mentioned above changes' - ); - -my $defaultEmailFlagString = - 'ExcludeSelf~' . 'on~' . - - 'emailOwnerRemoveme~' . 'on~' . - 'emailOwnerComments~' . 'on~' . - 'emailOwnerAttachments~' . 'on~' . - 'emailOwnerStatus~' . 'on~' . - 'emailOwnerResolved~' . 'on~' . - 'emailOwnerKeywords~' . 'on~' . - 'emailOwnerCC~' . 'on~' . - 'emailOwnerOther~' . 'on~' . - - 'emailReporterRemoveme~' . 'on~' . - 'emailReporterComments~' . 'on~' . - 'emailReporterAttachments~' . 'on~' . - 'emailReporterStatus~' . 'on~' . - 'emailReporterResolved~' . 'on~' . - 'emailReporterKeywords~' . 'on~' . - 'emailReporterCC~' . 'on~' . - 'emailReporterOther~' . 'on~' . - - 'emailQAcontactRemoveme~' . 'on~' . - 'emailQAcontactComments~' . 'on~' . - 'emailQAcontactAttachments~' . 'on~' . - 'emailQAcontactStatus~' . 'on~' . - 'emailQAcontactResolved~' . 'on~' . - 'emailQAcontactKeywords~' . 'on~' . - 'emailQAcontactCC~' . 'on~' . - 'emailQAcontactOther~' . 'on~' . - - 'emailCClistRemoveme~' . 'on~' . - 'emailCClistComments~' . 'on~' . - 'emailCClistAttachments~' . 'on~' . - 'emailCClistStatus~' . 'on~' . - 'emailCClistResolved~' . 'on~' . - 'emailCClistKeywords~' . 'on~' . - 'emailCClistCC~' . 'on~' . - 'emailCClistOther~' . 'on~' . - - 'emailVoterRemoveme~' . 'on~' . - 'emailVoterComments~' . 'on~' . - 'emailVoterAttachments~' . 'on~' . - 'emailVoterStatus~' . 'on~' . - 'emailVoterResolved~' . 'on~' . - 'emailVoterKeywords~' . 'on~' . - 'emailVoterCC~' . 'on~' . - 'emailVoterOther~' . 'on' ; - -sub EmitEntry { - my ($description, $entry) = (@_); - print qq{$description:$entry\n}; -} +# The default email flags leave all email on. +my $defaultflagstring = "ExcludeSelf~on~"; -sub Error { - my ($msg) = (@_); - print qq{ -$msg -

-Please hit back and try again. -}; - PutFooter(); - exit(); +foreach my $role ("Owner", "Reporter", "QAcontact", "CClist", "Voter") { + foreach my $reason ("Removeme", "Comments", "Attachments", "Status", + "Resolved", "Keywords", "CC", "Other") + { + $defaultflagstring .= "email$role$reason~on~"; + } } +# Remove final "~". +chop $defaultflagstring; -sub ShowAccount { +############################################################################### +# Each panel has two functions - panel Foo has a DoFoo, to get the data +# necessary for displaying the panel, and a SaveFoo, to save the panel's +# contents from the form data (if appropriate.) +# SaveFoo may be called before DoFoo. +############################################################################### +sub DoAccount { SendSQL("SELECT realname FROM profiles WHERE userid = $userid"); - my ($realname) = (FetchSQLData()); - - $realname = value_quote($realname); - - EmitEntry("Old password", - qq|| . - qq||); - EmitEntry("New password", - qq{}); - EmitEntry("Re-enter new password", - qq{}); - EmitEntry("Your real name (optional)", - qq{}); + $vars->{'realname'} = FetchSQLData(); } sub SaveAccount { - if ($::FORM{'Bugzilla_password'} ne "" - || $::FORM{'pwd1'} ne "" || $::FORM{'pwd2'} ne "") { + if ($::FORM{'Bugzilla_password'} ne "" || + $::FORM{'new_password1'} ne "" || + $::FORM{'new_password2'} ne "") + { my $old = SqlQuote($::FORM{'Bugzilla_password'}); - my $pwd1 = SqlQuote($::FORM{'pwd1'}); - my $pwd2 = SqlQuote($::FORM{'pwd2'}); + my $pwd1 = SqlQuote($::FORM{'new_password1'}); + my $pwd2 = SqlQuote($::FORM{'new_password2'}); SendSQL("SELECT cryptpassword FROM profiles WHERE userid = $userid"); my $oldcryptedpwd = FetchOneColumn(); - if ( !$oldcryptedpwd ) { - Error("I was unable to retrieve your old password from the database."); + if (!$oldcryptedpwd) { + DisplayError("I was unable to retrieve your old password from the database."); + exit; } - if ( crypt($::FORM{'Bugzilla_password'}, $oldcryptedpwd) ne $oldcryptedpwd ) { - Error("You did not enter your old password correctly."); + if (crypt($::FORM{'Bugzilla_password'}, $oldcryptedpwd) ne + $oldcryptedpwd) + { + DisplayError("You did not enter your old password correctly."); + exit; } if ($pwd1 ne $pwd2) { - Error("The two passwords you entered did not match."); + DisplayError("The two passwords you entered did not match."); + exit; } - if ($::FORM{'pwd1'} eq '') { - Error("You must enter a new password."); + if ($::FORM{'new_password1'} eq '') { + DisplayError("You must enter a new password."); + exit; } - my $passworderror = ValidatePassword($::FORM{'pwd1'}); - Error($passworderror) if $passworderror; - - my $cryptedpassword = SqlQuote(Crypt($::FORM{'pwd1'})); - SendSQL("UPDATE profiles - SET cryptpassword = $cryptedpassword - WHERE userid = $userid"); + my $passworderror = ValidatePassword($::FORM{'new_password1'}); + (DisplayError($passworderror) && exit) if $passworderror; + + my $cryptedpassword = SqlQuote(Crypt($::FORM{'new_password1'})); + SendSQL("UPDATE profiles + SET cryptpassword = $cryptedpassword + WHERE userid = $userid"); # Invalidate all logins except for the current one InvalidateLogins($userid, $::COOKIE{"Bugzilla_logincookie"}); } + SendSQL("UPDATE profiles SET " . "realname = " . SqlQuote(trim($::FORM{'realname'})) . " WHERE userid = $userid"); } -# -# Set email flags in database based on the parameter string. -# -sub setEmailFlags ($) { - - my $emailFlagString = $_[0]; - - SendSQL("UPDATE profiles SET emailflags = " . - SqlQuote($emailFlagString) . " WHERE userid = $userid"); -} - - -sub ShowEmailOptions () { +sub DoEmail { if (Param("supportwatchers")) { my $watcheduserSet = new RelationSet; $watcheduserSet->mergeFromDB("SELECT watched FROM watch WHERE" . " watcher=$userid"); - my $watchedusers = $watcheduserSet->toString(); - - print qq{ -


- -If you want to help cover for someone when they're on vacation, or if -you need to do the QA related to all of their bugs, you can tell bugzilla -to send mail related to their bugs to you also. List the email addresses -of any users you wish to watch here, separated by commas. -}; - - EmitEntry("Users to watch", - qq{}); + $vars->{'watchedusers'} = $watcheduserSet->toString(); } - print qq{
}; - - showAdvancedEmailFilterOptions(); - -print qq { - -
}; - -} - -sub showAdvancedEmailFilterOptions () { - - my $flags; - my $notify; - my %userEmailFlags = (); - - print qq{ -
- Advanced Email Filtering Options -
-
-

-

- If you don't like getting a notification for "trivial" - changes to bugs, you can use the settings below to - filter some (or even all) notifications. -
-
- }; - SendSQL("SELECT emailflags FROM profiles WHERE userid = $userid"); - ($flags) = FetchSQLData(); + my ($flagstring) = FetchSQLData(); - # if the emailflags haven't been set before, that means that this user - # hasn't been to (the email pane of?) userprefs.cgi since the change to - # use emailflags. create a default flagset for them, based on + # If the emailflags haven't been set before, that means that this user + # hasn't been to the email pane of userprefs.cgi since the change to + # use emailflags. Create a default flagset for them, based on # static defaults. - # - if ( !$flags ) { - $flags = $defaultEmailFlagString; - setEmailFlags($flags); + if (!$flagstring) { + $flagstring = $defaultflagstring; + SendSQL("UPDATE profiles SET emailflags = " . + SqlQuote($flagstring) . " WHERE userid = $userid"); } - # the 255 param is here, because without a third param, split will - # trim any trailing null fields, which causes perl to eject lots of - # warnings. any suitably large number would do. - # - %userEmailFlags = split(/~/ , $flags, 255); - - showExcludeSelf(\%userEmailFlags); - - # print STDERR "$flags\n"; - - print qq{ -
-       - Field/recipient specific options:

- }; - - - my @tmpGroups = @emailGroups; - while ((my $groupName,my $groupText) = splice(@tmpGroups,0,2) ) { - printEmailPrefGroup($groupName,$groupText,\%userEmailFlags); - } - -} - -sub showExcludeSelf (\%) { + # The 255 param is here, because without a third param, split will + # trim any trailing null fields, which causes Perl to eject lots of + # warnings. Any suitably large number would do. + my %emailflags = split(/~/, $flagstring, 255); - my %CurrentFlags = %{$_[0]}; - - my $excludeSelf = " "; + $vars->{'excludeself'} = 0; - while ( my ($key,$value) = each (%CurrentFlags) ) { - - # print qq{flag name: $key value: $value
}; - - if ( $key eq 'ExcludeSelf' ) { - - if ( $value eq 'on' ) { - - $excludeSelf = "CHECKED"; - } - } + # Parse the info into a hash of hashes; the first hash keyed by role, + # the second by reason, and the value being 1 or 0 (undef). + foreach my $key (keys %emailflags) { + # ExcludeSelf is special. + if ($key eq 'ExcludeSelf' && $emailflags{$key} eq 'on') { + $vars->{'excludeself'} = 1; + next; } - print qq { - - - -
      - Global options:
- Only email me reports of changes made by other people - -
-
- }; - -} - -sub printEmailPrefGroup ($$\%) { - - my ($groupName,$textName,$refCurrentFlags) = @_[0,1,2]; - my @tmpFlags = @emailFlags; - - print qq { }; - print qq { }; - - while ((my $flagName,my $flagText) = splice(@tmpFlags,0,2)) { - - printEmailOption($groupName . $flagName, $flagText, $refCurrentFlags); - } - print qq {
      - When I\'m $textName, email me:
}; - print qq {
}; -} - -sub printEmailOption ($$\%) { - - my $value= ''; - - my ($optionName,$description,$refCurrentFlags) = @_[0,1,2]; - - #print qq{ email$optionName: $$refCurrentFlags{"email$optionName"}
}; - - # if the db value is 'on', then mark that checkbox - if ($$refCurrentFlags{"email$optionName"} eq 'on'){ - $value = 'CHECKED'; - } - - # **** Kludge ... also mark on if the value in $$refCurrentFlags in undef - if (!defined($$refCurrentFlags{"email$optionName"})) { - $value = 'CHECKED'; + # All other keys match this regexp. + $key =~ /email([A-Z]+[a-z]+)([A-Z]+[a-z]*)/; + + # Create a new hash if we don't have one... + if (!defined($vars->{$1})) { + $vars->{$1} = {}; + } + + if ($emailflags{$key} eq "on") { + $vars->{$1}{$2} = 1; + } } - - print qq{ - - - $description - - }; } -sub SaveEmailOptions () { - - # I don't understand: global variables and %FORM variables are - # not preserved between ShowEmailOptions() and SaveEmailOptions() - # The form value here is from a hidden variable just before the SUBMIT. - - my $useNewEmailTech = $::FORM{'savedEmailTech'}; - my $updateString; - - if ( defined $::FORM{'ExcludeSelf'}) { +# Note: we no longer store "off" values in the database. +sub SaveEmail { + my $updateString = ""; + + if (defined $::FORM{'ExcludeSelf'}) { $updateString .= 'ExcludeSelf~on'; } else { $updateString .= 'ExcludeSelf~'; } - my @tmpGroups = @emailGroups; - - while ((my $groupName,my $groupText) = splice(@tmpGroups,0,2) ) { - - my @tmpFlags = @emailFlags; - - while ((my $flagName,my $flagText) = splice(@tmpFlags,0,2) ) { - - my $entry = 'email' . $groupName . $flagName; - my $entryValue; - - if (!defined $::FORM{$entry} ) { - $entryValue = ""; - } else { - $entryValue = $::FORM{$entry}; - } - - $updateString .= '~' . $entry . '~' . $entryValue; + + foreach my $key (keys %::FORM) { + if ($key =~ /email([A-Z]+[a-z]+)([A-Z]+[a-z]*)/) { + $updateString .= "~$key~on"; } } - - #open(FID,">updateString"); - #print qq{UPDATE STRING: $updateString
}; - #close(FID); - - SendSQL("UPDATE profiles SET emailflags = " . - SqlQuote($updateString) . " WHERE userid = $userid"); - - if (Param("supportwatchers") ) { - - if (exists $::FORM{'watchedusers'}) { - - # Just in case. Note that this much locking is actually overkill: - # we don't really care if anyone reads the watch table. So - # some small amount of contention could be gotten rid of by - # using user-defined locks rather than table locking. - # - SendSQL("LOCK TABLES watch WRITE, profiles READ"); - - # what the db looks like now - # - my $origWatchedUsers = new RelationSet; - $origWatchedUsers->mergeFromDB("SELECT watched FROM watch WHERE" . - " watcher=$userid"); - - # update the database to look like the form - # - my $newWatchedUsers = new RelationSet($::FORM{'watchedusers'}); - my @CCDELTAS = $origWatchedUsers->generateSqlDeltas( - $newWatchedUsers, - "watch", - "watcher", - $userid, - "watched"); - $CCDELTAS[0] eq "" || SendSQL($CCDELTAS[0]); - $CCDELTAS[1] eq "" || SendSQL($CCDELTAS[1]); - - # all done - # - SendSQL("UNLOCK TABLES"); - - } + + SendSQL("UPDATE profiles SET emailflags = " . SqlQuote($updateString) . + " WHERE userid = $userid"); + + if (Param("supportwatchers") && exists $::FORM{'watchedusers'}) { + # Just in case. Note that this much locking is actually overkill: + # we don't really care if anyone reads the watch table. So + # some small amount of contention could be gotten rid of by + # using user-defined locks rather than table locking. + SendSQL("LOCK TABLES watch WRITE, profiles READ"); + + # what the db looks like now + my $origWatchedUsers = new RelationSet; + $origWatchedUsers->mergeFromDB("SELECT watched FROM watch WHERE" . + " watcher=$userid"); + + # Update the database to look like the form + my $newWatchedUsers = new RelationSet($::FORM{'watchedusers'}); + my @CCDELTAS = $origWatchedUsers->generateSqlDeltas( + $newWatchedUsers, + "watch", + "watcher", + $userid, + "watched"); + ($CCDELTAS[0] eq "") || SendSQL($CCDELTAS[0]); + ($CCDELTAS[1] eq "") || SendSQL($CCDELTAS[1]); + + SendSQL("UNLOCK TABLES"); } } - -sub ShowFooter { +sub DoFooter { SendSQL("SELECT mybugslink FROM profiles " . "WHERE userid = $userid"); - my ($mybugslink) = (FetchSQLData()); - my $entry = - BuildPulldown("mybugslink", - [["1", "should appear"], - ["0", "should not be displayed"]], - $mybugslink); - EmitEntry("The 'My bugs' link at the footer of each page", $entry); + $vars->{'mybugslink'} = FetchSQLData(); + SendSQL("SELECT name, linkinfooter FROM namedqueries " . "WHERE userid = $userid"); - my $count = 0; + + my @queries; while (MoreSQLData()) { - my ($name, $linkinfooter) = (FetchSQLData()); - if ($name eq $::defaultqueryname) { - next; - } - my $entry = - BuildPulldown("query-$count", - [["0", "should only appear in the query page"], - ["1", "should appear on the footer of every page"]], - $linkinfooter); - EmitEntry("Your query named '$name'", $entry); - my $q = value_quote($name); - print qq{\n}; - $count++; - } - print qq{\n}; - if (!$count) { - print qq{ - -If you go create remembered queries in the query page, -you can then come to this page and choose to have some of them appear in the -footer of each Bugzilla page. -}; + my ($name, $footer) = (FetchSQLData()); + next if ($name eq $::defaultqueryname); + + push (@queries, { name => $name, footer => $footer }); } + + $vars->{'queries'} = \@queries; } - sub SaveFooter { my %old; SendSQL("SELECT name, linkinfooter FROM namedqueries " . "WHERE userid = $userid"); while (MoreSQLData()) { - my ($name, $linkinfooter) = (FetchSQLData()); - $old{$name} = $linkinfooter; + my ($name, $footer) = (FetchSQLData()); + $old{$name} = $footer; } - for (my $c=0 ; $c<$::FORM{'numqueries'} ; $c++) { + for (my $c = 0; $c < $::FORM{'numqueries'}; $c++) { my $name = $::FORM{"name-$c"}; if (exists $old{$name}) { my $new = $::FORM{"query-$c"}; if ($new ne $old{$name}) { + detaint_natural($new); SendSQL("UPDATE namedqueries SET linkinfooter = $new " . "WHERE userid = $userid " . "AND name = " . SqlQuote($name)); } } else { - Error("Hmm, the $name query seems to have gone away."); + DisplayError("Hmm, the $name query seems to have gone away."); } } - SendSQL("UPDATE profiles SET mybugslink = " . SqlQuote($::FORM{'mybugslink'}) . - " WHERE userid = $userid"); + SendSQL("UPDATE profiles SET mybugslink = " . + SqlQuote($::FORM{'mybugslink'}) . " WHERE userid = $userid"); } - - -sub ShowPermissions { - print "You have the following permission bits set on your account:\n"; - print "

    \n"; - my $found = 0; + +sub DoPermissions { + my (@has_bits, @set_bits); + SendSQL("SELECT description FROM groups " . "WHERE bit & $::usergroupset != 0 " . "ORDER BY bit"); while (MoreSQLData()) { - my ($description) = (FetchSQLData()); - print "
  • $description\n"; - $found = 1; - } - if ($found == 0) { - print "
  • (No extra permission bits have been set).\n"; + push(@has_bits, FetchSQLData()); } - print "
\n"; + SendSQL("SELECT blessgroupset FROM profiles WHERE userid = $userid"); my $blessgroupset = FetchOneColumn(); if ($blessgroupset) { - print "And you can turn on or off the following bits for\n"; - print qq{other users:\n}; - print "

    \n"; SendSQL("SELECT description FROM groups " . "WHERE bit & $blessgroupset != 0 " . "ORDER BY bit"); while (MoreSQLData()) { - my ($description) = (FetchSQLData()); - print "
  • $description\n"; + push(@set_bits, FetchSQLData()); } - print "
\n"; } - print "\n"; + + $vars->{'has_bits'} = \@has_bits; + $vars->{'set_bits'} = \@set_bits; } - - - - -###################################################################### -################# Live code (not sub defs) starts here ############### +# No SavePermissions() because this panel has no changeable fields. +############################################################################### +# Live code (not subroutine definitions) starts here +############################################################################### confirm_login(); -print "Content-type: text/html\n\n"; - GetVersionTable(); -PutHeader("User Preferences", "User Preferences", $::COOKIE{'Bugzilla_login'}); - -# foreach my $k (sort(keys(%::FORM))) { -# print "
" . value_quote($k) . ": " . value_quote($::FORM{$k}) . "\n
"; -# } - -my $bank = $::FORM{'bank'} || "account"; - -my @banklist = ( - ["account", "Account settings", - \&ShowAccount, \&SaveAccount], - ["diffs", "Email settings", - \&ShowEmailOptions, \&SaveEmailOptions], - ["footer", "Page footer", - \&ShowFooter, \&SaveFooter], - ["permissions", "Permissions", - \&ShowPermissions, undef] - ); +$userid = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'}); +$vars->{'login'} = $::COOKIE{'Bugzilla_login'}; +$vars->{'changes_saved'} = $::FORM{'dosave'}; -my $numbanks = @banklist; -my $numcols = $numbanks + 2; +my $current_tab_name = $::FORM{'tab'} || "account"; -my $headcol = '"lightblue"'; +my @tabs = ( { name => "account", description => "Account settings", + saveable => "1" }, + { name => "email", description => "Email settings", + saveable => "1" }, + { name => "footer", description => "Page footer", + saveable => "1" }, + { name => "permissions", description => "Permissions", + saveable => "0" } ); -print qq{ -
- - - - - -}; - - -my $bankdescription; -my $showfunc; -my $savefunc; - -foreach my $i (@banklist) { - my ($name, $description) = (@$i); - my $color = ""; - if ($name eq $bank) { - print qq{}; - my $zz; - ($zz, $bankdescription, $showfunc, $savefunc) = (@$i); - } else { - print qq{}; +# Work out the current tab +foreach my $tab (@tabs) { + if ($tab->{'name'} eq $current_tab_name) { + $vars->{'current_tab'} = $tab; + last; } } -print qq{ - -
User preferences
 $description$description 
-
-

-}; - - - -if (defined $bankdescription) { - $userid = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'}); +$vars->{'tabs'} = \@tabs; - if ($::FORM{'dosave'}) { - &$savefunc; - print "Your changes have been saved."; - } - print qq{

$bankdescription

}; - - # execute subroutine from @banklist based on bank selected. - &$showfunc; - - print qq{
}; - print qq{}; - } else { - print qq{0">}; - } - print qq{ }; - - if ($savefunc) { - print qq{
- -
}; - } - print qq{
\n}; -} else { - print "

Please choose from the above links which settings you wish to change.

"; +# Do any saving, and then display the current tab. +SWITCH: for ($current_tab_name) { + /^account$/ && do { + SaveAccount() if $::FORM{'dosave'}; + DoAccount(); + last SWITCH; + }; + /^email$/ && do { + SaveEmail() if $::FORM{'dosave'}; + DoEmail(); + last SWITCH; + }; + /^footer$/ && do { + SaveFooter() if $::FORM{'dosave'}; + DoFooter(); + last SWITCH; + }; + /^permissions$/ && do { + DoPermissions(); + last SWITCH; + }; } +# Generate and return the UI (HTML page) from the appropriate template. +print "Content-type: text/html\n\n"; +$template->process("prefs/userprefs.tmpl", $vars) + || DisplayError("Template process failed: " . $template->error()) + && exit; -print "

"; - - -PutFooter(); -- cgit v1.2.3-24-g4f1b