userprefs.cgi 19.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#!/usr/bonsaitools/bin/perl -w
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# 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.
#
# Contributor(s): Terry Weissman <terry@mozilla.org>
17
#                 Dan Mosedale <dmose@mozilla.org>
18
#                 Alan Raetz <al_raetz@yahoo.com>
19
#                 David Miller <justdave@syndicomm.com>
20
#                 Christopher Aillon <christopher@aillon.com>
21 22 23 24 25 26

use diagnostics;
use strict;

require "CGI.pl";

27 28
use RelationSet;

29 30 31 32 33
# Shut up misguided -w warnings about "used only once".  "use vars" just
# doesn't work for me.
sub sillyness {
    my $zz;
    $zz = $::defaultqueryname;
34
    $zz = $::usergroupset;
35
}
36 37 38

my $userid;

39 40 41 42 43 44 45 46 47
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',
48 49
        'CClist',       'on the CC list',
        'Voter',        'a Voter'
50 51 52
        );

my @emailFlags = (
53
        'Removeme',     'When I\'m added to or removed from this capacity',
54 55 56 57 58 59 60 61 62 63
        '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 =
64
        'ExcludeSelf~'               . 'on~' .
65

66
        'emailOwnerRemoveme~'        . 'on~' .
67 68 69 70 71 72 73 74
        'emailOwnerComments~'        . 'on~' .
        'emailOwnerAttachments~'     . 'on~' .
        'emailOwnerStatus~'          . 'on~' .
        'emailOwnerResolved~'        . 'on~' .
        'emailOwnerKeywords~'        . 'on~' .
        'emailOwnerCC~'              . 'on~' .
        'emailOwnerOther~'           . 'on~' .

75
        'emailReporterRemoveme~'     . 'on~' .
76 77 78 79 80 81 82 83
        'emailReporterComments~'     . 'on~' .
        'emailReporterAttachments~'  . 'on~' .
        'emailReporterStatus~'       . 'on~' .
        'emailReporterResolved~'     . 'on~' .
        'emailReporterKeywords~'     . 'on~' .
        'emailReporterCC~'           . 'on~' .
        'emailReporterOther~'        . 'on~' .

84
        'emailQAcontactRemoveme~'    . 'on~' .
85 86 87 88 89 90 91 92
        'emailQAcontactComments~'    . 'on~' .
        'emailQAcontactAttachments~' . 'on~' .
        'emailQAcontactStatus~'      . 'on~' .
        'emailQAcontactResolved~'    . 'on~' .
        'emailQAcontactKeywords~'    . 'on~' .
        'emailQAcontactCC~'          . 'on~' .
        'emailQAcontactOther~'       . 'on~' .

93
        'emailCClistRemoveme~'       . 'on~' .
94 95 96 97 98 99
        'emailCClistComments~'       . 'on~' .
        'emailCClistAttachments~'    . 'on~' .
        'emailCClistStatus~'         . 'on~' .
        'emailCClistResolved~'       . 'on~' .
        'emailCClistKeywords~'       . 'on~' .
        'emailCClistCC~'             . 'on~' .
100
        'emailCClistOther~'          . 'on~' .
101

102 103
        'emailVoterRemoveme~'        . 'on~' .
        'emailVoterComments~'        . 'on~' .
104
        'emailVoterAttachments~'     . 'on~' .
105 106 107 108 109
        'emailVoterStatus~'          . 'on~' .
        'emailVoterResolved~'        . 'on~' .
        'emailVoterKeywords~'        . 'on~' .
        'emailVoterCC~'              . 'on~' .
        'emailVoterOther~'           . 'on' ;
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

sub EmitEntry {
    my ($description, $entry) = (@_);
    print qq{<TR><TH ALIGN="right">$description:</TH><TD>$entry</TD></TR>\n};
}

sub Error {
    my ($msg) = (@_);
    print qq{
$msg
<P>
Please hit <B>back</B> and try again.
};
    PutFooter();
    exit();
}


sub ShowAccount {
    SendSQL("SELECT realname FROM profiles WHERE userid = $userid");
    my ($realname) = (FetchSQLData());

    $realname = value_quote($realname);
133
        
134
    EmitEntry("Old password",
135 136
              qq|<input type=hidden name="Bugzilla_login" value="$::COOKIE{Bugzilla_login}">| .
              qq|<input type=password name="Bugzilla_password">|);
137 138 139 140
    EmitEntry("New password",
              qq{<input type=password name="pwd1">});
    EmitEntry("Re-enter new password", 
              qq{<input type=password name="pwd2">});
141
    EmitEntry("Your real name (optional)",
142 143 144 145
              qq{<INPUT SIZE=35 NAME="realname" VALUE="$realname">});
}

sub SaveAccount {
146
    if ($::FORM{'Bugzilla_password'} ne ""
147
        || $::FORM{'pwd1'} ne "" || $::FORM{'pwd2'} ne "") {
148
        my $old = SqlQuote($::FORM{'Bugzilla_password'});
149 150
        my $pwd1 = SqlQuote($::FORM{'pwd1'});
        my $pwd2 = SqlQuote($::FORM{'pwd2'});
151 152 153 154 155 156
        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 ( crypt($::FORM{'Bugzilla_password'}, $oldcryptedpwd) ne $oldcryptedpwd ) {
157 158 159 160 161 162 163 164
            Error("You did not enter your old password correctly.");
        }
        if ($pwd1 ne $pwd2) {
            Error("The two passwords you entered did not match.");
        }
        if ($::FORM{'pwd1'} eq '') {
            Error("You must enter a new password.");
        }
165 166 167 168 169 170 171
        my $passworderror = ValidatePassword($::FORM{'pwd1'});
        Error($passworderror) if $passworderror;

        my $cryptedpassword = SqlQuote(Crypt($::FORM{'pwd1'}));
        SendSQL("UPDATE  profiles 
                 SET     cryptpassword = $cryptedpassword 
                 WHERE   userid = $userid");
172 173
    }
    SendSQL("UPDATE profiles SET " .
174
            "realname = " . SqlQuote(trim($::FORM{'realname'})) .
175 176 177
            " WHERE userid = $userid");
}

178 179 180 181 182 183 184 185 186 187 188
#
# 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");
}

189

190
sub ShowEmailOptions () {
191

192 193 194
    if (Param("supportwatchers")) {
        my $watcheduserSet = new RelationSet;
        $watcheduserSet->mergeFromDB("SELECT watched FROM watch WHERE" .
195
                                    " watcher=$userid");
196
        my $watchedusers = $watcheduserSet->toString();
197

198
        print qq{
199 200
<TR><TD COLSPAN="4"><HR></TD></TR>
<TR><TD COLSPAN="4">
201
If you want to help cover for someone when they're on vacation, or if
202 203 204
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.
205 206
</TD></TR>};

207
        EmitEntry("Users to watch",
208
              qq{<INPUT SIZE=35 NAME="watchedusers" VALUE="$watchedusers">});
209
    }
210

211
    print qq{<TR><TD COLSPAN="2"><HR></TD></TR>};
212

213
    showAdvancedEmailFilterOptions();
214

215 216 217 218
print qq {
<TABLE CELLSPACING="0" CELLPADDING="10" BORDER=0 WIDTH="100%">
<TR><TD>};

219 220
}

221 222 223 224 225 226 227 228 229 230 231 232
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>
233 234 235 236
        <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.
237 238 239 240
        </center></td></tr></table>
       <hr width=800 align=center>
    };

