diff options
-rw-r--r-- | template/default/prefs/account.tmpl | 56 | ||||
-rw-r--r-- | template/default/prefs/email.tmpl | 156 | ||||
-rw-r--r-- | template/default/prefs/footer.tmpl | 75 | ||||
-rw-r--r-- | template/default/prefs/permissions.tmpl | 56 | ||||
-rw-r--r-- | template/default/prefs/userprefs.tmpl | 105 | ||||
-rwxr-xr-x | userprefs.cgi | 696 |
6 files changed, 644 insertions, 500 deletions
diff --git a/template/default/prefs/account.tmpl b/template/default/prefs/account.tmpl new file mode 100644 index 000000000..486aba082 --- /dev/null +++ b/template/default/prefs/account.tmpl @@ -0,0 +1,56 @@ +[%# 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): Gervase Markham <gerv@gerv.net> + #%] + +[%# INTERFACE: + # realname: string. The user's real name, if any. + # login: string. The user's Bugzilla login email address. + #%] + +<table> + <tr> + <th align="right">Old password:</th> + <td> + <input type="hidden" name="Bugzilla_login" + value="[% login FILTER html %]" /> + <input type="password" name="Bugzilla_password" /> + </td> + </tr> + + <tr> + <th align="right">New password:</th> + <td> + <input type="password" name="new_password1" /> + </td> + </tr> + + <tr> + <th align="right">Re-enter new password:</th> + <td> + <input type="password" name="new_password2" /> + </td> + </tr> + + <tr> + <th align="right">Your real name (optional, but encouraged):</th> + <td> + <input size="35" name="realname" value="[% realname FILTER html %]" /> + </td> + </tr> +</table> diff --git a/template/default/prefs/email.tmpl b/template/default/prefs/email.tmpl new file mode 100644 index 000000000..2210f2716 --- /dev/null +++ b/template/default/prefs/email.tmpl @@ -0,0 +1,156 @@ +[%# 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): Gervase Markham <gerv@gerv.net> + #%] + +[%# INTERFACE: + # watchedusers: string. + # Comma-separated list of email addresses this user watches. + # excludeself: boolean. + # True if user is not receiving self-generated mail. + # <rolename>: Multiple hashes, one for each rolename (e.g. owner; see + # below), keyed by reasonname (e.g. comments; again, see + # below). The value is a boolean - true if the user is + # receiving mail for that reason when in that role. + # Also references the 'supportwatchers' Param. + #%] + +<table> + [% IF Param('supportwatchers') %] + <tr> + <td colspan="4"> + <hr /> + </td> + </tr> + + <tr> + <td colspan="4"> + 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. + </td> + </tr> + + <tr> + <th align="right">Users to watch:</th> + <td> + <input size="35" name="watchedusers" value="[% watchedusers %]" /> + </td> + </tr> + [% END %] + + <tr> + <td colspan="2"> + <p> + 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. + </p> + </td> + </tr> +</table> + +<hr /> + +<table> + <tr> + <td colspan="2"> + <b>Global options:</b> + </td> + </tr> + + <tr> + <td width="150"></td> + <td> + Only email me reports of changes made by other people + <input type="checkbox" name="ExcludeSelf" value="on" + [% " checked" IF excludeself %]> + <br /> + </td> + </tr> +</table> + +<hr /> +<b>Field/recipient specific options:</b> +<br /> +<br /> + + +<table width="100%" border="1"> + <tr> + <td colspan="5" align="center" width="50%"> + <b>When my relationship to this bug is:</b> + </td> + <td rowspan="2" width="50%"> + <b>I want to receive mail when:</b> + </td> + </tr> + + <tr> + <td align="center" width="10%"> + <b>Reporter</b> + </td> + <td align="center" width="10%"> + <b>Assignee</b> + </td> + <td align="center" width="10%"> + <b>QA Contact</b> + </td> + <td align="center" width="10%"> + <b>CC</b> + </td> + <td align="center" width="10%"> + <b>Voter</b> + </td> + </tr> + + [% FOREACH reason = [ + { name = 'Removeme', + description = 'I\'m added to or removed from this capacity' }, + { name = 'Comments', + description = 'New Comments are added' }, + { name = 'Attachments', + description = 'New Attachments are added' }, + { name = 'Status', + description = 'Priority, status, severity, and/or milestone changes' }, + { name = 'Resolved', + description = 'The bug is resolved or verified' }, + { name = 'Keywords', + description = 'Keywords field changes' }, + { name = 'CC', + description = 'CC field changes' }, + { name = 'Other', + description = 'Any field not mentioned above changes' } ] %] + <tr> + [% FOREACH role = [ "Owner", "Reporter", "QAcontact", "CClist", "Voter" ] + %] + <td align="center"> + <input type="checkbox" name="email[% role %][% reason.name %]" value="on" + [% " checked" IF $role.${reason.name} %]> + </td> + [% END %] + <td> + [% reason.description %] + </td> + </tr> + [% END %] +</table> + +<br /> diff --git a/template/default/prefs/footer.tmpl b/template/default/prefs/footer.tmpl new file mode 100644 index 000000000..7b0756dbf --- /dev/null +++ b/template/default/prefs/footer.tmpl @@ -0,0 +1,75 @@ +[%# 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): Gervase Markham <gerv@gerv.net> + #%] + +[%# INTERFACE: + # mybugslink: boolean. True if the user wishes the My Bugs link to appear. + # queries: array of hashes. May be empty. Each hash has two members: + # name: string. The name of the query. + # footer: boolean. True if the query appears in the footer. + #%] + +<table> + <tr> + <th align="right">The 'My bugs' link:</th> + <td> + <select name="mybugslink"> + <option value="1">should appear on the footer of every page</option> + <option value="0" + [% " selected" IF NOT mybugslink %]>should not be displayed + </option> + </select> + </td> + </tr> + + <input type="hidden" name="numqueries" value="[% queries.size %]" /> + [% IF queries.size %] + [% FOREACH query = queries %] + <tr> + <th align="right">Your query named '[% query.name FILTER html %]':</th> + <td> + <select name="query-[% loop.index %]"> + <option value="0">should only appear in the query page</option> + <option value="1" + [% " selected" IF query.footer %]> + should appear on the footer of every page + </option> + </select> + </td> + </tr> + + <input type="hidden" name="name-[% loop.index %]" + value="[% query.name FILTER html %]" /> + [% END %] + + [% ELSE %] + <tr> + <td colspan="4"> + <br /> + If you create remembered queries using the + <a href="query.cgi">query page</a>, + you can then come to this page and choose to have some of them + appear in the footer of each Bugzilla page. + <br /> + <br /> + </td> + </tr> + [% END %] + +</table> diff --git a/template/default/prefs/permissions.tmpl b/template/default/prefs/permissions.tmpl new file mode 100644 index 000000000..56429a78d --- /dev/null +++ b/template/default/prefs/permissions.tmpl @@ -0,0 +1,56 @@ +[%# 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): Gervase Markham <gerv@gerv.net> + #%] + +[%# INTERFACE: + # has_bits: array of strings. May be empty. + # Descriptions of the permission bits the user has. + # set_bits: array of strings. May be empty. + # Descriptions of the permission bits the user can set for + # other people. + #%] + +<table> + <tr> + <td> + [% IF has_bits.size %] + You have the following permission bits set on your account: + <ul> + [% FOREACH bit_description = has_bits %] + <li>[% bit_description %]</li> + [% END %] + </ul> + [% ELSE %] + There are no permission bits set on your account. + [% END %] + + [% IF set_bits.size %] + And you can turn on or off the following bits for + <a HREF="editusers.cgi">other users</a>: + <p> + <ul> + [% FOREACH bit_description = set_bits %] + <li>[% bit_description %]</li> + [% END %] + </ul> + </p> + [% END %] + </td> + </tr> +</table> diff --git a/template/default/prefs/userprefs.tmpl b/template/default/prefs/userprefs.tmpl new file mode 100644 index 000000000..ba14521df --- /dev/null +++ b/template/default/prefs/userprefs.tmpl @@ -0,0 +1,105 @@ +[%# 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): Gervase Markham <gerv@gerv.net> + #%] + +[%# INTERFACE: + # login: string. The user's Bugzilla login email address. + # tabs: List of hashes. May not be empty. Each hash has three members: + # name: string. Name of the tab (used internally.) + # description: string. Description of the tab (used in tab title.) + # saveable: boolean. True if tab has a form which can be submitted. + # True if user is not receiving self-generated mail. + # Note: For each tab name, a template "prefs/${tab.name}.tmpl" must exist, + # and its interface must be fulfilled. + # current_tab: A direct reference to one of the hashes in the tabs list. + # This tab will be displayed. + # changes_saved: boolean. True if the CGI processed form data before + # displaying anything. + #%] + +[% INCLUDE global/header + title = "User Preferences" + h2 = login + style = "td.selected_tab { + border-width: 2px 2px 0px; + border-style: solid; + } + td.unselected_tab, td.spacer { + border-width: 0px 0px 2px 0px; + border-style: solid; + }" + %] + +<center> + <table cellspacing="0" cellpadding="10" border="0" width="100%"> + <tr> + <td class="spacer"> </td> + + [% FOREACH tab = tabs %] + [% IF tab.name == current_tab.name %] + <td align="center" bgcolor="lightblue" class="selected_tab"> + [% tab.description %] + </td> + [% ELSE %] + <td align="center" bgcolor="#BBBBEE" class="unselected_tab"> + <a HREF="userprefs.cgi?tab=[% tab.name %]">[% tab.description %]</a> + </td> + [% END %] + [% END %] + + <td class="spacer"> </td> + </tr> + </table> +</center> + +[% IF changes_saved %] + <p> + <font color="red"> + The changes to your + [% current_tab.description FILTER lower %] have been saved. + </font> + </p> +[% END %] + +<h3>[% current_tab.description %]</h3> + +<form method="post"> + <input type="hidden" name="tab" value="[% current_tab.name %]"> + + [% INCLUDE "prefs/${current_tab.name}.tmpl" %] + + [% IF current_tab.saveable %] + <input type="hidden" name="dosave" value="1"> + + <table> + <tr> + <td width="150"></td> + <td> + <input type="submit" value="Submit Changes"> + </td> + </tr> + </table> + [% END %] + +</form> + +[% INCLUDE global/footer %] + + + diff --git a/userprefs.cgi b/userprefs.cgi index eb823326a..d86282d08 100755 --- a/userprefs.cgi +++ b/userprefs.cgi @@ -18,6 +18,7 @@ # Alan Raetz <al_raetz@yahoo.com> # David Miller <justdave@syndicomm.com> # Christopher Aillon <christopher@aillon.com> +# Gervase Markham <gerv@gerv.net> 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{<TR><TH ALIGN="right">$description:</TH><TD>$entry</TD></TR>\n}; -} +# The default email flags leave all email on. +my $defaultflagstring = "ExcludeSelf~on~"; -sub Error { - my ($msg) = (@_); - print qq{ -$msg -<P> -Please hit <B>back</B> 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|<input type=hidden name="Bugzilla_login" value="$::COOKIE{Bugzilla_login}">| . - qq|<input type=password name="Bugzilla_password">|); - EmitEntry("New password", - qq{<input type=password name="pwd1">}); - EmitEntry("Re-enter new password", - qq{<input type=password name="pwd2">}); - EmitEntry("Your real name (optional)", - qq{<INPUT SIZE=35 NAME="realname" VALUE="$realname">}); + $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{ -<TR><TD COLSPAN="4"><HR></TD></TR> -<TR><TD COLSPAN="4"> -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. -</TD></TR>}; - - EmitEntry("Users to watch", - qq{<INPUT SIZE=35 NAME="watchedusers" VALUE="$watchedusers">}); + $vars->{'watchedusers'} = $watcheduserSet->toString(); } - print qq{<TR><TD COLSPAN="2"><HR></TD></TR>}; - - showAdvancedEmailFilterOptions(); - -print qq { -<TABLE CELLSPACING="0" CELLPADDING="10" BORDER=0 WIDTH="100%"> -<TR><TD>}; - -} - -sub showAdvancedEmailFilterOptions () { - - my $flags; - my $notify; - my %userEmailFlags = (); - - print qq{ - <TR><TD COLSPAN="2"><center> - <font size=+1>Advanced Email Filtering Options</font> - </center> - </TD></TR><tr><td colspan="2"> - <p> - <center> - 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. - </center></td></tr></table> - <hr width=800 align=center> - }; - 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{ - <hr width=800 align=left> - - <b>Field/recipient specific options:</b><br><br> - }; - - - 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<br>}; - - 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 { - <table><tr><td colspan=2> - <b>Global options:</b></tr> - <tr><td width=150></td><td> - Only email me reports of changes made by other people - <input type="checkbox" name="ExcludeSelf" VALUE="on" $excludeSelf> - <br> - </td> - </tr> - </table> - }; - -} - -sub printEmailPrefGroup ($$\%) { - - my ($groupName,$textName,$refCurrentFlags) = @_[0,1,2]; - my @tmpFlags = @emailFlags; - - print qq {<table cellspacing=0 cellpadding=6> }; - print qq {<tr><td colspan=2> - When I\'m $textName, email me:</td></tr> }; - - while ((my $flagName,my $flagText) = splice(@tmpFlags,0,2)) { - - printEmailOption($groupName . $flagName, $flagText, $refCurrentFlags); - } - print qq { </table> }; - print qq { <hr WIDTH=320 ALIGN=left> }; -} - -sub printEmailOption ($$\%) { - - my $value= ''; - - my ($optionName,$description,$refCurrentFlags) = @_[0,1,2]; - - #print qq{ email$optionName: $$refCurrentFlags{"email$optionName"} <br>}; - - # 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{ - <tr><td width=320></td> - <td><input type="checkbox" name="email$optionName" VALUE="on" $value> - $description</td> - </tr> - }; } -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 <br>}; - #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{<INPUT TYPE=HIDDEN NAME="name-$count" VALUE="$q">\n}; - $count++; - } - print qq{<INPUT TYPE=HIDDEN NAME="numqueries" VALUE="$count">\n}; - if (!$count) { - print qq{ -<TR><TD COLSPAN="4"> -If you go create remembered queries in the <A HREF="query.cgi">query page</A>, -you can then come to this page and choose to have some of them appear in the -footer of each Bugzilla page. -</TD></TR>}; + 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 "<TR><TD>You have the following permission bits set on your account:\n"; - print "<P><UL>\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 "<LI>$description\n"; - $found = 1; - } - if ($found == 0) { - print "<LI>(No extra permission bits have been set).\n"; + push(@has_bits, FetchSQLData()); } - print "</UL>\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{<A HREF="editusers.cgi">other users</A>:\n}; - print "<P><UL>\n"; SendSQL("SELECT description FROM groups " . "WHERE bit & $blessgroupset != 0 " . "ORDER BY bit"); while (MoreSQLData()) { - my ($description) = (FetchSQLData()); - print "<LI>$description\n"; + push(@set_bits, FetchSQLData()); } - print "</UL>\n"; } - print "</TR></TD>\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 "<pre>" . value_quote($k) . ": " . value_quote($::FORM{$k}) . "\n</pre>"; -# } - -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{ -<CENTER> -<TABLE CELLSPACING="0" CELLPADDING="10" BORDER=0 WIDTH="100%"> -<TR> -<TH COLSPAN="$numcols" BGCOLOR="lightblue">User preferences</TH> -</TR> -<TR><TD BGCOLOR=$headcol> </TD> -}; - - -my $bankdescription; -my $showfunc; -my $savefunc; - -foreach my $i (@banklist) { - my ($name, $description) = (@$i); - my $color = ""; - if ($name eq $bank) { - print qq{<TD ALIGN="center">$description</TD>}; - my $zz; - ($zz, $bankdescription, $showfunc, $savefunc) = (@$i); - } else { - print qq{<TD ALIGN="center" BGCOLOR="lightblue"><A HREF="userprefs.cgi?bank=$name">$description</A></TD>}; +# Work out the current tab +foreach my $tab (@tabs) { + if ($tab->{'name'} eq $current_tab_name) { + $vars->{'current_tab'} = $tab; + last; } } -print qq{ -<TD BGCOLOR=$headcol> </TD></TR> -</TABLE> -</CENTER> -<P> -}; - - - -if (defined $bankdescription) { - $userid = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'}); +$vars->{'tabs'} = \@tabs; - if ($::FORM{'dosave'}) { - &$savefunc; - print "Your changes have been saved."; - } - print qq{<H3>$bankdescription</H3><FORM METHOD="POST"><TABLE>}; - - # execute subroutine from @banklist based on bank selected. - &$showfunc; - - print qq{</TABLE><INPUT TYPE="hidden" NAME="dosave" VALUE="1">}; - print qq{<INPUT TYPE="hidden" NAME="savedEmailTech" VALUE="}; - - # default this to 0 if it's not already set - # - if (defined $showNewEmailTech) { - print qq{$showNewEmailTech">}; - } else { - print qq{0">}; - } - print qq{<INPUT TYPE="hidden" NAME="bank" VALUE="$bank"> }; - - if ($savefunc) { - print qq{<table><tr><td width=150></td><td> - <INPUT TYPE="submit" VALUE="Submit Changes"> - </td></tr></table> }; - } - print qq{</FORM>\n}; -} else { - print "<P>Please choose from the above links which settings you wish to change.</P>"; +# 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 "<P>"; - - -PutFooter(); |