[LON-CAPA-cvs] cvs: loncom /interface createaccount.pm domainprefs.pm loncommon.pm lonconfigsettings.pm loncreateuser.pm lonhtmlcommon.pm

raeburn raeburn at source.lon-capa.org
Thu Aug 3 12:22:10 EDT 2017


raeburn		Thu Aug  3 16:22:10 2017 EDT

  Modified files:              
    /loncom/interface	createaccount.pm domainprefs.pm loncommon.pm 
                     	lonconfigsettings.pm loncreateuser.pm 
                     	lonhtmlcommon.pm 
  Log:
  - Self-creation of user accounts using e-mail verification.
    - Options set by domain coordinator for account requests verified by e-mail
      for: processing, restrictions on permitted e-mail addresses, and choice
      of username can be set to different values, depending on requester's
      (self-reported) institutional status.
    - Setting which status types can be self-reported by requesters using 
      e-mail verification moved from 
      "Default authentication/language/timezone/portal/types" panel to
      "Users self-creating accounts" panel.
    - Web form used to request account prompts user for appropriate responses 
      depending on self-reported status, and e-mail and username constraints.
  
  
-------------- next part --------------
Index: loncom/interface/createaccount.pm
diff -u loncom/interface/createaccount.pm:1.73 loncom/interface/createaccount.pm:1.74
--- loncom/interface/createaccount.pm:1.73	Wed Jul 26 19:45:45 2017
+++ loncom/interface/createaccount.pm	Thu Aug  3 16:22:08 2017
@@ -4,7 +4,7 @@
 # kerberos, or SSO) or an e-mail address. Requests to use an e-mail address as
 # username may be processed automatically, or may be queued for approval.
 #
-# $Id: createaccount.pm,v 1.73 2017/07/26 19:45:45 raeburn Exp $
+# $Id: createaccount.pm,v 1.74 2017/08/03 16:22:08 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -150,7 +150,7 @@
     my %domconfig =
         &Apache::lonnet::get_dom('configuration',['usercreation'],$domain);
     my ($cancreate,$statustocreate,$statusforemail,$emailusername,
-        $verification,$emaildomain,$types,$usertypes,$othertitle) =
+        $emailoptions,$verification,$emaildomain,$types,$usertypes,$othertitle) =
         &get_creation_controls($domain,$domconfig{'usercreation'});
     my ($additems,$pagetitle);
     if (ref($cancreate) eq 'ARRAY') {
@@ -158,14 +158,15 @@
                 ($env{'form.create_with_email'})) {
             if ((grep(/^email$/,@{$cancreate})) && (ref($statusforemail) eq 'ARRAY')) {
                 my $usertype = &get_usertype($domain);
-                if (($usertype ne '') && (grep(/^\Q$usertype\E$/,@{$statusforemail}))) {
-                    $js .= &username_js();
-                } elsif (($usertype eq '') || (!grep(/^\Q$usertype\E$/,@{$statusforemail}))) {
+                if ((($usertype eq '') || (!grep(/^\Q$usertype\E$/,@{$statusforemail}))) && 
+                    (@{$statusforemail} > 0)) {
                     $js .= &setelements_js($statusforemail,$types,$usertypes,$othertitle);
                     $additems = {'add_entries' => { 'onload' => "setElements();"} };
-                    if (@{$cancreate} == 1) {
+                    if ((@{$cancreate} == 1) && (@{$statusforemail} > 0)) {
                         $pagetitle = 'Select affiliation';
                     }
+                } else {
+                    $js .= &username_js();
                 }
             }
         }
@@ -255,18 +256,32 @@
             return OK;
         }
     }
-    my ($usernameset,$condition);
+    my ($usernameset,$condition,$excluded,$hascustom);
     if ((grep(/^email$/,@{$cancreate})) && (($env{'form.create_with_email'}) ||
                                             ((!$token) && ($env{'form.phase'} eq '')))) {
         my $usertype = &get_usertype($domain);
+        if ($usertype eq '') {
+            $usertype = 'default';
+        }
         if (ref($verification) eq 'HASH') {
-            if ($verification->{$usertype} eq 'other') {
-                $usernameset = 1;
+            if ($verification->{$usertype} =~ /^(free|first)$/) {
+                $usernameset = $verification->{$usertype};
+            }
+        }
+        if (ref($emailoptions) eq 'HASH') {
+            if ($emailoptions->{$usertype} =~ /^(inst|noninst)$/) {
+                my $chosen = $1;
                 if (ref($emaildomain) eq 'HASH') {
-                    if ($emaildomain->{$usertype} ne '') {
-                        $condition = $emaildomain->{$usertype};
+                    if (ref($emaildomain->{$usertype}) eq 'HASH') {
+                        if ($chosen eq 'inst') {
+                            $condition = $emaildomain->{$usertype}->{$chosen};
+                        } else {
+                            $excluded = $emaildomain->{$usertype}->{$chosen};
+                        }
                     }
                 }
+            } elsif ($emailoptions->{$usertype} eq 'custom') {
+                $hascustom = 1;
             }
         }
     }
@@ -301,11 +316,14 @@
     } elsif ($env{'form.create_with_email'}) {
         &print_header($r,$start_page,$courseid);
         my $usertype = &get_usertype($domain);
+        if ($usertype eq '') {
+            $usertype = 'default';
+        }
         $output = &process_email_request($env{'form.uname'},$domain,$domdesc,
                                          $contact_name,$contact_email,$cancreate,
                                          $lonhost,$domconfig{'usercreation'},
                                          $emailusername,$courseid,$usertype,
-                                         $usernameset,$condition);
+                                         $usernameset,$condition,$excluded,$hascustom);
     } elsif (!$token) {
         &print_header($r,$start_page,$courseid,$pagetitle);
         my $now=time;
@@ -319,13 +337,13 @@
             }
         }
         if (grep(/^email$/,@{$cancreate})) {
-            $r->print(&javascript_validmail());
+            $r->print(&javascript_validmail($condition));
         }
         my $usertype = &get_usertype($domain);
         $output = &print_username_form($r,$domain,$domdesc,$cancreate,$now,$lonhost,
                                        $include,$courseid,$emailusername,
                                        $statusforemail,$usernameset,$condition,
-                                       $usertype,$types,$usertypes,$othertitle);
+                                       $excluded,$usertype,$types,$usertypes,$othertitle);
     }
     $r->print($output);
     &print_footer($r);