241
    SendSQL("SELECT emailflags FROM profiles WHERE userid = $userid");
242

243
    ($flags) = FetchSQLData();
244 245 246

    # 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 
247 248
    # use emailflags.  create a default flagset for them, based on
    # static defaults. 
249 250
    #
    if ( !$flags ) {
251
        $flags = $defaultEmailFlagString;
252
        setEmailFlags($flags);
253
    }
254

255 256 257
    # 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.
258
    #
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    %userEmailFlags = split(/~/ , $flags, 255);

    showExcludeSelf(\%userEmailFlags);

    # print STDERR "$flags\n";

    print qq{
                <hr width=800 align=left>
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                  <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 (\%) {

     my %CurrentFlags = %{$_[0]};
     
     my $excludeSelf = " ";

     while ( my ($key,$value) = each (%CurrentFlags) ) {

     # print qq{flag name: $key    value: $value<br>};

        if ( $key eq 'ExcludeSelf' ) {

                if ( $value eq 'on' ) {

                        $excludeSelf = "CHECKED";
                        }
                }
        }

        print qq {
                <table><tr><td colspan=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                <b>Global options:</b></tr>
                <tr><td width=150></td><td>
302
                Only email me reports of changes made by other people
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
             <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> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                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';
    }

342 343 344 345 346
    # **** Kludge ... also mark on if the value in $$refCurrentFlags in undef
    if (!defined($$refCurrentFlags{"email$optionName"})) {
        $value = 'CHECKED';
    }

347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    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;

364 365
    if ( defined $::FORM{'ExcludeSelf'}) {
        $updateString .= 'ExcludeSelf~on';
366
    } else {
367 368 369
        $updateString .= 'ExcludeSelf~';
    }
    my @tmpGroups = @emailGroups;
370

371
    while ((my $groupName,my $groupText) = splice(@tmpGroups,0,2) ) {
372

373
        my @tmpFlags = @emailFlags;
374

375
        while ((my $flagName,my $flagText) = splice(@tmpFlags,0,2) ) {
376

377 378
            my $entry = 'email' . $groupName . $flagName;
            my $entryValue;
379

380 381 382 383
            if (!defined $::FORM{$entry} ) {
                $entryValue = "";
            } else {
                $entryValue = $::FORM{$entry};
384 385
            }

386 387
            $updateString .= '~' . $entry . '~' . $entryValue;
        }
388 389 390 391 392 393 394 395 396
    }
        
    #open(FID,">updateString");
    #print qq{UPDATE STRING: $updateString <br>};
    #close(FID);

    SendSQL("UPDATE profiles SET emailflags = " .
            SqlQuote($updateString) . " WHERE userid = $userid");

397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
    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");
        
        }
    }
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
}



sub ShowFooter {
    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);
    SendSQL("SELECT name, linkinfooter FROM namedqueries " .
            "WHERE userid = $userid");
448
    my $count = 0;
449 450 451 452 453 454
    while (MoreSQLData()) {
        my ($name, $linkinfooter) = (FetchSQLData());
        if ($name eq $::defaultqueryname) {
            next;
        }
        my $entry =
455
            BuildPulldown("query-$count",
456 457 458 459
                          [["0", "should only appear in the query page"],
                           ["1", "should appear on the footer of every page"]],
                          $linkinfooter);
        EmitEntry("Your query named '$name'", $entry);
460 461 462
        my $q = value_quote($name);
        print qq{<INPUT TYPE=HIDDEN NAME="name-$count" VALUE="$q">\n};
        $count++;
463
    }
464 465
    print qq{<INPUT TYPE=HIDDEN NAME="numqueries" VALUE="$count">\n};
    if (!$count) {
466
        print qq{
467
<TR><TD COLSPAN="4">                            
468
If you go create remembered queries in the <A HREF="query.cgi">query page</A>,
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
you can then come to this page and choose to have some of them appear in the 
footer of each Bugzilla page.
</TD></TR>};
    }
}
              
    
sub SaveFooter {
    my %old;
    SendSQL("SELECT name, linkinfooter FROM namedqueries " .
            "WHERE userid = $userid");
    while (MoreSQLData()) {
        my ($name, $linkinfooter) = (FetchSQLData());
        $old{$name} = $linkinfooter;
    }
484 485 486 487 488
    
    for (my $c=0 ; $c<$::FORM{'numqueries'} ; $c++) {
        my $name = $::FORM{"name-$c"};
        if (exists $old{$name}) {
            my $new = $::FORM{"query-$c"};
489 490 491 492 493
            if ($new ne $old{$name}) {
                SendSQL("UPDATE namedqueries SET linkinfooter = $new " .
                        "WHERE userid = $userid " .
                        "AND name = " . SqlQuote($name));
            }
494 495
        } else {
            Error("Hmm, the $name query seems to have gone away.");
496 497
        }
    }
498 499
    SendSQL("UPDATE profiles SET mybugslink = " . SqlQuote($::FORM{'mybugslink'}) .
            " WHERE userid = $userid");
500 501 502 503
}
    


504
sub ShowPermissions {
505
    print "<TR><TD>You have the following permission bits set on your account:\n";
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
    print "<P><UL>\n";
    my $found = 0;
    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";
    }
    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";
        }
533
        print "</UL>\n";
534
    }
535
    print "</TR></TD>\n";
536 537 538 539 540
}
        



541 542 543 544 545 546 547 548 549 550
######################################################################
################# Live code (not sub defs) starts here ###############


confirm_login();

print "Content-type: text/html\n\n";

GetVersionTable();

551
PutHeader("User Preferences", "User Preferences", $::COOKIE{'Bugzilla_login'});
552

553 554 555 556
#  foreach my $k (sort(keys(%::FORM))) {
#      print "<pre>" . value_quote($k) . ": " . value_quote($::FORM{$k}) . "\n</pre>";
#  }

557 558 559 560 561 562
my $bank = $::FORM{'bank'} || "account";

my @banklist = (
                ["account", "Account settings",
                 \&ShowAccount, \&SaveAccount],
                ["diffs", "Email settings",
563
                                 \&ShowEmailOptions, \&SaveEmailOptions],
564
                ["footer", "Page footer",
565 566 567
                 \&ShowFooter, \&SaveFooter],
                ["permissions", "Permissions",
                 \&ShowPermissions, undef]
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
                );


my $numbanks = @banklist;
my $numcols = $numbanks + 2;

my $headcol = '"lightblue"';

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>&nbsp;</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>};
    }
}
print qq{
<TD BGCOLOR=$headcol>&nbsp;</TD></TR>
</TABLE>
</CENTER>
<P>
};




if (defined $bankdescription) {
    $userid = DBNameToIdAndCheck($::COOKIE{'Bugzilla_login'});

    if ($::FORM{'dosave'}) {
        &$savefunc;
        print "Your changes have been saved.";
    }
618 619 620
    print qq{<H3>$bankdescription</H3><FORM METHOD="POST"><TABLE>};

    # execute subroutine from @banklist based on bank selected.
621
    &$showfunc;
622 623 624 625 626 627 628 629 630 631 632 633 634

    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"> };

635
    if ($savefunc) {
636 637 638
              print qq{<table><tr><td width=150></td><td>
                        <INPUT TYPE="submit" VALUE="Submit Changes">
                        </td></tr></table> };
639 640
    }
    print qq{</FORM>\n};
641 642 643 644 645 646 647 648 649
} else {
    print "<P>Please choose from the above links which settings you wish to change.</P>";
}


print "<P>";


PutFooter();