@@ -409,7 +427,7 @@
 }
 
 sub javascript_setforms {
-    my ($now,$emailusername,$captcha,$usertype,$recaptchaversion,$usernameset,$condition) =  @_;
+    my ($now,$emailusername,$captcha,$usertype,$recaptchaversion,$usernameset,$condition,$excluded) =  @_;
     my ($setuserinfo, at required,$requiredchk);
     if (ref($emailusername) eq 'HASH') {
         if (ref($emailusername->{$usertype}) eq 'HASH') {  
@@ -431,7 +449,7 @@
                 '                    server.elements.recaptcha_response_field.value=client.elements.recaptcha_response_field.value;'."\n";
             }
         }
-        if ($usernameset) {
+        if ($usernameset eq 'free') {
             $setuserinfo .=
                 '                    server.elements.username.value=client.elements.username.value;'."\n";
         }
@@ -478,10 +496,8 @@
                 uextkey=client.elements.uextkey.value;
                 lextkey=client.elements.lextkey.value;
                 initkeys();
-
                 server.elements.upass.value
                     = getCrypted(client.elements.upass$now.value);
-
                 client.elements.uname.value='';
                 client.elements.upass$now.value='';
                 if (context == 'email') {
@@ -543,9 +559,12 @@
 }
 
 sub javascript_validmail {
+    my ($condition) = @_;
     my %js_lt = &Apache::lonlocal::texthash (
                email => 'The e-mail address you entered',
                notv  => 'is not a valid e-mail address',
+               avae  => 'A valid e-mail address is not formed when the value you entered',
+               isco  => 'is combined with the required domain',
     );
     my $output =  "\n".'<script type="text/javascript">'."\n".
                   '// <![CDATA['."\n".
@@ -554,8 +573,13 @@
     $output .= <<"ENDSCRIPT";
 function validate_email(client) {
     field = client.uname;
-    if (validmail(field) == false) {
-        alert("$js_lt{'email'}: "+field.value+" $js_lt{'notv'}.");
+    var condition = '$condition';
+    if (validmail(field,condition) == false) {
+        if ((condition != undefined) && (condition != '')) {
+            alert("$js_lt{'avae'}: "+field.value+" $js_lt{'isco'}: "+condition);
+        } else {
+            alert("$js_lt{'email'}: "+field.value+" $js_lt{'notv'}.");
+        }
         return false;
     }
     return true;
@@ -567,7 +591,7 @@
 
 sub print_username_form {
     my ($r,$domain,$domdesc,$cancreate,$now,$lonhost,$include,$courseid,$emailusername,
-        $statusforemail,$usernameset,$condition,$usertype,$types,$usertypes,
+        $statusforemail,$usernameset,$condition,$excluded,$usertype,$types,$usertypes,
         $othertitle) = @_;
     my %lt = &Apache::lonlocal::texthash (
                                          crac => 'Create account with a username provided by this institution',
@@ -595,14 +619,16 @@
         if (grep(/^email$/,@{$cancreate})) {
             $output .= '<div class="LC_left_float"><h3>'.$lt{'crae'}.'</h3>';
             if ($usertype ne '') {
-                if (ref($statusforemail) eq 'ARRAY') {
+                if ((ref($statusforemail) eq 'ARRAY') && (@{$statusforemail} > 0)) {
                     unless (grep(/^\Q$usertype\E$/,@{$statusforemail})) {
                         undef($usertype);
                     }
+                } elsif ($usertype ne 'default') {
+                    undef($usertype);
                 }
             }
             if (($usertype eq '') && (ref($statusforemail) eq 'ARRAY') && 
-                (@{$statusforemail}) && (ref($types) eq 'ARRAY')) {
+                (@{$statusforemail} > 0) && (ref($types) eq 'ARRAY') && (@{$types} > 0)) {
                 my @posstypes = @{$types};
                 unless (grep(/^default$/, at posstypes)) {
                     push(@posstypes,'default');
@@ -659,9 +685,12 @@
                         $output .= $prompt.'<br />'.
                                    $lt{'yopw'}.'<br />';
                     }
+                    if ($usertype eq '') {
+                        $usertype = 'default';
+                    }
                     $output .= &print_dataentry_form($r,$domain,$lonhost,$include,$now,$captchaform,
                                                      $courseid,$emailusername,$captcha,$usertype,
-                                                     $recaptchaversion,$usernameset,$condition);
+                                                     $recaptchaversion,$usernameset,$condition,$excluded);
                 }
             }
             $output .= '</div>';
@@ -729,42 +758,52 @@
 sub process_email_request {
     my ($useremail,$domain,$domdesc,$contact_name,$contact_email,$cancreate,
         $server,$settings,$emailusername,$courseid,$usertype,$usernameset,
-        $condition) = @_;
+        $condition,$excluded,$hascustom) = @_;
     my ($output,$uname);
     if (ref($cancreate) eq 'ARRAY') {
         if (!grep(/^email$/,@{$cancreate})) {
             $output = &invalid_state('noemails',$domdesc,
                                      $contact_name,$contact_email);
             return $output;
-        } elsif ($useremail !~ /^[^\@]+\@[^\@]+\.[^\@\.]+$/) {
+        } elsif ((($condition ne '') && ($useremail !~ /^[^\@]+$/)) ||
+                 (($condition eq '') && ($useremail !~ /^[^\@]+\@[^\@]+\.[^\@\.]+$/))) {
             $output = &invalid_state('baduseremail',$domdesc,
                                      $contact_name,$contact_email);
             return $output;
         } else {
             $useremail =~ s/^\s+|\s+$//g;
             my $possuname;
-            if ($env{'form.username'}) {
+            if ($condition ne '') {
+                if ($usernameset eq 'first') {
+                    $possuname = $useremail;
+                }
+                $useremail .= $condition;
+            } elsif ($excluded ne '') {
+                if ($useremail =~ /^[^\@]+\Q$excluded\E$/) {
+                    $output = &invalid_state('userrules',$domdesc,
+                                     $contact_name,$contact_email);
+                    return $output;
+                } 
+            }
+            if (($usernameset eq 'free') && ($env{'form.username'} ne '')) {
                 $possuname = $env{'form.username'};
-                $possuname  =~ s/^\s+|\s+$//g;
+            } elsif (($usernameset eq 'first') && ($condition eq '')) {
+                if ($condition eq '') {
+                    ($possuname) = ($useremail =~ /^([^\@]+)\@/);
+                }
             }
-            if (($usernameset) && ($possuname ne '')) {
-                if ($condition ne '') {
-                    if ($useremail =~ /\Q$condition\E/) {
-                        $uname = &LONCAPA::clean_username($possuname);
-                    } else {
-                        $output = &invalid_state('emailfail',$domdesc,
+            if ($possuname ne '') {
+                $possuname  =~ s/^\s+|\s+$//g;
+                if ($possuname ne '') {
+                    $uname=&LONCAPA::clean_username($possuname);
+                    if ($uname ne $possuname) {
+                        $output = &invalid_state('badusername',$domdesc,
                                                  $contact_name,$contact_email);
                         return $output;
                     }
-                } else {
-                    $uname=&LONCAPA::clean_username($possuname);
                 }
-                if ($uname ne $possuname) {
-                    $output = &invalid_state('badusername',$domdesc,
-                                             $contact_name,$contact_email);
-                    return $output;
-                }
-            } else {
+            }
+            if ($possuname eq '') {
                 $uname=&LONCAPA::clean_username($useremail);
                 if ($useremail ne $uname) {
                     $output = &invalid_state('badusername',$domdesc,
@@ -796,10 +835,10 @@
                         }
                     }
                 }
-                if (($usernameset) && ($possuname ne '')) {
+                if ($hascustom) {
                     my $format_msg = 
                         &guest_format_check($useremail,$domain,$cancreate,
-                                            $settings);
+                                            $settings,$usertype);
                     if ($format_msg) {
                         $output = &invalid_state('userformat',$domdesc,$contact_name,
                                                  $contact_email,$format_msg);
@@ -940,7 +979,7 @@
                                     $data{'inststatus'} = $usertype;
                                 }
                             } else {
-                                undef($usertype);
+                                $disposition = 'approval';
                             }
                         }
                         delete($data{'usertype'});
@@ -948,7 +987,12 @@
                     if (ref($settings) eq 'HASH') {
                         if (ref($settings->{'cancreate'}) eq 'HASH') {
                             if (ref($settings->{'cancreate'}{'selfcreateprocessing'}) eq 'HASH') {
-                                $disposition = $settings->{'cancreate'}{'selfcreateprocessing'}{$usertype}; 
+                                if ($usertype ne '') {
+                                    $disposition = $settings->{'cancreate'}{'selfcreateprocessing'}{$usertype};
+                                    unless ($disposition =~ /^(approval|automatic)$/) {
+                                        $disposition = 'approval';
+                                    }
+                                }
                             }
                         }
                     }
@@ -1027,7 +1071,7 @@
 #
 sub print_dataentry_form {
     my ($r,$domain,$lonhost,$include,$now,$captchaform,$courseid,$emailusername,$captcha,
-        $usertype,$recaptchaversion,$usernameset,$condition) = @_;
+        $usertype,$recaptchaversion,$usernameset,$condition,$excluded) = @_;
     my ($error,$output);
     if (open(my $jsh,"<$include/londes.js")) {
         while(my $line = <$jsh>) {
@@ -1035,7 +1079,7 @@
         }
         close($jsh);
         $output = &javascript_setforms($now,$emailusername,$captcha,$usertype,$recaptchaversion,
-                                       $usernameset,$condition).
+                                       $usernameset,$condition,$excluded).
                   "\n".&javascript_checkpass($now,'email');
         my ($lkey,$ukey) = &Apache::loncommon::des_keys();
         my ($lextkey,$uextkey) = &getkeys($lkey,$ukey);
@@ -1073,7 +1117,7 @@
             $output .= '<input type="hidden" name="type" value="'.
                        &HTML::Entities::encode($usertype,'\'<>"&').'" />'."\n";
         }
-        if ($usernameset) {
+        if ($usernameset eq 'free') {
             $output .= '<input type="hidden" name="username" value="" />'."\n"; 
         }
         $output .= <<"ENDSERVERFORM";
@@ -1097,7 +1141,7 @@
             &Apache::loncreateuser::personal_data_display('',$domain,'email','selfcreate',
                                                           '','',$now,$captchaform,
                                                           $emailusername,$usertype,
-                                                          $usernameset,$condition);
+                                                          $usernameset,$condition,$excluded);
         if ($rowcount) {
             $output .= '<div class="LC_left_float">'.$beginclientform.$datatable.$endclientform;
         } else {
@@ -1120,7 +1164,7 @@
 sub get_creation_controls {
     my ($domain,$usercreation) = @_;
     my (@cancreate, at statustocreate, at statusforemail,$emailusername,$processing,
-        $verification,$emaildomain,$othertitle,$usertypes,$types);
+        $emailoptions,$verification,$emaildomain,$othertitle,$usertypes,$types);
     if (ref($usercreation) eq 'HASH') {
         if (ref($usercreation->{'cancreate'}) eq 'HASH') {
             ($othertitle,$usertypes,$types) =
@@ -1152,6 +1196,9 @@
                 if (ref($usercreation->{'cancreate'}{'selfcreateprocessing'}) eq 'HASH') {
                     $processing = $usercreation->{'cancreate'}{'selfcreateprocessing'};
                 }
+                if (ref($usercreation->{'cancreate'}{'emailoptions'}) eq 'HASH') {
+                    $emailoptions = $usercreation->{'cancreate'}{'emailoptions'};
+                }
                 if (ref($usercreation->{'cancreate'}{'emailverified'}) eq 'HASH') {
                     $verification = $usercreation->{'cancreate'}{'emailverified'};
                 }
@@ -1162,27 +1209,15 @@
                     my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
                     my @emailtypes;
                     if (ref($domdefaults{'inststatusguest'}) eq 'ARRAY') {
-                        @emailtypes = @{$domdefaults{'inststatusguest'}};
-                        if (@emailtypes) {
-                            unless (grep(/^default$/, at emailtypes)) {
-                                push(@emailtypes,'default');
-                            }
-                            foreach my $type (@emailtypes) {
-                                if ($processing->{$type} ne 'notinuse') {
-                                    unless (grep(/^$type$/, at statusforemail)) {
-                                        push(@statusforemail,$type);
-                                    }
-                                }
-                            }
-                            unless (@statusforemail) {
-                                my @okcreate;
-                                foreach my $poss (@cancreate) {
-                                    unless ($poss eq 'email') {
-                                        push(@okcreate,$poss);
-                                    }
+                        @statusforemail = @{$domdefaults{'inststatusguest'}};
+                        unless (@statusforemail) {
+                            my @okcreate;
+                            foreach my $poss (@cancreate) {
+                                unless ($poss eq 'email') {
+                                     push(@okcreate,$poss);
                                 }
-                                @cancreate = @okcreate;
                             }
+                            @cancreate = @okcreate;
                         }
                     }
                 }  
@@ -1200,7 +1235,7 @@
         }
     }
     return (\@cancreate,\@statustocreate,\@statusforemail,$emailusername,
-            $verification,$emaildomain,$types,$usertypes,$othertitle);
+            $emailoptions,$verification,$emaildomain,$types,$usertypes,$othertitle);
 }
 
 sub create_account {
@@ -1240,7 +1275,13 @@
 
         my ($key,$caller)=split(/&/,$dataref->{'tmpinfo'});
         if ($caller eq 'createaccount') {
-            my $upass = &Apache::loncommon::des_decrypt($key,$encpass);
+            my $upass;
+            if ($encpass eq '') {
+                $output = &mt('Password retrieved was blank.');
+                return ('fail',$error.$output.$end.$rtnlink);
+            } else {
+                $upass = &Apache::loncommon::des_decrypt($key,$encpass);
+            }
 
 # See if we are allowed to use the proposed student/employee ID,
 # as per domain rules; if not, student/employee will be left blank.
@@ -1785,11 +1826,15 @@
 }
 
 sub guest_format_check {
-    my ($useremail,$domain,$cancreate,$settings) = @_;
+    my ($useremail,$domain,$cancreate,$settings,$usertype) = @_;
     my ($login,$format_match,$format_msg, at user_rules);
     if (ref($settings) eq 'HASH') {
         if (ref($settings->{'email_rule'}) eq 'ARRAY') {
             push(@user_rules,@{$settings->{'email_rule'}});
+        } elsif (ref($settings->{'email_rule'}) eq 'HASH') {
+            if (ref($settings->{'email_rule'}->{$usertype}) eq 'ARRAY') {
+                push(@user_rules,@{$settings->{'email_rule'}->{$usertype}});
+            }
         }
     }
     if (@user_rules > 0) {
@@ -1974,7 +2019,7 @@
 <script type="text/javascript">
 // <![CDATA[
 
-function toggleDisplay(caller,divid) {
+function toggleUsernameDisp(caller,divid) {
     if (document.getElementById(divid)) {
         if (caller.checked) {
             if (caller.value == '1') {
Index: loncom/interface/domainprefs.pm
diff -u loncom/interface/domainprefs.pm:1.304 loncom/interface/domainprefs.pm:1.305
--- loncom/interface/domainprefs.pm:1.304	Thu Jul 27 13:42:35 2017
+++ loncom/interface/domainprefs.pm	Thu Aug  3 16:22:08 2017
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Handler to set domain-wide configuration settings
 #
-# $Id: domainprefs.pm,v 1.304 2017/07/27 13:42:35 raeburn Exp $
+# $Id: domainprefs.pm,v 1.305 2017/08/03 16:22:08 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -281,7 +281,7 @@
                                  {col1 => 'Internal Authentication',
                                   col2 => 'Value'},
                                  {col1 => 'Institutional user types',
-                                  col2 => 'Assignable to non-institutional users'}],
+                                  col2 => 'Name displayed'}],
                       print => \&print_defaults,
                       modify => \&modify_defaults,
                     },
@@ -598,6 +598,9 @@
 $coursebrowserjs
 END
         }
+        if (grep(/^selfcreation$/, at actions)) {
+            $js .= &selfcreate_javascript();
+        }
         if (grep(/^contacts$/, at actions)) {
             $js .= &contacts_javascript();
         }
@@ -694,7 +697,7 @@
     } elsif ($action eq 'usercreation') {
         $output = &modify_usercreation($dom,%domconfig);
     } elsif ($action eq 'selfcreation') {
-        $output = &modify_selfcreation($dom,%domconfig);
+        $output = &modify_selfcreation($dom,$lastactref,%domconfig);
     } elsif ($action eq 'usermodification') {
         $output = &modify_usermodification($dom,%domconfig);
     } elsif ($action eq 'contacts') {
@@ -2279,7 +2282,7 @@
 }
 
 sub print_requestmail {
-    my ($dom,$action,$settings,$rowtotal) = @_;
+    my ($dom,$action,$settings,$rowtotal,$customcss,$rowstyle) = @_;
     my ($now,$datatable,%currapp);
     $now = time;
     if (ref($settings) eq 'HASH') {
@@ -2291,7 +2294,19 @@
     }
     my $numinrow = 2;
     my $css_class;
-    $css_class = ($$rowtotal%2? ' class="LC_odd_row"':'');
+    if ($$rowtotal%2) {
+        $css_class = 'LC_odd_row';
+    }
+    if ($customcss) {
+        $css_class .= " $customcss";
+    }
+    $css_class =~ s/^\s+//;
+    if ($css_class) {
+        $css_class = ' class="'.$css_class.'"';
+    }
+    if ($rowstyle) {
+        $css_class .= ' style="'.$rowstyle.'"';
+    }
     my $text;
     if ($action eq 'requestcourses') {
         $text = &mt('Receive notification of course requests requiring approval');
@@ -2764,7 +2779,7 @@
         my $locknamesettings;
         $datatable .= &insttypes_row($settings,$types,$usertypes,
                                      $dom,$numinrow,$othertitle,
-                                    'lockablenames');
+                                    'lockablenames',$rowtotal);
         $$rowtotal ++;
     } else {
         my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
@@ -2907,7 +2922,8 @@
         if (ref($usertypes) eq 'HASH') {
             if (keys(%{$usertypes}) > 0) {
                 $datatable .= &insttypes_row($settings,$types,$usertypes,$dom,
-                                             $numinrow,$othertitle,'cansearch');
+                                             $numinrow,$othertitle,'cansearch',
+                                             $rowtotal);
                 $cansrchrow = 1;
             }
         }
@@ -5752,7 +5768,8 @@
 
 sub print_selfcreation {
     my ($position,$dom,$settings,$rowtotal) = @_;
-    my (@selfcreate,$createsettings,$processing,$emailverified,$emaildomain,$datatable);
+    my (@selfcreate,$createsettings,$processing,$emailoptions,$emailverified,
+        $emaildomain,$datatable);
     if (ref($settings) eq 'HASH') {
         if (ref($settings->{'cancreate'}) eq 'HASH') {
             $createsettings = $settings->{'cancreate'};
@@ -5769,6 +5786,9 @@
                 if (ref($createsettings->{'selfcreateprocessing'}) eq 'HASH') {
                     $processing = $createsettings->{'selfcreateprocessing'};
                 }
+                if (ref($createsettings->{'emailoptions'}) eq 'HASH') {
+                    $emailoptions = $createsettings->{'emailoptions'};
+                }
                 if (ref($createsettings->{'emailverified'}) eq 'HASH') {
                     $emailverified = $createsettings->{'emailverified'};
                 }
@@ -5801,7 +5821,7 @@
             if (keys(%{$usertypes}) > 0) {
                 $datatable .= &insttypes_row($createsettings,$types,$usertypes,
                                              $dom,$numinrow,$othertitle,
-                                             'statustocreate',$$rowtotal);
+                                             'statustocreate',$rowtotal);
                 $$rowtotal ++;
             }
         }
@@ -5865,225 +5885,399 @@
         }
     } else {
         my %choices = &Apache::lonlocal::texthash (
-                                                      cancreate_email => 'Non-institutional username (e-mail verification)',
+                          'cancreate_email' => 'Non-institutional username (via e-mail verification)',
                                                   );
         my @toggles = sort(keys(%choices));
         my %defaultchecked = (
                                'cancreate_email' => 'off',
                              );
-        my $itemcount = 0;
+        my $customclass = 'LC_selfcreate_email';
+        my $classprefix = 'LC_canmodify_emailusername_';
+        my $optionsprefix = 'LC_options_emailusername_';
         my $display = 'none';
+        my $rowstyle = 'display:none';
         if (grep(/^\Qemail\E$/, at selfcreate)) {
             $display = 'block';
+            $rowstyle = 'display:table-row';
         }
-        my $onclick = "toggleDisplay(this.form,'emailoptions');";
-        my $additional = '<div id="emailoptions" style="display: '.$display.'">';
+        my $onclick = "toggleRows(this.form,'cancreate_email','selfassign','$customclass','$classprefix','$optionsprefix');";
+        ($datatable,$$rowtotal) = &radiobutton_prefs(\%radiohash,\@toggles,\%defaultchecked,
+                                                     \%choices,$$rowtotal,$onclick);
+        $datatable .= &print_requestmail($dom,'selfcreation',$createsettings,$rowtotal,$customclass,
+                                         $rowstyle);
+        $$rowtotal ++;
+        $datatable .= &captcha_choice('cancreate',$createsettings,$$rowtotal,$customclass,
+                                      $rowstyle);
+        $$rowtotal ++;
+        my (@ordered, at posstypes,%usertypeshash);
         my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
-        my $order;
-        if (ref($domdefaults{'inststatusguest'}) eq 'ARRAY') {
-            $order = $domdefaults{'inststatusguest'};
-        }
-        my (@ordered,%usertypeshash);
-        if (ref($order) eq 'ARRAY') {
-            @ordered = @{$order};
-        }
-        if (@ordered) {
-            unless (grep(/^default$/, at ordered)) {
-                push(@ordered,'default');
+        my ($emailrules,$emailruleorder) =
+            &Apache::lonnet::inst_userrules($dom,'email');
+        my $primary_id = &Apache::lonnet::domain($dom,'primary');
+        my $intdom = &Apache::lonnet::internet_dom($primary_id);
+        if (ref($types) eq 'ARRAY') {
+            @posstypes = @{$types};
+        }
+        if (@posstypes) {
+            unless (grep(/^default$/, at posstypes)) {
+                push(@posstypes,'default');
             }
             if (ref($usertypes) eq 'HASH') {
                 %usertypeshash = %{$usertypes};
             }
+            my $currassign;
+            if (ref($domdefaults{'inststatusguest'}) eq 'ARRAY') {
+                $currassign = {
+                                  selfassign => $domdefaults{'inststatusguest'},
+                              };
+                @ordered = @{$domdefaults{'inststatusguest'}};
+            } else {
+                $currassign = { selfassign => [] };
+            }
+            my $onclicktypes = "toggleDataRow(this.form,'selfassign','$customclass','$optionsprefix',);".
+                               "toggleDataRow(this.form,'selfassign','$customclass','$classprefix',1);";
+            $datatable .= &insttypes_row($currassign,$types,$usertypes,$dom,
+                                         $numinrow,$othertitle,'selfassign',
+                                         $rowtotal,$onclicktypes,$customclass,
+                                         $rowstyle);
+            $$rowtotal ++;
             $usertypeshash{'default'} = $othertitle;
-            $additional .= '<table><tr>';
-            foreach my $status (@ordered) {
-                $additional .= '<th align="center">'.$usertypeshash{$status}.'</th>';
-            }
-            $additional .= '</tr><tr>';
-            foreach my $status (@ordered) {
-                $additional .= '<td align="left" valign="top">'.&validate_by_email($rowtotal,$processing,$status).'</td>';
-            }
-            $additional .= '</tr></table><br />'.
-                           '<table><tr>';
-            foreach my $status (@ordered) {
-                $additional .= '<th align="center">'.$usertypeshash{$status}.'</th>';
-            }
-            $additional .= '</tr><tr>';
-            foreach my $status (@ordered) {
-                $additional .= '<td align="left" valign="top">'.&email_as_username($rowtotal,$emailverified,$emaildomain,$status).'</td>';
+            foreach my $status (@posstypes) {
+                my $css_class;
+                if ($$rowtotal%2) {
+                    $css_class = 'LC_odd_row ';
+                }
+                $css_class .= $customclass;
+                my $rowid = $optionsprefix.$status;
+                my $hidden = 1;
+                my $currstyle = 'display:none';
+                if (grep(/^\Q$status\E$/, at ordered)) {
+                    $currstyle = $rowstyle;
+                    $hidden = 0; 
+                }
+                $datatable .= &noninst_users($processing,$emailverified,$emailoptions,$emaildomain,
+                                             $emailrules,$emailruleorder,$settings,$status,$rowid,
+                                             $usertypeshash{$status},$css_class,$currstyle,$intdom);
+                unless ($hidden) {
+                    $$rowtotal ++;
+                }
             }
-            $additional .= '</tr></table>';
-  
         } else {
+            my $css_class;
+            if ($$rowtotal%2) {
+                $css_class = 'LC_odd_row ';
+            }
+            $css_class .= $customclass;
             $usertypeshash{'default'} = $othertitle;
-            $additional .= &mt('Approval:').' '.&validate_by_email($rowtotal,$processing).'<br />'.
-                           &mt('Username:').' '.&email_as_username($rowtotal,$emailverified,$emaildomain);
+            $datatable .= &noninst_users($processing,$emailverified,$emailoptions,$emaildomain,
+                                         $emailrules,$emailruleorder,$settings,'default','',
+                                         $othertitle,$css_class,$rowstyle,$intdom);
+            $$rowtotal ++;
         }
-        $additional .= '</div>'."\n";
-
-        ($datatable,$itemcount) = &radiobutton_prefs(\%radiohash,\@toggles,\%defaultchecked,
-                                                     \%choices,$$rowtotal,$onclick,$additional);
-        $$rowtotal ++;
-        $datatable .= &print_requestmail($dom,'selfcreation',$createsettings,$rowtotal);
-        $$rowtotal ++;
         my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info();
         $numinrow = 1;
-        foreach my $status (@ordered) {
-            $datatable .= &modifiable_userdata_row('cancreate','emailusername_'.$status,$settings,
-                                                   $numinrow,$$rowtotal,\%usertypeshash,$infofields,$infotitles);
-            $$rowtotal ++;
-        }
-        my ($emailrules,$emailruleorder) =
-            &Apache::lonnet::inst_userrules($dom,'email');
-        if (ref($emailrules) eq 'HASH') {
-            if (keys(%{$emailrules}) > 0) {
-                $datatable .= &user_formats_row('email',$settings,$emailrules,
-                                                $emailruleorder,$numinrow,$$rowtotal);
-                $$rowtotal ++;
+        if (@posstypes) {
+            foreach my $status (@posstypes) {
+                my $rowid = $classprefix.$status;
+                my $datarowstyle = 'display:none';
+                if (grep(/^\Q$status\E$/, at ordered)) { 
+                    $datarowstyle = $rowstyle; 
+                }
+                $datatable .= &modifiable_userdata_row('cancreate','emailusername_'.$status,$settings,
+                                                       $numinrow,$$rowtotal,\%usertypeshash,$infofields,
+                                                       $infotitles,$rowid,$customclass,$datarowstyle);
+                unless ($datarowstyle eq 'display:none') {
+                    $$rowtotal ++;
+                }
             }
+        } else {
+            $datatable .= &modifiable_userdata_row('cancreate','emailusername_default',$settings,
+                                                   $numinrow,$$rowtotal,\%usertypeshash,$infofields,
+                                                   $infotitles,'',$customclass,$rowstyle);
         }
-        $datatable .= &captcha_choice('cancreate',$createsettings,$$rowtotal);
     }
     return $datatable;
 }
 
-sub validate_by_email {
-    my ($rowtotal,$processing,$type) = @_;
-    my %choices =
-        &Apache::lonlocal::texthash (
-                                      automatic => 'Automatic approval',
-                                      approval  => 'Queued for approval',
-                                      notinuse  => 'Unavailable',
-                                    );
-    my $output;
-    my @options = ('automatic','approval');
-    unless ($type eq '') {
-        push(@options,'notinuse');
-    }
-    foreach my $option (@options) {
-        my $checked;
-        if (ref($processing) eq 'HASH') {
-            if ($type eq '') {   
-                if (!exists($processing->{'default'})) {
-                    if ($option eq 'automatic') {
-                        $checked = ' checked="checked"';
-                    }
-                } else {
-                    if ($processing->{'default'} eq $option) {
-                        $checked = ' checked="checked"';
+sub selfcreate_javascript {
+    return <<"ENDSCRIPT";
+
+<script type="text/javascript">
+// <![CDATA[
+
+function toggleRows(form,radio,checkbox,target,prefix,altprefix) {
+    var x = document.getElementsByClassName(target);
+    var insttypes = 0;
+    var insttypeRegExp = new RegExp(prefix);
+    if ((x.length != undefined) && (x.length > 0)) {
+        if (form.elements[radio].length != undefined) {
+            for (var i=0; i<form.elements[radio].length; i++) {
+                if (form.elements[radio][i].checked) {
+                    if (form.elements[radio][i].value == 1) {
+                        for (var j=0; j<x.length; j++) {
+                            if (x[j].id == 'undefined') {
+                                x[j].style.display = 'table-row';
+                            } else if (insttypeRegExp.test(x[j].id)) {
+                                insttypes ++;
+                            } else {
+                                x[j].style.display = 'table-row';
+                            }
+                        }
+                    } else {
+                        for (var j=0; j<x.length; j++) {
+                            x[j].style.display = 'none';
+                        }
                     }
+                    break;
                 }
-            } else {
-                if (!exists($processing->{$type})) {
-                    if ($option eq 'automatic') {
-                        $checked = ' checked="checked"';
+            }
+            if (insttypes > 0) {
+                toggleDataRow(form,checkbox,target,altprefix);
+                toggleDataRow(form,checkbox,target,prefix,1);
+            }
+        }
+    }
+    return;
+}
+
+function toggleDataRow(form,checkbox,target,prefix,docount) {
+    if (form.elements[checkbox].length != undefined) {
+        var count = 0;
+        if (docount) {
+            for (var i=0; i<form.elements[checkbox].length; i++) {
+                if (form.elements[checkbox][i].checked) {
+                    count ++;
+                }
+            }
+        }
+        for (var i=0; i<form.elements[checkbox].length; i++) {
+            var type = form.elements[checkbox][i].value;
+            if (document.getElementById(prefix+type)) {
+                if (form.elements[checkbox][i].checked) {
+                    document.getElementById(prefix+type).style.display = 'table-row';
+                    if (count % 2 == 1) {
+                        document.getElementById(prefix+type).className = target+' LC_odd_row';
+                    } else {
+                        document.getElementById(prefix+type).className = target;
                     }
+                    count ++;
                 } else {
-                    if ($processing->{$type} eq $option) {
-                        $checked = ' checked="checked"';
-                    }
+                    document.getElementById(prefix+type).style.display = 'none';
                 }
             }
-        } elsif ($option eq 'automatic') {
-            $checked = ' checked="checked"'; 
-        }
-        my $name = 'cancreate_emailprocess';
-        if (($type ne '') && ($type ne 'default')) {
-            $name .= '_'.$type;
-        }
-        $output .= '<span class="LC_nobreak"><label>'.
-                   '<input type="radio" name="'.$name.'"'.
-                   $checked.' value="'.$option.'" />'.
-                   $choices{$option}.'</label></span>';
-        if ($type eq '') {
-            $output .= ' ';
-        } else {
-            $output .= '<br />';
         }
     }
-    $$rowtotal ++;
-    return $output;
+    return;
 }
 
-sub email_as_username {
-    my ($rowtotal,$emailverified,$emaildomain,$type) = @_;
-    my %choices =
-        &Apache::lonlocal::texthash (
-                                      email => 'Username is e-mail',
-                                      other => 'Username is chosen',
-                                    );
-    my $name = 'cancreate_emailverified';
-    my $condition = 'cancreate_emaildomain';
-    if (($type ne '') && ($type ne 'default')) {
-        $name .= '_'.$type;
-        $condition .= '_'.$type;
+function toggleEmailOptions(form,radio,prefix,altprefix,status) {
+    var caller = radio+'_'+status;
+    if (form.elements[caller].length != undefined) {
+        for (var i=0; i<form.elements[caller].length; i++) {
+            if (form.elements[caller][i].checked) {
+                if (document.getElementById(altprefix+'_inst_'+status)) {
+                    var curr = form.elements[caller][i].value;
+                    if (prefix) {
+                        document.getElementById(prefix+'_'+status).style.display = 'none';
+                    }
+                    document.getElementById(altprefix+'_inst_'+status).style.display = 'none';
+                    document.getElementById(altprefix+'_noninst_'+status).style.display = 'none';
+                    if (curr == 'custom') {
+                        if (prefix) { 
+                            document.getElementById(prefix+'_'+status).style.display = 'inline';
+                        }
+                    } else if (curr == 'inst') {
+                        document.getElementById(altprefix+'_inst_'+status).style.display = 'inline';
+                    } else if (curr == 'noninst') {
+                        document.getElementById(altprefix+'_noninst_'+status).style.display = 'inline';
+                    }
+                    break;
+                }
+            }
+        }
     }
-    my $display = 'none';
-    my $onclick = "toggleDisplay(this.form,'$name');";
+}
 
-    my $output;
-    foreach my $option ('email','other') {
-        my ($checked,$domain);
-        if (ref($emailverified) eq 'HASH') {
-            if ($type eq '') {
-                if (!exists($emailverified->{'default'})) {
-                    if ($option eq 'email') {
-                        $checked = ' checked="checked"';
-                    }
-                } else {
-                    if ($emailverified->{'default'} eq $option) {
-                        $checked = ' checked="checked"';
+// ]]>
+</script>
+
+ENDSCRIPT
+}
+
+sub noninst_users {
+    my ($processing,$emailverified,$emailoptions,$emaildomain,$emailrules,
+        $emailruleorder,$settings,$type,$rowid,$typetitle,$css_class,$rowstyle,$intdom) = @_; 
+    my $class = 'LC_left_item';
+    if ($css_class) {
+        $css_class = ' class="'.$css_class.'"'; 
+    }
+    if ($rowid) {
+        $rowid = ' id="'.$rowid.'"';
+    }
+    if ($rowstyle) {
+        $rowstyle = ' style="'.$rowstyle.'"';
+    }
+    my ($output,$description);
+    if ($type eq 'default') {
+        $description = &mt('Requests for: [_1]',$typetitle);
+    } else {
+        $description = &mt('Requests for: [_1] (status self-reported)',$typetitle);
+    }
+    $output = '<tr'.$css_class.$rowid.$rowstyle.'>'.
+              "<td>$description</td>\n".           
+              '<td class="'.$class.'" colspan="2">'.
+              '<table><tr>';
+    my %headers = &Apache::lonlocal::texthash( 
+              approve  => 'Processing',
+              email    => 'E-mail',
+              username => 'Username',
+    );
+    foreach my $item ('approve','email','username') {
+        $output .= '<th>'.$headers{$item}.'</th>';
+    }
+    $output .= '</tr><tr>';
+    foreach my $item ('approve','email','username') {
+        $output .= '<td valign="top">';
+        my (%choices, at options,$hashref,$defoption,$name,$onclick,$hascustom);
+        if ($item eq 'approve') {
+            %choices = &Apache::lonlocal::texthash (
+                                                     automatic => 'Automatically approved',
+                                                     approval  => 'Queued for approval',
+                                                   );
+            @options = ('automatic','approval');
+            $hashref = $processing;
+            $defoption = 'automatic';
+            $name = 'cancreate_emailprocess_'.$type;
+        } elsif ($item eq 'email') {
+            %choices = &Apache::lonlocal::texthash (
+                                                     any     => 'Any e-mail',
+                                                     inst    => 'Institutional only',
+                                                     noninst => 'Non-institutional only',
+                                                     custom  => 'Custom restrictions',
+                                                   );
+            @options = ('any','inst','noninst');
+            my $showcustom;
+            if (ref($emailrules) eq 'HASH') {
+                if (keys(%{$emailrules}) > 0) {
+                    push(@options,'custom');
+                    $showcustom = 'cancreate_emailrule';
+                    if (ref($settings) eq 'HASH') {
+                        if (ref($settings->{'email_rule'}) eq 'ARRAY') {
+                            foreach my $rule (@{$settings->{'email_rule'}}) {
+                                if (exists($emailrules->{$rule})) {
+                                    $hascustom ++;
+                                }
+                            }
+                        } elsif (ref($settings->{'email_rule'}) eq 'HASH') {
+                            if (ref($settings->{'email_rule'}{$type}) eq 'ARRAY') {
+                                foreach my $rule (@{$settings->{'email_rule'}{$type}}) {
+                                    if (exists($emailrules->{$rule})) {
+                                        $hascustom ++;
+                                    }
+                                }
+                            }
+                        }
                     }
                 }
-            } else {
-                if (!exists($emailverified->{$type})) {
-                    if ($option eq 'email') {
-                        $checked = ' checked="checked"';
+            }
+            $onclick = ' onclick="toggleEmailOptions(this.form,'."'cancreate_emailoptions','$showcustom',".
+                                                     "'cancreate_emaildomain','$type'".');"';
+            $hashref = $emailoptions;
+            $defoption = 'any';
+            $name = 'cancreate_emailoptions_'.$type;
+        } elsif ($item eq 'username') {
+            %choices = &Apache::lonlocal::texthash (
+                                                     all    => 'Same as e-mail',
+                                                     first  => 'Omit @domain',
+                                                     free   => 'Free to choose',
+                                                   );
+            @options = ('all','first','free');
+            $hashref = $emailverified;
+            $defoption = 'all';
+            $name = 'cancreate_usernameoptions_'.$type;
+        }
+        foreach my $option (@options) {
+            my $checked;
+            if (ref($hashref) eq 'HASH') {
+                if ($type eq '') {
+                    if (!exists($hashref->{'default'})) {
+                        if ($option eq $defoption) {
+                            $checked = ' checked="checked"';
+                        }
+                    } else {
+                        if ($hashref->{'default'} eq $option) {
+                            $checked = ' checked="checked"';
+                        }
                     }
                 } else {
-                    if ($emailverified->{$type} eq $option) {
-                        $checked = ' checked="checked"';
-                        if ($option eq 'other') {
-                            $display = 'inline';
+                    if (!exists($hashref->{$type})) {
+                        if ($option eq $defoption) {
+                            $checked = ' checked="checked"';
+                        }
+                    } else {
+                        if ($hashref->{$type} eq $option) {
+                            $checked = ' checked="checked"';
                         }
                     }
                 }
+            } elsif (($item eq 'email') && ($hascustom)) {
+                if ($option eq 'custom') {
+                    $checked = ' checked="checked"';
+                }
+            } elsif ($option eq $defoption) {
+                $checked = ' checked="checked"';
             }
-        } elsif ($option eq 'email') {
-            $checked = ' checked="checked"';
-        }
-        $output .= '<span class="LC_nobreak"><label>'.
-                   '<input type="radio" name="'.$name.'"'.
-                   $checked.' value="'.$option.'" onclick="'.$onclick.'" />'.
-                   $choices{$option}.'</label></span>';
-        if ($type eq '') {
-            $output .= ' ';
-        } else {
-            $output .= '<br />';
-        }
-    }
-    my $domain;
-    if (ref($emaildomain) eq 'HASH') {
-        if ($type eq '') {
-            if (exists($emaildomain->{'default'})) {
-                $domain = $emaildomain->{'default'};
-            }
-        } else {
-            if (exists($emaildomain->{$type})) {
-                $domain = $emaildomain->{$type};
+            $output .= '<span class="LC_nobreak"><label>'.
+                       '<input type="radio" name="'.$name.'"'.
+                       $checked.' value="'.$option.'"'.$onclick.' />'.
+                       $choices{$option}.'</label></span><br />';
+            if ($item eq 'email') {
+                if ($option eq 'custom') {
+                    my $id = 'cancreate_emailrule_'.$type;
+                    my $display = 'none';
+                    if ($checked) {
+                        $display = 'inline';
+                    }
+                    my $numinrow = 2;
+                    $output .= '<fieldset id="'.$id.'" style="display:'.$display.';">'.
+                               '<legend>'.&mt('Disallow').'</legend><table>'.
+                               &user_formats_row('email',$settings,$emailrules,
+                                                 $emailruleorder,$numinrow,'',$type);
+                              '</table></fieldset>';
+                } elsif (($option eq 'inst') || ($option eq 'noninst')) {
+                    my %text = &Apache::lonlocal::texthash (
+                                                             inst    => 'must end:',
+                                                             noninst => 'cannot end:',
+                                                           );
+                    my $value;
+                    if (ref($emaildomain) eq 'HASH') {
+                        if (ref($emaildomain->{$type}) eq 'HASH') {
+                            $value = $emaildomain->{$type}->{$option}; 
+                        }
+                    }
+                    if ($value eq '') {
+                        $value = '@'.$intdom;
+                    }
+                    my $condition = 'cancreate_emaildomain_'.$option.'_'.$type;
+                    my $display = 'none';
+                    if ($checked) {
+                        $display = 'inline';
+                    }
+                    $output .= '<div id="'.$condition.'" style="display:'.$display.';">'.
+                               '<span class="LC_domprefs_email">'.$text{$option}.'</span> '.
+                               '<input type="text" name="'.$condition.'" value="'.$value.'" size="10" />'.
+                               '</div>';
+                }
             }
         }
+        $output .= '</td>'."\n";
     }
-    $output .= '<div id="'.$name.'" style="display: '.$display.';">'.
-               '<span class="LC_domprefs_email">'.&mt('E-mail domain').'</span> '.
-               '<input type="text" name="'.$condition.'" value="'.$domain.'" size="10" />'.
-               '</div>';
-    $$rowtotal ++;
+    $output .= "</tr></table></td></tr>\n";
     return $output;
 }
 
 sub captcha_choice {
-    my ($context,$settings,$itemcount) = @_;
+    my ($context,$settings,$itemcount,$customcss,$rowstyle) = @_;
     my ($keyentry,$currpub,$currpriv,%checked,$rowname,$pubtext,$privtext,
         $vertext,$currver);
     my %lt = &captcha_phrases();
@@ -6116,7 +6310,20 @@
     } else {
         $checked{'original'} = ' checked="checked"';
     }
-    my $css_class = $itemcount%2?' class="LC_odd_row"':'';
+    my $css_class;
+    if ($itemcount%2) {
+        $css_class = 'LC_odd_row';
+    }
+    if ($customcss) {
+        $css_class .= " $customcss";
+    }
+    $css_class =~ s/^\s+//;
+    if ($css_class) {
+        $css_class = ' class="'.$css_class.'"';
+    }
+    if ($rowstyle) {
+        $css_class .= ' style="'.$rowstyle.'"';
+    }
     my $output = '<tr'.$css_class.'>'.
                  '<td class="LC_left_item">'.$rowname.'</td><td class="LC_left_item" colspan="2">'."\n".
                  '<table><tr><td>'."\n";
@@ -6135,7 +6342,7 @@
 # specified for use with the key should be broad enough to accommodate all servers in the LON-CAPA domain.
 #
     $output .= '</td></tr>'."\n".
-               '<tr><td>'."\n".
+               '<tr><td class="LC_zero_height">'."\n".
                '<span class="LC_nobreak"><span id="'.$context.'_recaptchapubtxt">'.$pubtext.'</span> '."\n".
                '<input type="'.$keyentry.'" id="'.$context.'_recaptchapub" name="'.$context.'_recaptchapub" value="'.
                $currpub.'" size="40" /></span><br />'."\n".
@@ -6151,23 +6358,19 @@
 }
 
 sub user_formats_row {
-    my ($type,$settings,$rules,$ruleorder,$numinrow,$rowcount) = @_;
+    my ($type,$settings,$rules,$ruleorder,$numinrow,$rowcount,$status) = @_;
     my $output;
     my %text = (
                    'username' => 'new usernames',
                    'id'       => 'IDs',
-                   'email'    => 'self-created accounts (e-mail)',
                );
-    my $css_class = $rowcount%2?' class="LC_odd_row"':'';
-    $output = '<tr '.$css_class.'>'.
-              '<td><span class="LC_nobreak">';
-    if ($type eq 'email') {
-        $output .= &mt("Formats disallowed for $text{$type}: ");
-    } else {
-        $output .= &mt("Format rules to check for $text{$type}: ");
+    unless ($type eq 'email') {
+        my $css_class = $rowcount%2?' class="LC_odd_row"':'';
+        $output = '<tr '.$css_class.'>'.
+                  '<td><span class="LC_nobreak">'.
+                  &mt("Format rules to check for $text{$type}: ").
+                  '</td><td class="LC_left_item" colspan="2"><table>';
     }
-    $output .= '</span></td>'.
-               '<td class="LC_left_item" colspan="2"><table>';
     my $rem;
     if (ref($ruleorder) eq 'ARRAY') {
         for (my $i=0; $i<@{$ruleorder}; $i++) {
@@ -6185,25 +6388,41 @@
                         if (grep(/^\Q$ruleorder->[$i]\E$/,@{$settings->{$type.'_rule'}})) {
                             $check = ' checked="checked" ';
                         }
+                    } elsif ((ref($settings->{$type.'_rule'}) eq 'HASH') && ($status ne '')) {
+                        if (ref($settings->{$type.'_rule'}->{$status}) eq 'ARRAY') {
+                            if (grep(/^\Q$ruleorder->[$i]\E$/,@{$settings->{$type.'_rule'}->{$status}})) {
+                                $check = ' checked="checked" ';
+                            }
+                        }
                     }
                 }
+                my $name = $type.'_rule';
+                if ($type eq 'email') {
+                    $name .= '_'.$status;
+                }
                 $output .= '<td class="LC_left_item">'.
                            '<span class="LC_nobreak"><label>'.
-                           '<input type="checkbox" name="'.$type.'_rule" '.
+                           '<input type="checkbox" name="'.$name.'"'.
                            'value="'.$ruleorder->[$i].'"'.$check.'/>'.
                            $rules->{$ruleorder->[$i]}{'name'}.'</label></span></td>';
             }
         }
         $rem = @{$ruleorder}%($numinrow);
     }
-    my $colsleft = $numinrow - $rem;
+    my $colsleft;
+    if ($rem) {
+        $colsleft = $numinrow - $rem;
+    }
     if ($colsleft > 1 ) {
         $output .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                    ' </td>';
     } elsif ($colsleft == 1) {
         $output .= '<td class="LC_left_item"> </td>';
     }
-    $output .= '</tr></table></td></tr>';
+    $output .= '</tr></table>';
+    unless ($type eq 'email') {
+        $output .= '</td></tr>';
+    }
     return $output;
 }
 
@@ -6425,17 +6644,12 @@
     } else {
         my %defaults;
         if (ref($settings) eq 'HASH') {
-            if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH') &&
-                (ref($settings->{'inststatusguest'}) eq 'ARRAY')) {
+            if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) {
                 my $maxnum = @{$settings->{'inststatusorder'}};
                 for (my $i=0; $i<$maxnum; $i++) {
                     $css_class = $rownum%2?' class="LC_odd_row"':'';
                     my $item = $settings->{'inststatusorder'}->[$i];
                     my $title = $settings->{'inststatustypes'}->{$item};
-                    my $guestok;
-                    if (grep(/^\Q$item\E$/,@{$settings->{'inststatusguest'}})) {
-                        $guestok = 1;
-                    }
                     my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'$item'".');"';
                     $datatable .= '<tr'.$css_class.'>'.
                                   '<td><span class="LC_nobreak">'.
@@ -6448,23 +6662,12 @@
                         }
                         $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
                     }
-                    my ($checkedon,$checkedoff);
-                    $checkedoff = ' checked="checked"';
-                    if ($guestok) {
-                        $checkedon = $checkedoff;
-                        $checkedoff = ''; 
-                    }
                     $datatable .= '</select> '.&mt('Internal ID:').' <b>'.$item.'</b> '.
                                   '<input type="checkbox" name="inststatus_delete" value="'.$item.'" />'.
                                   &mt('delete').'</span></td>'.
-                                  '<td class="LC_left_item"><span class="LC_nobreak">'.&mt('Name displayed:').
+                                  '<td class="LC_left_item" colspan="2"><span class="LC_nobreak">'.&mt('Name displayed:').
                                   '<input type="text" size="20" name="inststatus_title_'.$item.'" value="'.$title.'" />'.
-                                  '</span></td>'.
-                                  '<td class="LC_right_item"><span class="LC_nobreak">'.
-                                  '<label><input type="radio" value="1" name="inststatus_guest_'.$item.'"'.$checkedon.' />'.
-                                  &mt('Yes').'</label>'.(' 'x2).
-                                  '<label><input type="radio" value="0" name="inststatus_guest_'.$item.'"'.$checkedoff.' />'.
-                                  &mt('No').'</label></span></td></tr>';
+                                  '</span></td></tr>';
                 }
                 $css_class = $rownum%2?' class="LC_odd_row"':'';
                 my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'addinststatus_pos'".');"';
@@ -6481,14 +6684,9 @@
                 $datatable .= '</select> '.&mt('Internal ID:').
                               '<input type="text" size="10" name="addinststatus" value="" />'.
                               ' '.&mt('(new)').
-                              '</span></td><td class="LC_left_item"><span class="LC_nobreak">'.
+                              '</span></td><td class="LC_left_item" colspan="2"><span class="LC_nobreak">'.
                               &mt('Name displayed:').
                               '<input type="text" size="20" name="addinststatus_title" value="" /></span></td>'.
-                              '<td class="LC_right_item"><span class="LC_nobreak">'.
-                              '<label><input type="radio" value="1" name="addinststatus_guest" />'.
-                              &mt('Yes').'</label>'.(' 'x2).
-                              '<label><input type="radio" value="0" name="addinststatus_guest" />'.
-                              &mt('No').'</label></span></td></tr>';
                               '</tr>'."\n";
                 $rownum ++;
             }
@@ -7386,13 +7584,14 @@
 }
 
 sub modifiable_userdata_row {
-    my ($context,$item,$settings,$numinrow,$rowcount,$usertypes,$fieldsref,$titlesref) = @_;
+    my ($context,$item,$settings,$numinrow,$rowcount,$usertypes,$fieldsref,$titlesref,
+        $rowid,$customcss,$rowstyle) = @_;
     my ($role,$rolename,$statustype);
     $role = $item;
     if ($context eq 'cancreate') {
-        if ($item =~ /^emailusername_(.+)$/) {
-            $statustype = $1;
-            $role = 'emailusername';
+        if ($item =~ /^(emailusername)_(.+)$/) {
+            $role = $1;
+            $statustype = $2;
             if (ref($usertypes) eq 'HASH') {
                 if ($usertypes->{$statustype}) {
                     $rolename = &mt('Data provided by [_1]',$usertypes->{$statustype});
@@ -7427,8 +7626,25 @@
         %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();
     }
     my $output;
-    my $css_class = $rowcount%2?' class="LC_odd_row"':'';
-    $output = '<tr '.$css_class.'>'.
+    my $css_class;
+    if ($rowcount%2) {
+        $css_class = 'LC_odd_row';
+    }
+    if ($customcss) {
+        $css_class .= " $customcss";
+    }
+    $css_class =~ s/^\s+//;
+    if ($css_class) {
+        $css_class = ' class="'.$css_class.'"';
+    }
+    if ($rowstyle) {
+        $css_class .= ' style="'.$rowstyle.'"';
+    }
+    if ($rowid) {
+        $rowid = ' id="'.$rowid.'"';
+    }
+    
+    $output = '<tr '.$css_class.$rowid.'>'.
               '<td><span class="LC_nobreak">'.$rolename.'</span></td>'.
               '<td class="LC_left_item" colspan="2"><table>';
     my $rem;
@@ -7462,9 +7678,10 @@
             }
         }
     }
-     
-    for (my $i=0; $i<@fields; $i++) {
-        my $rem = $i%($numinrow);
+ 
+    my $total = scalar(@fields);
+    for (my $i=0; $i<$total; $i++) {
+        $rem = $i%($numinrow);
         if ($rem == 0) {
             if ($i > 0) {
                 $output .= '</tr>';
@@ -7506,10 +7723,13 @@
                        '</label>';
         }
         $output .= '</span></td>';
-        $rem = @fields%($numinrow);
     }
-    my $colsleft = $numinrow - $rem;
-    if ($colsleft > 1 ) {
+    $rem = $total%$numinrow;
+    my $colsleft;
+    if ($rem) {
+        $colsleft = $numinrow - $rem;
+    }
+    if ($colsleft > 1) {
         $output .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                    ' </td>';
     } elsif ($colsleft == 1) {
@@ -7520,11 +7740,13 @@
 }
 
 sub insttypes_row {
-    my ($settings,$types,$usertypes,$dom,$numinrow,$othertitle,$context,$rownum) = @_;
+    my ($settings,$types,$usertypes,$dom,$numinrow,$othertitle,$context,$rowtotal,$onclick,
+        $customcss,$rowstyle) = @_;
     my %lt = &Apache::lonlocal::texthash (
                       cansearch => 'Users allowed to search',
                       statustocreate => 'Institutional affiliation(s) able to create own account (login/SSO)',
                       lockablenames => 'User preference to lock name',
+                      selfassign    => 'Self-reportable affiliations',
              );
     my $showdom;
     if ($context eq 'cansearch') {
@@ -7534,9 +7756,22 @@
     if ($context eq 'statustocreate') {
         $class = 'LC_right_item';
     }
-    my $css_class = ' class="LC_odd_row"';
-    if ($rownum ne '') { 
-        $css_class = ($rownum%2? ' class="LC_odd_row"':'');
+    my $css_class;
+    if ($$rowtotal%2) {
+        $css_class = 'LC_odd_row';
+    }
+    if ($customcss) {
+        $css_class .= ' '.$customcss;
+    }
+    $css_class =~ s/^\s+//;
+    if ($css_class) {
+        $css_class = ' class="'.$css_class.'"';
+    }
+    if ($rowstyle) {
+        $css_class .= ' style="'.$rowstyle.'"';
+    }
+    if ($onclick) {
+        $onclick = 'onclick="'.$onclick.'" ';
     }
     my $output = '<tr'.$css_class.'>'.
                  '<td>'.$lt{$context}.$showdom.
@@ -7565,7 +7800,7 @@
                 $output .= '<td class="LC_left_item">'.
                            '<span class="LC_nobreak"><label>'.
                            '<input type="checkbox" name="'.$context.'" '.
-                           'value="'.$types->[$i].'"'.$check.'/>'.
+                           'value="'.$types->[$i].'"'.$check.$onclick.' />'.
                            $usertypes->{$types->[$i]}.'</label></span></td>';
             }
         }
@@ -7592,7 +7827,7 @@
     }
     $output .= '<span class="LC_nobreak"><label>'.
                '<input type="checkbox" name="'.$context.'" '.
-               'value="default"'.$defcheck.'/>'.
+               'value="default"'.$defcheck.$onclick.' />'.
                $othertitle.'</label></span></td>'.
                '</tr></table></td></tr>';
     return $output;
@@ -11264,16 +11499,14 @@
 }
 
 sub modify_selfcreation {
-    my ($dom,%domconfig) = @_;
-    my ($resulttext,$warningmsg,%curr_usercreation,%curr_usermodify,%changes,%cancreate);
-    my (%save_usercreate,%save_usermodify, at types,%usertypes);
+    my ($dom,$lastactref,%domconfig) = @_;
+    my ($resulttext,$warningmsg,%curr_usercreation,%curr_usermodify,%curr_inststatus,%changes,%cancreate);
+    my (%save_usercreate,%save_usermodify,%save_inststatus, at types,%usertypes);
+    my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
     my ($othertitle,$usertypesref,$typesref) = &Apache::loncommon::sorted_inst_types($dom);
     if (ref($typesref) eq 'ARRAY') {
         @types = @{$typesref};
     }
-    unless (grep(/^default$/, at types)) {
-        push(@types,'default');
-    }
     if (ref($usertypesref) eq 'HASH') {
         %usertypes = %{$usertypesref};
     }
@@ -11291,7 +11524,7 @@
                             ($item eq 'recaptchaversion') || ($item eq 'notify') ||
                             ($item eq 'emailusername') || ($item eq 'shibenv') ||
                             ($item eq 'selfcreateprocessing') || ($item eq 'emailverified') ||
-                            ($item eq 'emaildomain')) {
+                            ($item eq 'emailoptions') || ($item eq 'emaildomain')) {
                             $curr_usercreation{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};
                         } else {
                             $save_usercreate{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};
@@ -11317,14 +11550,29 @@
             }
         }
     }
+#
+# Retrieve current domain configuration for institutional status types from $domconfig{'inststatus'}.
+#
+    if (ref($domconfig{'inststatus'}) eq 'HASH') {
+        foreach my $key (keys(%{$domconfig{'inststatus'}})) {
+            if ($key eq 'inststatusguest') {
+                $curr_inststatus{$key} = $domconfig{'inststatus'}{$key};
+            } else {
+                $save_inststatus{$key} = $domconfig{'inststatus'}{$key};
+            }
+        }
+    }
 
     my @contexts = ('selfcreate');
     @{$cancreate{'selfcreate'}} = ();
     %{$cancreate{'emailusername'}} = ();
-    @{$cancreate{'statustocreate'}} = ();
+    if (@types) {
+        @{$cancreate{'statustocreate'}} = ();
+    }
     %{$cancreate{'selfcreateprocessing'}} = ();
     %{$cancreate{'shibenv'}} = ();
     %{$cancreate{'emailverified'}} = ();
+    %{$cancreate{'emailoptions'}} = ();
     %{$cancreate{'emaildomain'}} = ();
     my %selfcreatetypes = (
                              sso   => 'users authenticated by institutional single sign on',
@@ -11335,35 +11583,127 @@
 # Populate $cancreate{'selfcreate'} array reference with types of user, for which self-creation of user accounts
 # is permitted.
 #
+    my ($emailrules,$emailruleorder) = &Apache::lonnet::inst_userrules($dom,'email');
 
-    my @statuses;
-    if (ref($domconfig{'inststatus'}) eq 'HASH') {
-        if (ref($domconfig{'inststatus'}{'inststatusguest'}) eq 'ARRAY') {
-            @statuses = @{$domconfig{'inststatus'}{'inststatusguest'}};
-        }
-    }
-    push(@statuses,'default');
-
+    my (@statuses,%email_rule);
     foreach my $item ('login','sso','email') {
         if ($item eq 'email') {
             if ($env{'form.cancreate_email'}) {
-                push(@{$cancreate{'selfcreate'}},'email');
-                push(@contexts,('selfcreateprocessing','emailverified','emaildomain'));
-                foreach my $type (@statuses) {
-                    if ($type eq 'default') {
-                        $cancreate{'selfcreateprocessing'}{$type} = $env{'form.cancreate_emailprocess'};
-                        unless ($cancreate{'selfcreateprocessing'}{$type} eq 'notinuse') {
-                            $cancreate{'emailverified'}{$type} = $env{'form.cancreate_emailverified'};
-                            if ($cancreate{'emailverified'}{$type} eq 'other') {
-                                $cancreate{'emaildomain'}{$type} =$env{'form.cancreate_emaildomain'};
-                            }
-                        }
-                    } else { 
-                        $cancreate{'selfcreateprocessing'}{$type} = $env{'form.cancreate_emailprocess_'.$type};
-                        $cancreate{'emailverified'}{$type} = $env{'form.cancreate_emailverified_'.$type};
-                        if ($cancreate{'emailverified'}{$type} eq 'other') {
-                            $cancreate{'emaildomain'}{$type} =$env{'form.cancreate_emaildomain_'.$type};
+                if (@types) {
+                    my @poss_statuses = &Apache::loncommon::get_env_multiple('form.selfassign');
+                    foreach my $status (@poss_statuses) {
+                        if (grep(/^\Q$status\E$/,(@types,'default'))) {
+                            push(@statuses,$status);
+                        }
+                    }
+                    $save_inststatus{'inststatusguest'} = \@statuses;
+                } else {
+                    push(@statuses,'default');
+                }
+                if (@statuses) {
+                    my %curr_rule;
+                    if (ref($curr_usercreation{'email_rule'}) eq 'ARRAY') {
+                        foreach my $type (@statuses) {
+                            $curr_rule{$type} = $curr_usercreation{'email_rule'};
                         }
+                    } elsif (ref($curr_usercreation{'email_rule'}) eq 'HASH') {
+                        foreach my $type (@statuses) {
+                            $curr_rule{$type} = $curr_usercreation{'email_rule'}{$type};
+                        }
+                    }
+                    push(@{$cancreate{'selfcreate'}},'email');
+                    push(@contexts,('selfcreateprocessing','emailverified','emailoptions'));
+                    my %curremaildom;
+                    if (ref($curr_usercreation{'cancreate'}{'emaildomain'}) eq 'HASH') {
+                        %curremaildom = %{$curr_usercreation{'cancreate'}{'emaildomain'}};
+                    }
+                    foreach my $type (@statuses) {
+                        if ($env{'form.cancreate_emailprocess_'.$type} =~ /^(?:approval|automatic)$/) {
+                            $cancreate{'selfcreateprocessing'}{$type} = $env{'form.cancreate_emailprocess_'.$type};
+                        }
+                        if ($env{'form.cancreate_usernameoptions_'.$type} =~ /^(?:all|first|free)$/) {
+                            $cancreate{'emailverified'}{$type} = $env{'form.cancreate_usernameoptions_'.$type};
+                        }
+                        if ($env{'form.cancreate_emailoptions_'.$type} =~ /^(any|inst|noninst|custom)$/) {
+#
+# Retrieve rules (if any) governing types of e-mail address which may be used to verify a username.
+#
+                            my $chosen = $1;
+                            if (($chosen eq 'inst') || ($chosen eq 'noninst')) {
+                                my $emaildom;
+                                if ($env{'form.cancreate_emaildomain_'.$chosen.'_'.$type} =~ /^\@[^\@]+$/) {
+                                    $emaildom = $env{'form.cancreate_emaildomain_'.$chosen.'_'.$type}; 
+                                    $cancreate{'emaildomain'}{$type}{$chosen} = $emaildom;
+                                    if (ref($curremaildom{$type}) eq 'HASH') {
+                                        if (exists($curremaildom{$type}{$chosen})) {
+                                            if ($curremaildom{$type}{$chosen} ne $emaildom) {
+                                                push(@{$changes{'cancreate'}},'emaildomain');
+                                            }
+                                        } elsif ($emaildom ne '') {
+                                            push(@{$changes{'cancreate'}},'emaildomain');
+                                        }
+                                    } elsif ($emaildom ne '') {
+                                        push(@{$changes{'cancreate'}},'emaildomain');
+                                    } 
+                                }
+                                $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type};
+                            } elsif ($chosen eq 'custom') {
+                                my @possemail_rules = &Apache::loncommon::get_env_multiple('form.email_rule_'.$type);
+                                $email_rule{$type} = [];
+                                if (ref($emailrules) eq 'HASH') {
+                                    foreach my $rule (@possemail_rules) {
+                                        if (exists($emailrules->{$rule})) {
+                                            push(@{$email_rule{$type}},$rule);
+                                        }
+                                    }
+                                }
+                                if (@{$email_rule{$type}}) {
+                                    $cancreate{'emailoptions'}{$type} = 'custom';
+                                    if (ref($curr_rule{$type}) eq 'ARRAY') {
+                                        if (@{$curr_rule{$type}} > 0) {
+                                            foreach my $rule (@{$curr_rule{$type}}) {
+                                                if (!grep(/^\Q$rule\E$/,@{$email_rule{$type}})) {
+                                                    push(@{$changes{'email_rule'}},$type);
+                                                }
+                                            }
+                                        }
+                                        foreach my $type (@{$email_rule{$type}}) {
+                                            if (!grep(/^\Q$type\E$/,@{$curr_rule{$type}})) {
+                                                push(@{$changes{'email_rule'}},$type);
+                                            }
+                                        }
+                                    } else {
+                                        push(@{$changes{'email_rule'}},$type);
+                                    }
+                                }
+                            } else {
+                                $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type};
+                            }
+                        }
+                    }
+                    if (@types) {
+                        if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') {
+                            my @changed = &Apache::loncommon::compare_arrays(\@statuses,$curr_inststatus{'inststatusguest'});
+                            if (@changed) {
+                                push(@{$changes{'inststatus'}},'inststatusguest');
+                            }
+                        } else {
+                            push(@{$changes{'inststatus'}},'inststatusguest');
+                        }
+                    }
+                } else {
+                    delete($env{'form.cancreate_email'});
+                    if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') {
+                        if (@{$curr_inststatus{'inststatusguest'}} > 0) {
+                            push(@{$changes{'inststatus'}},'inststatusguest');
+                        }
+                    }
+                }
+            } else {
+                $save_inststatus{'inststatusguest'} = [];
+                if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') {
+                    if (@{$curr_inststatus{'inststatusguest'}} > 0) {
+                        push(@{$changes{'inststatus'}},'inststatusguest');
                     }
                 }
             }
@@ -11373,7 +11713,7 @@
             }
         }
     }
-    my (@email_rule,%userinfo,%savecaptcha);
+    my (%userinfo,%savecaptcha);
     my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info();
 #
 # Populate $cancreate{'emailusername'}{$type} hash ref with information fields (if new user will provide data
@@ -11382,8 +11722,8 @@
 
     if ($env{'form.cancreate_email'}) {
         push(@contexts,'emailusername');
-        if (@types) {
-            foreach my $type (@types) {
+        if (@statuses) {
+            foreach my $type (@statuses) {
                 if (ref($infofields) eq 'ARRAY') {
                     foreach my $field (@{$infofields}) {
                         if ($env{'form.canmodify_emailusername_'.$type.'_'.$field} =~ /^(required|optional)$/) {
@@ -11415,36 +11755,13 @@
             push(@{$changes{'cancreate'}},'notify');
         }
 
-#
-# Retrieve rules (if any) governing types of e-mail address which may be used to verify a username.
-#
-        @email_rule = &Apache::loncommon::get_env_multiple('form.email_rule');
         &process_captcha('cancreate',\%changes,\%savecaptcha,$curr_usercreation{'cancreate'});
-        if (ref($curr_usercreation{'email_rule'}) eq 'ARRAY') {
-            if (@{$curr_usercreation{'email_rule'}} > 0) {
-                foreach my $type (@{$curr_usercreation{'email_rule'}}) {
-                    if (!grep(/^\Q$type\E$/, at email_rule)) {
-                        push(@{$changes{'email_rule'}},$type);
-                    }
-                }
-            }
-            if (@email_rule > 0) {
-                foreach my $type (@email_rule) {
-                    if (!grep(/^\Q$type\E$/,@{$curr_usercreation{'email_rule'}})) {
-                        push(@{$changes{'email_rule'}},$type);
-                    }
-                }
-            }
-        } elsif (@email_rule > 0) {
-            push(@{$changes{'email_rule'}}, at email_rule);
-        }
     }
 #  
 # Check if domain default is set appropriately, if self-creation of accounts is to be available for
 # institutional log-in.
 #
     if (grep(/^login$/,@{$cancreate{'selfcreate'}})) {
-        my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
         if (!((($domdefaults{'auth_def'} =~/^krb/) && ($domdefaults{'auth_arg_def'} ne '')) || 
                ($domdefaults{'auth_def'} eq 'localauth'))) {
             $warningmsg = &mt('Although account creation has been set to be available for institutional logins, currently default authentication in this domain has not been set to support this.').' '.
@@ -11464,12 +11781,8 @@
 #
     if (($env{'form.cancreate_login'}) || ($env{'form.cancreate_sso'})) {
         if (@types) {
-            if (@types > 1) {
-                @{$cancreate{'statustocreate'}} = &Apache::loncommon::get_env_multiple('form.statustocreate');
-                push(@contexts,'statustocreate');
-            } else {
-                undef($cancreate{'statustocreate'});
-            } 
+            @{$cancreate{'statustocreate'}} = &Apache::loncommon::get_env_multiple('form.statustocreate');
+            push(@contexts,'statustocreate');
             foreach my $type (@types) {
                 my @modifiable =  &Apache::loncommon::get_env_multiple('form.canmodify_'.$type);
                 foreach my $field (@fields) {
@@ -11542,34 +11855,28 @@
             }
         } elsif (ref($curr_usercreation{'cancreate'}{$item}) eq 'HASH') {
             if (ref($cancreate{$item}) eq 'HASH') {
-                foreach my $curr (keys(%{$curr_usercreation{'cancreate'}{$item}})) {
-                    if (ref($curr_usercreation{'cancreate'}{$item}{$curr}) eq 'HASH') {
-                        foreach my $field (keys(%{$curr_usercreation{'cancreate'}{$item}{$curr}})) {
-                            unless ($curr_usercreation{'cancreate'}{$item}{$curr}{$field} eq $cancreate{$item}{$curr}{$field}) {
+                foreach my $type (keys(%{$curr_usercreation{'cancreate'}{$item}})) {
+                    if (ref($curr_usercreation{'cancreate'}{$item}{$type}) eq 'HASH') {
+                        foreach my $field (keys(%{$curr_usercreation{'cancreate'}{$item}{$type}})) {
+                            unless ($curr_usercreation{'cancreate'}{$item}{$type}{$field} eq $cancreate{$item}{$type}{$field}) {
                                 if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                     push(@{$changes{'cancreate'}},$item);
                                 }
                             }
                         }
-                    } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emaildomain')) {
-                        if ($cancreate{$item}{$curr} ne $curr_usercreation{'cancreate'}{$item}{$curr}) {
-                            if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
-                                push(@{$changes{'cancreate'}},$item);
-                            }
-                        }
-                    } else {
-                        if (!$cancreate{$item}{$curr}) {
+                    } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) {
+                        if ($cancreate{$item}{$type} ne $curr_usercreation{'cancreate'}{$item}{$type}) {
                             if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                 push(@{$changes{'cancreate'}},$item);
                             }
                         }
                     }
                 }
-                foreach my $field (keys(%{$cancreate{$item}})) {
-                    if (ref($cancreate{$item}{$field}) eq 'HASH') {
-                        foreach my $inner (keys(%{$cancreate{$item}{$field}})) {
-                            if (ref($curr_usercreation{'cancreate'}{$item}{$field}) eq 'HASH') {
-                                unless ($curr_usercreation{'cancreate'}{$item}{$field}{$inner} eq $cancreate{$item}{$field}{$inner}) {
+                foreach my $type (keys(%{$cancreate{$item}})) {
+                    if (ref($cancreate{$item}{$type}) eq 'HASH') {
+                        foreach my $field (keys(%{$cancreate{$item}{$type}})) {
+                            if (ref($curr_usercreation{'cancreate'}{$item}{$type}) eq 'HASH') {
+                                unless ($curr_usercreation{'cancreate'}{$item}{$type}{$field} eq $cancreate{$item}{$type}{$field}) {
                                     if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                         push(@{$changes{'cancreate'}},$item);
                                     }
@@ -11580,14 +11887,8 @@
                                 }
                             }
                         }
-                    } elsif ($item eq 'selfcreateprocessing') {
-                        if ($cancreate{$item}{$field} ne $curr_usercreation{'cancreate'}{$item}{$field}) {
-                            if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
-                                push(@{$changes{'cancreate'}},$item);
-                            }
-                        }
-                    } else {
-                        if (!$curr_usercreation{'cancreate'}{$item}{$field}) {
+                    } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) {
+                        if ($cancreate{$item}{$type} ne $curr_usercreation{'cancreate'}{$item}{$type}) {
                             if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                 push(@{$changes{'cancreate'}},$item);
                             }
@@ -11602,11 +11903,11 @@
                         push(@{$changes{'cancreate'}},$item);
                     }
                 }
-            } elsif (ref($cancreate{$item}) eq 'HASH') {
-                if (!$cancreate{$item}{$curr_usercreation{'cancreate'}{$item}}) {
-                    if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
-                        push(@{$changes{'cancreate'}},$item);
-                    }
+            }
+        } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) {
+            if (ref($cancreate{$item}) eq 'HASH') {
+                if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
+                    push(@{$changes{'cancreate'}},$item);
                 }
             }
         } elsif ($item eq 'emailusername') {
@@ -11642,6 +11943,9 @@
     if (ref($cancreate{'emailverified'}) eq 'HASH') {
         $save_usercreate{'cancreate'}{'emailverified'} = $cancreate{'emailverified'};
     }
+    if (ref($cancreate{'emailoptions'}) eq 'HASH') {
+        $save_usercreate{'cancreate'}{'emailoptions'} = $cancreate{'emailoptions'};
+    }
     if (ref($cancreate{'emaildomain'}) eq 'HASH') {
         $save_usercreate{'cancreate'}{'emaildomain'} = $cancreate{'emaildomain'};
     }
@@ -11652,16 +11956,18 @@
         $save_usercreate{'cancreate'}{'shibenv'} = $cancreate{'shibenv'};
     }
     $save_usercreate{'cancreate'}{'emailusername'} = $cancreate{'emailusername'};
-    $save_usercreate{'emailrule'} = \@email_rule;
+    $save_usercreate{'email_rule'} = \%email_rule;
 
     my %userconfig_hash = (
             usercreation     => \%save_usercreate,
             usermodification => \%save_usermodify,
+            inststatus       => \%save_inststatus,
     );
+
     my $putresult = &Apache::lonnet::put_dom('configuration',\%userconfig_hash,
                                              $dom);
 #
-# Accumulate details of changes to domain cofiguration for self-creation of usernames in $resulttext
+# Accumulate details of changes to domain configuration for self-creation of usernames in $resulttext
 #
     if ($putresult eq 'ok') {
         if (keys(%changes) > 0) {
@@ -11691,19 +11997,11 @@
                                     }
                                 }
                                 if (grep(/^email$/,@{$cancreate{$type}})) {
-                                    if (@statuses > 1) {
-                                        my $numtypes = 0;
-                                        foreach my $type (@statuses) {
-                                            unless ($cancreate{'selfcreateprocessing'}{$type} eq 'notinuse') {
-                                                $numtypes ++;
-                                            }
-                                        }
-                                        if (!$numtypes) {
-                                            $chgtext .= '<span class="LC_warning">'.
-                                                        &mt("However, e-mail verification is currently set to 'unavailable' for all user types (including 'other'), so self-creation of accounts is not possible for non-institutional log-in.").
-                                                        '</span><br />';
+                                    if (!@statuses) {
+                                        $chgtext .= '<span class="LC_warning">'.
+                                                    &mt("However, e-mail verification is currently set to 'unavailable' for all user types (including 'other'), so self-creation of accounts is not possible for non-institutional log-in.").
+                                                    '</span><br />';
 
-                                        }
                                     }
                                 }
                             }
@@ -11770,23 +12068,16 @@
                         my %choices = &Apache::lonlocal::texthash (
                                                                     automatic => 'Automatic approval',
                                                                     approval  => 'Queued for approval',
-                                                                    notinuse  => 'Unavailable',
                                                                   );
-                        if (@statuses > 1) {
-                            my $numtypes = 0;
-                            foreach my $type (@statuses) {
-                                unless ($cancreate{'selfcreateprocessing'}{$type} eq 'notinuse') {
-                                    $numtypes ++;
-                                }
-                            }
-                            if ($numtypes) {
+                        if (@types) {
+                            if (@statuses) {
                                 $chgtext .= &mt('Processing of requests to create account with e-mail verification set as follows:'). 
-                                            '<ul>';
-                                foreach my $type (@statuses) {
-                                    if ($type eq 'default') {
-                                        $chgtext .= '<li>'.$othertitle.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$type}}.'</li>';
+                                                '<ul>';
+                                foreach my $status (@statuses) {
+                                    if ($status eq 'default') {
+                                        $chgtext .= '<li>'.$othertitle.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$status}}.'</li>';
                                     } else {
-                                        $chgtext .= '<li>'.$usertypes{$type}.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$type}}.'</li>';
+                                        $chgtext .= '<li>'.$usertypes{$status}.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$status}}.'</li>';
                                     }
                                 }
                                 $chgtext .= '</ul>';
@@ -11797,77 +12088,104 @@
                         }
                     } elsif ($type eq 'emailverified') {
                         my %options = &Apache::lonlocal::texthash (
-                                                                    email => 'Username is e-mail',
-                                                                    other => 'Username is chosen',
+                                                                    all   => 'Same as e-mail',
+                                                                    first => 'Omit @domain',
+                                                                    free  => 'Free to choose',
                                                                   );
-                        if (@statuses > 1) {
-                            my $numtypes = 0;
-                            foreach my $type (@statuses) {
-                                unless ($cancreate{'selfcreateprocessing'}{$type} eq 'notinuse') {
-                                    $numtypes ++;
-                                }
-                            }
-                            if ($numtypes) {
+                        if (@types) {
+                            if (@statuses) {
                                 $chgtext .= &mt('For self-created accounts verified by e-mail address, username is set as follows:').
                                             '<ul>';
-                                foreach my $type (@statuses) {
-                                    next if ($cancreate{'selfcreateprocessing'}{$type} eq 'notinuse'); 
+                                foreach my $status (@statuses) {
                                     if ($type eq 'default') {
-                                        $chgtext .= '<li>'.$othertitle.' -- '.$options{$cancreate{'emailverified'}{$type}}.'</li>';
+                                        $chgtext .= '<li>'.$othertitle.' -- '.$options{$cancreate{'emailverified'}{$status}}.'</li>';
                                     } else {
-                                        $chgtext .= '<li>'.$usertypes{$type}.' -- '.$options{$cancreate{'emailverified'}{$type}}.'</li>';
+                                        $chgtext .= '<li>'.$usertypes{$status}.' -- '.$options{$cancreate{'emailverified'}{$status}}.'</li>';
                                     }
                                 }
                                 $chgtext .= '</ul>';
                             }
                         } else {
-                            $chgtext .= &mt('For self-created accounts verified by e-mail address: "[_1]"',
+                            $chgtext .= &mt("For self-created accounts verified by e-mail address, user's username is: '[_1]'",
                                             $options{$cancreate{'emailverified'}{'default'}});
                         }
-                    } elsif ($type eq 'emaildomain') {
-                        if (@statuses > 1) {
-                            my $numtypes = 0;
-                            foreach my $type (@statuses) {
-                                unless ($cancreate{'selfcreateprocessing'}{$type} eq 'notinuse') {
-                                    if ($cancreate{'emailverified'}{$type} eq 'other') {
-                                        $numtypes ++;
-                                    }
-                                }
-                            }
-                            if ($numtypes) {                     
-                                $chgtext .= &mt('For self-created accounts verified by e-mail address, with username chosen by user ...').
+                    } elsif ($type eq 'emailoptions') {
+                        my %options = &Apache::lonlocal::texthash (
+                                                                    any     => 'Any e-mail',
+                                                                    inst    => 'Institutional only',
+                                                                    noninst => 'Non-institutional only',
+                                                                    custom  => 'Custom restrictions',
+                                                                  );
+                        if (@types) {
+                            if (@statuses) {
+                                $chgtext .= &mt('For self-created accounts verified by e-mail address, requirements for e-mail address are as follows:').
                                             '<ul>';
-                                foreach my $type (@statuses) {
-                                    next if (($cancreate{'selfcreateprocessing'}{$type} eq 'notinuse') ||
-                                             ($cancreate{'emailverified'}{$type} eq 'email'));
+                                foreach my $status (@statuses) {
                                     if ($type eq 'default') {
-                                        if ($cancreate{'emaildomain'}{$type} eq '') {
-                                            $chgtext .= '<li>'.$othertitle.' -- '.&mt('No restriction on e-mail domain').'</li>';
-                                        } else {
-                                            $chgtext .= '<li>'.$othertitle.' -- '.&mt("User's e-mail domain needs to be: [_1]",
-                                                                                      $cancreate{'emaildomain'}{$type}).'</li>';
-                                        }
+                                        $chgtext .= '<li>'.$othertitle.' -- '.$options{$cancreate{'emailoptions'}{$status}}.'</li>';
                                     } else {
-                                        if ($cancreate{'emaildomain'}{$type} eq '') {
-                                            $chgtext .= '<li>'.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'</li>';
-                                        } else {
-                                            $chgtext .= '<li>'.$usertypes{$type}.' -- '.&mt("User's e-mail domain needs to be: [_1]",
-                                                                                            $cancreate{'emaildomain'}{$type}).'</li>'; 
-                                        }
+                                        $chgtext .= '<li>'.$usertypes{$status}.' -- '.$options{$cancreate{'emailoptions'}{$status}}.'</li>';
                                     }
                                 }
                                 $chgtext .= '</ul>';
                             }
                         } else {
-                            if ($cancreate{'emailverified'}{'default'} eq 'other') {
-                                if ($cancreate{'emaildomain'}{'default'} eq '') {
-                                    $chgtext .= &mt('No restriction on e-mail domain for self-created accounts verified by e-mail address, where username is chosen by user.');
-                                } else {
-                                    $chgtext .= &mt('For self-created accounts verified by e-mail address, where username is chosen by user, e-mail domain needs to be: [_1]',
-                                                    $cancreate{'selfcreateprocessing'}{'default'});
+                            if ($cancreate{'emailoptions'}{'default'} eq 'any') {
+                                $chgtext .= &mt('For self-created accounts verified by e-mail address, any e-mail may be used');
+                            } else {
+                                $chgtext .= &mt('For self-created accounts verified by e-mail address, e-mail restricted to: "[_1]"',
+                                                $options{$cancreate{'emailoptions'}{'default'}});
+                            }
+                        }
+                    } elsif ($type eq 'emaildomain') {
+                        my $output;
+                        if (@statuses) {
+                            foreach my $type (@statuses) {
+                                if (ref($cancreate{'emaildomain'}{$type}) eq 'HASH') {
+                                    if ($cancreate{'emailoptions'}{$type} eq 'inst') {
+                                        if ($type eq 'default') {
+                                            if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
+                                                ($cancreate{'emaildomain'}{$type}{'inst'} eq '')) {
+                                                $output = '<li>'.$othertitle.' -- '.&mt('No restriction on e-mail domain').'</li>';
+                                            } else {
+                                                $output = '<li>'.$othertitle.' -- '.&mt("User's e-mail address needs to end: [_1]",
+                                                                                        $cancreate{'emaildomain'}{$type}{'inst'}).'</li>';
+                                            }
+                                        } else {
+                                            if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
+                                                ($cancreate{'emaildomain'}{$type}{'inst'} eq '')) {
+                                                $output = '<li>'.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'</li>';
+                                            } else {
+                                                $output = '<li>'.$usertypes{$type}.' -- '.&mt("User's e-mail address needs to end: [_1]",
+                                                                                              $cancreate{'emaildomain'}{$type}{'inst'}).'</li>'; 
+                                            }
+                                        }
+                                    } elsif ($cancreate{'emailoptions'}{$type} eq 'noninst') {
+                                        if ($type eq 'default') {
+                                            if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
+                                                ($cancreate{'emaildomain'}{$type}{'noninst'} eq '')) {
+                                                $output = '<li>'.$othertitle.' -- '.&mt('No restriction on e-mail domain').'</li>';
+                                            } else {
+                                                $output = '<li>'.$othertitle.' -- '.&mt("User's e-mail address must not end: [_1]",
+                                                                                        $cancreate{'emaildomain'}{$type}{'noninst'}).'</li>';
+                                            }
+                                        } else {
+                                            if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
+                                                ($cancreate{'emaildomain'}{$type}{'noninst'} eq '')) {
+                                                $output = '<li>'.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'</li>';
+                                            } else {
+                                                $output = '<li>'.$usertypes{$type}.' -- '.&mt("User's e-mail address must not end: [_1]",
+                                                                                                $cancreate{'emaildomain'}{$type}{'noninst'}).'</li>';   
+                                            }
+                                        }
+                                    }
                                 }
                             }
                         }
+                        if ($output ne '') {
+                            $chgtext .= &mt('For self-created accounts verified by e-mail address:').
+                                        '<ul>'.$output.'</ul>';
+                        }
                     } elsif ($type eq 'captcha') {
                         if ($savecaptcha{$type} eq 'notused') {
                             $chgtext .= &mt('No CAPTCHA validation in use for self-creation screen.');
@@ -11903,8 +12221,8 @@
                         }
                     } elsif ($type eq 'emailusername') {
                         if (ref($cancreate{'emailusername'}) eq 'HASH') {
-                            if (@types) {
-                                foreach my $type (@types) {
+                            if (@statuses) {
+                                foreach my $type (@statuses) {
                                     if (ref($cancreate{'emailusername'}{$type}) eq 'HASH') {
                                         if (keys(%{$cancreate{'emailusername'}{$type}}) > 0) {
                                             $chgtext .= &mt('When self-creating account with e-mail verification, the following information will be provided by [_1]:',"'$usertypes{$type}'").
@@ -11943,25 +12261,60 @@
                     }
                 }
             }
-            if (ref($changes{'email_rule'}) eq 'ARRAY') {
+            if ((ref($changes{'email_rule'}) eq 'ARRAY') && (@{$changes{'email_rule'}} > 0)) {
                 my ($emailrules,$emailruleorder) =
                     &Apache::lonnet::inst_userrules($dom,'email');
-                my $chgtext = '<ul>';
-                foreach my $type (@email_rule) {
-                    if (ref($emailrules->{$type}) eq 'HASH') {
-                        $chgtext .= '<li>'.$emailrules->{$type}{'name'}.'</li>';
+                foreach my $type (@{$changes{'email_rule'}}) {
+                    if (ref($email_rule{$type}) eq 'ARRAY') {
+                        my $chgtext = '<ul>';
+                        foreach my $rule (@{$email_rule{$type}}) {
+                            if (ref($emailrules->{$rule}) eq 'HASH') {
+                                $chgtext .= '<li>'.$emailrules->{$rule}{'name'}.'</li>';
+                            }
+                        }
+                        $chgtext .= '</ul>';
+                        if (@types) {
+                            my $typename;
+                            if ($type eq 'default') {
+                                $typename = $othertitle;
+                            } else {
+                                $typename = $usertypes{$type};
+                            } 
+                            $chgtext .= &mt('(Affiliation: [_1])',$typename);
+                        }
+                        if (@{$email_rule{$type}} > 0) {
+                            $resulttext .= '<li>'.
+                                           &mt('Accounts may not be created by users verified by e-mail, for e-mail addresses of the following types: ',
+                                               $usertypes{$type}).
+                                           $chgtext.
+                                           '</li>';
+                        } else {
+                            $resulttext .= '<li>'.
+                                           &mt('There are now no restrictions on e-mail addresses which may be used for verification when a user requests an account.',
+                                                $usertypes{$type}).
+                                           '</li>'.
+                                           &mt('(Affiliation: [_1])',$type);
+                        }
                     }
                 }
-                $chgtext .= '</ul>';
-                if (@email_rule > 0) {
-                    $resulttext .= '<li>'.
-                                   &mt('Accounts may not be created by users verified by e-mail, for e-mail addresses of the following types: ').
-                                       $chgtext.
-                                   '</li>';
-                } else {
-                    $resulttext .= '<li>'.
-                                   &mt('There are now no restrictions on e-mail addresses which may be used for verification when self-enrolling.').
-                                   '</li>';
+            }
+            if (ref($changes{'inststatus'}) eq 'ARRAY') {
+                if (ref($save_inststatus{'inststatusguest'}) eq 'ARRAY') {
+                    if (@{$save_inststatus{'inststatusguest'}} > 0) {
+                        my $chgtext = '<ul>';
+                        foreach my $type (@{$save_inststatus{'inststatusguest'}}) {
+                            $chgtext .= '<li>'.$usertypes{$type}.'</li>';
+                        }
+                        $chgtext .= '</ul>';
+                        $resulttext .= '<li>'.
+                                       &mt('A user will self-report one of the following affiliations when requesting an account verified by e-mail: ').
+                                          $chgtext.
+                                       '</li>';
+                    } else {
+                        $resulttext .= '<li>'.
+                                       &mt('No affiliations available for self-reporting when requesting an account verified by e-mail.').
+                                       '</li>';
+                    }
                 }
             }
             if (ref($changes{'selfcreate'}) eq 'ARRAY') {
@@ -11994,6 +12347,12 @@
                 $resulttext .= '</ul></li>';
             }
             $resulttext .= '</ul>';
+            my $cachetime = 24*60*60;
+            $domdefaults{'inststatusguest'} = $save_inststatus{'inststatusguest'};
+            &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
+            if (ref($lastactref) eq 'HASH') {
+                $lastactref->{'domdefaults'} = 1;
+            }
         } else {
             $resulttext = &mt('No changes made to self-creation settings');
         }
@@ -12277,9 +12636,16 @@
     }
     my @todelete = &Apache::loncommon::get_env_multiple('form.inststatus_delete');
     my @allpos;
-    my %guests;
     my %alltypes;
-    my ($currtitles,$currguests,$currorder);
+    my @inststatusguest;
+    if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {
+        foreach my $type (@{$currinststatus->{'inststatusguest'}}) {
+            unless (grep(/^\Q$type\E$/, at todelete)) {
+                push(@inststatusguest,$type);
+            }
+        }
+    }
+    my ($currtitles,$currorder);
     if (ref($currinststatus) eq 'HASH') {
         if (ref($currinststatus->{'inststatusorder'}) eq 'ARRAY') {
             foreach my $type (@{$currinststatus->{'inststatusorder'}}) {
@@ -12294,14 +12660,8 @@
                     $allpos[$position] = $type;
                     $alltypes{$type} = $env{'form.inststatus_title_'.$type};
                     $alltypes{$type} =~ s/`//g;
-                    if ($env{'form.inststatus_guest_'.$type}) {
-                        $guests{$type} = 1;
-                    }
                 }
             }
-            if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {
-                $currguests = join(',',@{$currinststatus->{'inststatusguest'}});
-            }
             $currorder = join(',',@{$currinststatus->{'inststatusorder'}});
             $currtitles =~ s/,$//;
         }
@@ -12310,9 +12670,6 @@
         my $newtype = $env{'form.addinststatus'};
         $newtype =~ s/\W//g;
         unless (exists($alltypes{$newtype})) {
-            if ($env{'form.addinststatus_guest'}) {
-                $guests{$newtype} = 1;
-            }
             $alltypes{$newtype} = $env{'form.addinststatus_title'};
             $alltypes{$newtype} =~ s/`//g; 
             my $position = $env{'form.addinststatus_pos'};
@@ -12322,13 +12679,10 @@
             }
         }
     }
-    my (@orderedstatus, at orderedguests);
+    my @orderedstatus;
     foreach my $type (@allpos) {
         unless (($type eq '') || (grep(/^\Q$type\E$/, at orderedstatus))) {
             push(@orderedstatus,$type);
-            if ($guests{$type}) {
-                push(@orderedguests,$type);
-            }
         }
     }
     foreach my $type (keys(%alltypes)) {
@@ -12339,7 +12693,7 @@
     $defaults_hash{'inststatus'} = {
                                      inststatustypes => \%alltypes,
                                      inststatusorder => \@orderedstatus,
-                                     inststatusguest => \@orderedguests,
+                                     inststatusguest => \@inststatusguest,
                                    };
     if (ref($defaults_hash{'inststatus'}) eq 'HASH') {
         foreach my $item ('inststatustypes','inststatusorder','inststatusguest') {
@@ -12349,9 +12703,6 @@
     if ($currorder ne join(',', at orderedstatus)) {
         $changes{'inststatus'}{'inststatusorder'} = 1;
     }
-    if ($currguests ne join(',', at orderedguests)) {
-        $changes{'inststatus'}{'inststatusguest'} = 1;
-    }
     my $newtitles;
     foreach my $item (@orderedstatus) {
         $newtitles .= $alltypes{$item}.',';
@@ -12370,26 +12721,15 @@
             foreach my $item (sort(keys(%changes))) {
                 if ($item eq 'inststatus') {
                     if (ref($changes{'inststatus'}) eq 'HASH') {
-                        if (($changes{'inststatus'}{'inststatustypes'}) || $changes{'inststatus'}{'inststatusorder'}) {
+                        if (@orderedstatus) {
                             $resulttext .= '<li>'.&mt('Institutional user status types set to:').' ';
                             foreach my $type (@orderedstatus) { 
                                 $resulttext .= $alltypes{$type}.', ';
                             }
                             $resulttext =~ s/, $//;
                             $resulttext .= '</li>';
-                        }
-                        if ($changes{'inststatus'}{'inststatusguest'}) {
-                            $resulttext .= '<li>'; 
-                            if (@orderedguests) {
-                                $resulttext .= &mt('Types assignable to "non-institutional" usernames set to:').' ';
-                                foreach my $type (@orderedguests) {
-                                    $resulttext .= $alltypes{$type}.', ';
-                                }
-                                $resulttext =~ s/, $//;
-                            } else {
-                                $resulttext .= &mt('Types assignable to "non-institutional" usernames set to none.');
-                            }
-                            $resulttext .= '</li>';
+                        } else {
+                            $resulttext .= '<li>'.&mt('Institutional user status types deleted').'</li>'; 
                         }
                     }
                 } else {
@@ -13893,12 +14233,12 @@
                         $resulttext .= '</ul></li>'; 
                     }
                 }
-                if ((exists($changes{'admin'})) || (exists($changes{'default'}))) {
-                    my $cachetime = 24*60*60;
-                    &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
-                    if (ref($lastactref) eq 'HASH') {
-                        $lastactref->{'domdefaults'} = 1;
-                    }
+            }
+            if ((exists($changes{'admin'})) || (exists($changes{'default'}))) {
+                my $cachetime = 24*60*60;
+                &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
+                if (ref($lastactref) eq 'HASH') {
+                    $lastactref->{'domdefaults'} = 1;
                 }
             }
             $resulttext .= '</ul>';
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1285 loncom/interface/loncommon.pm:1.1286
--- loncom/interface/loncommon.pm:1.1285	Thu Jul 13 15:29:56 2017
+++ loncom/interface/loncommon.pm	Thu Aug  3 16:22:09 2017
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1285 2017/07/13 15:29:56 raeburn Exp $
+# $Id: loncommon.pm,v 1.1286 2017/08/03 16:22:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -6452,6 +6452,11 @@
   background: $tabbg;
 }
 
+td.LC_zero_height {
+  line-height: 0; 
+  cellpadding: 0;
+}
+
 table.LC_data_table {
   border: 1px solid #000000;
   border-collapse: separate;
@@ -7134,6 +7139,7 @@
   color: #440055;  
 }
 
+.LC_domprefs_email,
 .LC_docs_alias_name,
 .LC_docs_reinit_warn,
 .LC_docs_ext_edit {
Index: loncom/interface/lonconfigsettings.pm
diff -u loncom/interface/lonconfigsettings.pm:1.35 loncom/interface/lonconfigsettings.pm:1.36
--- loncom/interface/lonconfigsettings.pm:1.35	Thu Jul 27 13:45:12 2017
+++ loncom/interface/lonconfigsettings.pm	Thu Aug  3 16:22:09 2017
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Handler to set domain-wide configuration settings
 #
-# $Id: lonconfigsettings.pm,v 1.35 2017/07/27 13:45:12 raeburn Exp $
+# $Id: lonconfigsettings.pm,v 1.36 2017/08/03 16:22:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -185,20 +185,30 @@
             }
             if (grep(/^selfcreation$/, at actions)) {
                 my $prefix = 'cancreate_emailverified';
-                my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
-                if (ref($domdefaults{'inststatusguest'}) eq 'ARRAY') {
-                    my $order = $domdefaults{'inststatusguest'};
-                    if (ref($order) eq 'ARRAY') {
-                        foreach my $type (@{$order}) {
-                            if (($type ne '') && ($type ne 'default')) {
-                                my $name = $prefix.'_'.$type;
-                                $onload .= "toggleDisplay(document.display,'$name');";
-                            }
-                        }
+                my $customclass = 'LC_selfcreate_email';
+                my $classprefix = 'LC_canmodify_emailusername_';
+                my $optionsprefix = 'LC_options_emailusername_';
+                $onload .= "toggleRows(document.display,'cancreate_email','selfassign','$customclass','$classprefix','$optionsprefix');";
+                my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
+                my $hascustom;
+                my ($emailrules,$emailruleorder) = &Apache::lonnet::inst_userrules($dom,'email');
+                if (ref($emailrules) eq 'HASH') {
+                    if (keys(%{$emailrules}) > 0) {
+                        $hascustom = 'cancreate_emailrule';
                     }
                 }
-                $onload .= "toggleDisplay(document.display,'$prefix');".
-                           "toggleDisplay(document.display,'emailoptions');";
+                my @posstypes;
+                if (ref($types) eq 'ARRAY') {
+                    @posstypes = @{$types};
+                    push(@posstypes,'default');
+                    foreach my $type (@posstypes) {
+                        $onload .= "toggleEmailOptions(document.display,'cancreate_emailoptions','$hascustom',".
+                                                               "'cancreate_emaildomain','$type');";
+                    }
+                } else {
+                    $onload .= "toggleEmailOptions(document.display,'cancreate_emailoptions','$hascustom',".
+                                                       "'cancreate_emaildomain','default');";
+                }
             }
             if ($onload) {
                 my %loaditems = (
Index: loncom/interface/loncreateuser.pm
diff -u loncom/interface/loncreateuser.pm:1.442 loncom/interface/loncreateuser.pm:1.443
--- loncom/interface/loncreateuser.pm:1.442	Wed Jul 26 19:45:45 2017
+++ loncom/interface/loncreateuser.pm	Thu Aug  3 16:22:09 2017
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Create a user
 #
-# $Id: loncreateuser.pm,v 1.442 2017/07/26 19:45:45 raeburn Exp $
+# $Id: loncreateuser.pm,v 1.443 2017/08/03 16:22:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -2352,7 +2352,7 @@
 
 sub personal_data_display {
     my ($ccuname,$ccdomain,$newuser,$context,$inst_results,$rolesarray,
-        $now,$captchaform,$emailusername,$usertype,$usernameset,$condition) = @_;
+        $now,$captchaform,$emailusername,$usertype,$usernameset,$condition,$excluded) = @_;
     my ($output,%userenv,%canmodify,%canmodify_status);
     my @userinfo = ('firstname','middlename','lastname','generation',
                     'permanentemail','id');
@@ -2428,16 +2428,38 @@
     $output = '<h3>'.$lt{'pd'}.'</h3>'.
               &Apache::lonhtmlcommon::start_pick_box();
     if (($context eq 'selfcreate') && ($newuser eq 'email')) {
+        my $size = 25;
         if ($condition) {
-            if ($condition =~ /[-\w]+\.[-\w]+$/) {
-                $condition = '@'.$condition;
+            if ($condition =~ /^\@[^\@]+$/) {
+                $size = 10;
             } else {
                 undef($condition);
             }
+        } 
+        if ($excluded) {
+            unless ($excluded =~ /^\@[^\@]+$/) {
+                undef($condition);
+            }
         }
         $output .= &Apache::lonhtmlcommon::row_title($lt{'email'}.'<b>*</b>',undef,
                                                      'LC_oddrow_value')."\n".
-                   '<input type="text" name="uname" size="25" value="'.$condition.'" autocomplete="off" />';
+                   '<input type="text" name="uname" size="'.$size.'" value="" autocomplete="off" />';
+        if ($condition) {
+            $output .= $condition;
+        } elsif ($excluded) {
+            $output .= '<br /><span style="font-size: smaller">'.&mt('You must use an e-mail address that does not end with [_1]',
+                                                                     $excluded).'</span>';
+        }
+        if ($usernameset eq 'first') {
+            $output .= '<br /><span style="font-size: smaller">';
+            if ($condition) {
+                $output .= &mt('Your username in LON-CAPA will be the part of your e-mail address before [_1]',
+                                      $condition);
+            } else {
+                $output .= &mt('Your username in LON-CAPA will be the part of your e-mail address before the @');
+            }
+            $output .= '</span>';
+        }
         $rowcount ++;
         $output .= &Apache::lonhtmlcommon::row_closure(1);
         my $upassone = '<input type="password" name="upass'.$now.'" size="20" autocomplete="off" />';
@@ -2452,8 +2474,8 @@
                                                      'LC_oddrow_value')."\n".
                    $upasstwo.
                    &Apache::lonhtmlcommon::row_closure()."\n";
-        if ($usernameset) {
-            my $onclick = "toggleDisplay(this,'selfcreateusername');"; 
+        if ($usernameset eq 'free') {
+            my $onclick = "toggleUsernameDisp(this,'selfcreateusername');"; 
             $output .= &Apache::lonhtmlcommon::row_title($lt{'username'},undef,'LC_oddrow_value')."\n".
                        &mt('Use e-mail address: ').
                        '<label><input type="radio" name="emailused" value="1" checked="checked" onclick="'.$onclick.'" />'.&mt('Yes').'</label>'."\n".
@@ -4943,13 +4965,17 @@
                             my ($showstatus,$showemail,$pickstart);
                             my $numextras = 0;
                             my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
-                            if (ref($usertypes) eq 'HASH') {
-                                if ($usertypes->{$usertype}) {
-                                    $showstatus = $usertypes->{$usertype};
-                                } else {
-                                    $showstatus = $othertitle;
+                            if ((ref($types) eq 'ARRAY') && (@{$types} > 0)) {
+                                if (ref($usertypes) eq 'HASH') {
+                                    if ($usertypes->{$usertype}) {
+                                        $showstatus = $usertypes->{$usertype};
+                                    } else {
+                                        $showstatus = $othertitle;
+                                    }
+                                    if ($showstatus) {
+                                        $numextras ++;
+                                    }
                                 }
-                                $numextras ++;
                             }
                             if (($info{$uname}{'email'} ne '') && ($info{$uname}{'email'} ne $uname)) {
                                 $showemail = $info{$uname}{'email'};
Index: loncom/interface/lonhtmlcommon.pm
diff -u loncom/interface/lonhtmlcommon.pm:1.387 loncom/interface/lonhtmlcommon.pm:1.388
--- loncom/interface/lonhtmlcommon.pm:1.387	Thu Jul 13 21:25:28 2017
+++ loncom/interface/lonhtmlcommon.pm	Thu Aug  3 16:22:09 2017
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common html routines
 #
-# $Id: lonhtmlcommon.pm,v 1.387 2017/07/13 21:25:28 raeburn Exp $
+# $Id: lonhtmlcommon.pm,v 1.388 2017/08/03 16:22:09 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -3608,15 +3608,18 @@
 # javascript_valid_email
 #
 # Generates javascript to validate an e-mail address.
-# Returns a javascript function which accetps a form field as argumnent, and
+# Returns a javascript function which accepts a form field as argument, and
 # returns false if field.value does not satisfy two regular expression matches
 # for a valid e-mail address.  Backwards compatible with old browsers without
 # support for javascript RegExp (just checks for @ in field.value in this case). 
 
 sub javascript_valid_email {
     my $scripttag .= <<'END';
-function validmail(field) {
+function validmail(field,suffix) {
     var str = field.value;
+    if (suffix != '' && suffix != undefined) {
+        str += suffix;
+    }
     if (window.RegExp) {
         var reg1str = "(@.*@)|(\\.\\.)|(@\\.)|(\\.@)|(^\\.)";
         var reg2str = "^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$"; //"


More information about the LON-CAPA-cvs mailing list