--- loncom/interface/loncommon.pm	2007/12/01 03:42:47	1.617
+++ loncom/interface/loncommon.pm	2008/03/28 14:52:52	1.650
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.617 2007/12/01 03:42:47 albertel Exp $
+# $Id: loncommon.pm,v 1.650 2008/03/28 14:52:52 www Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -78,6 +78,76 @@ my $readit;
 ## Global Variables
 ##
 
+
+# ----------------------------------------------- SSI with retries:
+#
+
+=pod
+
+=head1 Server Side include with retries:
+
+=over 4
+
+=item * &ssi_with_retries(resource,retries form)
+
+Performs an ssi with some number of retries.  Retries continue either
+until the result is ok or until the retry count supplied by the
+caller is exhausted.  
+
+Inputs:
+
+=over 4
+
+resource   - Identifies the resource to insert.
+
+retries    - Count of the number of retries allowed.
+
+form       - Hash that identifies the rendering options.
+
+=back
+
+Returns:
+
+=over 4
+
+content    - The content of the response.  If retries were exhausted this is empty.
+
+response   - The response from the last attempt (which may or may not have been successful.
+
+=back
+
+=back
+
+=cut
+
+sub ssi_with_retries {
+    my ($resource, $retries, %form) = @_;
+
+
+    my $ok = 0;			# True if we got a good response.
+    my $content;
+    my $response;
+
+    # Try to get the ssi done. within the retries count:
+
+    do {
+	($content, $response) = &Apache::lonnet::ssi($resource, %form);
+	$ok      = $response->is_success;
+        if (!$ok) {
+            &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
+        }
+	$retries--;
+    } while (!$ok && ($retries > 0));
+
+    if (!$ok) {
+	$content = '';		# On error return an empty content.
+    }
+    return ($content, $response);
+
+}
+
+
+
 # ----------------------------------------------- Filetypes/Languages/Copyright
 my %language;
 my %supported_language;
@@ -214,14 +284,14 @@ BEGIN {
 
 =over 4
 
-=item * browser_and_searcher_javascript ()
+=item * &browser_and_searcher_javascript()
 
 X<browsing, javascript>X<searching, javascript>Returns a string
 containing javascript with two functions, C<openbrowser> and
 C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
 tags.
 
-=item * openbrowser(formname,elementname,only,omit) [javascript]
+=item * &openbrowser(formname,elementname,only,omit) [javascript]
 
 inputs: formname, elementname, only, omit
 
@@ -234,7 +304,7 @@ with the given extension.  Can be a comm
 Specifying 'omit' will restrict the browser to NOT displaying files
 with the given extension.  Can be a comma separated list.
 
-=item * opensearcher(formname, elementname) [javascript]
+=item * &opensearcher(formname,elementname) [javascript]
 
 Inputs: formname, elementname
 
@@ -319,7 +389,7 @@ sub storeresurl {
     unless ($resurl=~/^\/res/) { return 0; }
     $resurl=~s/\/$//;
     &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
-    &Apache::lonnet::appenv('environment.lastresurl' => $resurl);
+    &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
     return 1;
 }
 
@@ -472,7 +542,10 @@ sub setsec_javascript {
     my ($sec_element,$formname) = @_;
     my $setsections = qq|
 function setSect(sectionlist) {
-    var sectionsArray = sectionlist.split(",");
+    var sectionsArray = new Array();
+    if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
+        sectionsArray = sectionlist.split(",");
+    }
     var numSections = sectionsArray.length;
     document.$formname.$sec_element.length = 0;
     if (numSections == 0) {
@@ -539,7 +612,7 @@ ENDSCRT
 
 =pod
 
-=item * linked_select_forms(...)
+=item * &linked_select_forms(...)
 
 linked_select_forms returns a string containing a <script></script> block
 and html for two <select> menus.  The select menus will be linked in that
@@ -704,7 +777,7 @@ END
 
 =pod
 
-=item * help_open_topic($topic, $text, $stayOnPage, $width, $height)
+=item * &help_open_topic($topic,$text,$stayOnPage,$width,$height)
 
 Returns a string corresponding to an HTML link to the given help
 $topic, where $topic corresponds to the name of a .tex file in
@@ -758,7 +831,7 @@ sub help_open_topic {
 
     # Add the graphic
     my $title = &mt('Online Help');
-    my $helpicon=&lonhttpdurl("/adm/help/gif/smallHelp.gif");
+    my $helpicon=&lonhttpdurl("/res/adm/pages/help.png");
     $template .= <<"ENDTEMPLATE";
  <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a>
 ENDTEMPLATE
@@ -779,10 +852,10 @@ sub helpLatexCheatsheet {
     }
     return '<table><tr><td>'.
 	$addOther .
-	&Apache::loncommon::help_open_topic("Greek_Symbols",'Greek Symbols',
+	&Apache::loncommon::help_open_topic("Greek_Symbols",&mt('Greek Symbols'),
 					    undef,undef,600)
 	.'</td><td>'.
-	&Apache::loncommon::help_open_topic("Other_Symbols",'Other Symbols',
+	&Apache::loncommon::help_open_topic("Other_Symbols",&mt('Other Symbols'),
 					    undef,undef,600)
 	.'</td></tr></table>';
 }
@@ -1014,7 +1087,7 @@ ENDTEMPLATE
 
 =pod
 
-=item * change_content_javascript():
+=item * &change_content_javascript():
 
 This and the next function allow you to create small sections of an
 otherwise static HTML page that you can update on the fly with
@@ -1069,7 +1142,7 @@ DOMBASED
 
 =pod
 
-=item * changable_area($name, $origContent):
+=item * &changable_area($name,$origContent):
 
 This provides a "changable area" that can be modified on the fly via
 the Javascript code provided in C<change_content_javascript>. $name is
@@ -1093,7 +1166,7 @@ sub changable_area {
 
 =pod
 
-=item * viewport_geometry_js {
+=item * &viewport_geometry_js 
 
 Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
 
@@ -1140,7 +1213,7 @@ GEOMETRY
 
 =pod
 
-=item * viewport_size_js {
+=item * &viewport_size_js()
 
 Provides a javascript function to set values of two form elements - width and height (elements are passed in as arguments to the javascript function) to the dimensions of the user's browser window. 
 
@@ -1164,7 +1237,7 @@ DIMS
 
 =pod
 
-=item * resize_textarea_js
+=item * &resize_textarea_js()
 
 emits the needed javascript to resize a textarea to be as big as possible
 
@@ -1173,6 +1246,7 @@ the id of the element to resize, second
 surrounds everything that comes after the textarea, this routine needs
 to be attached to the <body> for the onload and onresize events.
 
+=back
 
 =cut
 
@@ -1225,8 +1299,6 @@ RESIZE
 
 =pod
 
-=back
- 
 =head1 Excel and CSV file utility routines
 
 =over 4
@@ -1238,7 +1310,7 @@ RESIZE
 
 =pod
 
-=item * csv_translate($text) 
+=item * &csv_translate($text) 
 
 Translate $text to allow it to be output as a 'comma separated values' 
 format.
@@ -1259,7 +1331,7 @@ sub csv_translate {
 
 =pod
 
-=item * define_excel_formats
+=item * &define_excel_formats()
 
 Define some commonly used Excel cell formats.
 
@@ -1315,7 +1387,7 @@ sub define_excel_formats {
 
 =pod
 
-=item * create_workbook
+=item * &create_workbook()
 
 Create an Excel worksheet.  If it fails, output message on the
 request object and return undefs.
@@ -1358,7 +1430,7 @@ sub create_workbook {
 
 =pod
 
-=item * create_text_file
+=item * &create_text_file()
 
 Create a file to write to and eventually make available to the user.
 If file creation fails, outputs an error message on the request object and 
@@ -1426,7 +1498,7 @@ sub domain_select {
 
 =over 4
 
-=item * multiple_select_form($name,$value,$size,$hash,$order)
+=item * &multiple_select_form($name,$value,$size,$hash,$order)
 
 Returns a string containing a <select> element int multiple mode
 
@@ -1476,7 +1548,7 @@ sub multiple_select_form {
 
 =pod
 
-=item * select_form($defdom,$name,%hash)
+=item * &select_form($defdom,$name,%hash)
 
 Returns a string containing a <select name='$name' size='1'> form to 
 allow a user to select options from a hash option_name => displayed text.  
@@ -1563,7 +1635,7 @@ sub select_level_form {
 
 =pod
 
-=item * select_dom_form($defdom,$name,$includeempty,$showdomdesc)
+=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc)
 
 Returns a string containing a <select name='$name' size='1'> form to 
 allow a user to select the domain to preform an operation in.  
@@ -1603,7 +1675,7 @@ sub select_dom_form {
 
 =pod
 
-=item * home_server_form_item($domain,$name,$defaultflag)
+=item * &home_server_form_item($domain,$name,$defaultflag)
 
 input: 4 arguments (two required, two optional) - 
     $domain - domain of new user
@@ -1763,14 +1835,12 @@ sub decode_user_agent {
 
 =over 4
 
-=item * authform_xxxxxx
+=item * &authform_xxxxxx()
 
 The authform_xxxxxx subroutines provide javascript and html forms which 
 handle some of the conveniences required for authentication forms.  
 This is not an optimal method, but it works.  
 
-See loncreateuser.pm for invocation and use examples.
-
 =over 4
 
 =item * authform_header
@@ -1787,7 +1857,7 @@ See loncreateuser.pm for invocation and
 
 =back
 
-=back 
+See loncreateuser.pm for invocation and use examples.
 
 =cut
 
@@ -1942,6 +2012,11 @@ sub authform_kerberos {
     if (defined($in{'curr_authtype'})) {
         if ($in{'curr_authtype'} eq 'krb') {
             $krbcheck = ' checked="on"';
+            if (defined($in{'mode'})) {
+                if ($in{'mode'} eq 'modifyuser') {
+                    $krbcheck = '';
+                }
+            }
             if (defined($in{'curr_kerb_ver'})) {
                 if ($in{'curr_krb_ver'} eq '5') {
                     $check5 = ' checked="on"';
@@ -2039,6 +2114,11 @@ sub authform_internal{
         if ($in{'curr_authtype'} eq 'int') {
             if ($can_assign{'int'}) {
                 $intcheck = 'checked="on" ';
+                if (defined($in{'mode'})) {
+                    if ($in{'mode'} eq 'modifyuser') {
+                        $intcheck = '';
+                    }
+                }
                 if (defined($in{'curr_autharg'})) {
                     $intarg = $in{'curr_autharg'};
                 }
@@ -2073,6 +2153,7 @@ sub authform_internal{
     $result = &mt
         ('[_1] Internally authenticated (with initial password [_2])',
          '<label>'.$authtype,'</label>'.$autharg);
+    $result.="<label><input type=\"checkbox\" name=\"visible\" onClick='if (this.checked) { this.form.intarg.type=\"text\" } else { this.form.intarg.type=\"password\" }' />".&mt('Visible input').'</label>';
     return $result;
 }
 
@@ -2088,6 +2169,11 @@ sub authform_local{
         if ($in{'curr_authtype'} eq 'loc') {
             if ($can_assign{'loc'}) {
                 $loccheck = 'checked="on" ';
+                if (defined($in{'mode'})) {
+                    if ($in{'mode'} eq 'modifyuser') {
+                        $loccheck = '';
+                    }
+                }
                 if (defined($in{'curr_autharg'})) {
                     $locarg = $in{'curr_autharg'};
                 }
@@ -2137,6 +2223,11 @@ sub authform_filesystem{
         if ($in{'curr_authtype'} eq 'fsys') {
             if ($can_assign{'fsys'}) {
                 $fsyscheck = 'checked="on" ';
+                if (defined($in{'mode'})) {
+                    if ($in{'mode'} eq 'modifyuser') {
+                        $fsyscheck = '';
+                    }
+                }
             } else {
                 $result = &mt('Currently Filesystem Authenticated.');
                 return $result;
@@ -2218,42 +2309,6 @@ sub get_assignable_auth {
 }
 
 ###############################################################
-##    Get Authentication Defaults for Domain                 ##
-###############################################################
-
-=pod
-
-=head1 Domains and Authentication
-
-Returns default authentication type and an associated argument as
-listed in file 'domain.tab'.
-
-=over 4
-
-=item * get_auth_defaults
-
-get_auth_defaults($target_domain) returns the default authentication
-type and an associated argument (initial password or a kerberos domain).
-These values are stored in lonTabs/domain.tab
-
-($def_auth, $def_arg) = &get_auth_defaults($target_domain);
-
-If target_domain is not found in domain.tab, returns nothing ('').
-
-=cut
-
-#-------------------------------------------
-sub get_auth_defaults {
-    my $domain=shift;
-    return (&Apache::lonnet::domain($domain,'auth_def'),
-	    &Apache::lonnet::domain($domain,'auth_arg_def'));
-	    
-}
-###############################################################
-##   End Get Authentication Defaults for Domain              ##
-###############################################################
-
-###############################################################
 ##    Get Kerberos Defaults for Domain                 ##
 ###############################################################
 ##
@@ -2265,22 +2320,31 @@ sub get_auth_defaults {
 
 =pod
 
-=item * get_kerberos_defaults
+=item * &get_kerberos_defaults()
 
 get_kerberos_defaults($target_domain) returns the default kerberos
-version and domain. If not found in domain.tabs, it defaults to
-version 4 and the domain of the server.
+version and domain. If not found, it defaults to version 4 and the 
+domain of the server.
+
+=over 4
 
 ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
 
+=back
+
+=back
+
 =cut
 
 #-------------------------------------------
 sub get_kerberos_defaults {
     my $domain=shift;
-    my ($krbdef,$krbdefdom) =
-        &Apache::loncommon::get_auth_defaults($domain);
-    unless ($krbdef =~/^krb/ && $krbdefdom) {
+    my ($krbdef,$krbdefdom);
+    my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
+    if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
+        $krbdef = $domdefaults{'auth_def'};
+        $krbdefdom = $domdefaults{'auth_arg_def'};
+    } else {
         $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
         my $krbdefdom=$1;
         $krbdefdom=~tr/a-z/A-Z/;
@@ -2289,11 +2353,6 @@ sub get_kerberos_defaults {
     return ($krbdef,$krbdefdom);
 }
 
-=pod
-
-=back
-
-=cut
 
 ###############################################################
 ##                Thesaurus Functions                        ##
@@ -2305,7 +2364,7 @@ sub get_kerberos_defaults {
 
 =over 4
 
-=item * initialize_keywords
+=item * &initialize_keywords()
 
 Initializes the package variable %Keywords if it is empty.  Uses the
 package variable $thesaurus_db_file.
@@ -2350,7 +2409,7 @@ sub initialize_keywords {
 
 =pod
 
-=item * keyword($word)
+=item * &keyword($word)
 
 Returns true if $word is a keyword.  A keyword is a word that appears more 
 than the average number of times in the thesaurus database.  Calls 
@@ -2371,7 +2430,7 @@ sub keyword {
 
 =pod 
 
-=item * get_related_words
+=item * &get_related_words()
 
 Look up a word in the thesaurus.  Takes a scalar argument and returns
 an array of words.  If the keyword is not in the thesaurus, an empty array
@@ -2429,7 +2488,7 @@ sub get_related_words {
 
 =over 4
 
-=item * plainname($uname,$udom,$first)
+=item * &plainname($uname,$udom,$first)
 
 Takes a users logon name and returns it as a string in
 "first middle last generation" form 
@@ -2458,7 +2517,7 @@ sub plainname {
 # -------------------------------------------------------------------- Nickname
 =pod
 
-=item * nickname($uname,$udom)
+=item * &nickname($uname,$udom)
 
 Gets a users name and returns it as a string as
 
@@ -2508,18 +2567,21 @@ sub getnames {
 }
 
 # -------------------------------------------------------------------- getemails
+
 =pod
 
-=item * getemails($uname,$udom)
+=item * &getemails($uname,$udom)
 
 Gets a user's email information and returns it as a hash with keys:
 notification, critnotification, permanentemail
 
 For notification and critnotification, values are comma-separated lists 
-of e-mail address(es); for permanentemail, value is a single e-mail address.
+of e-mail addresses; for permanentemail, value is a single e-mail address.
  
+
 =cut
 
+
 sub getemails {
     my ($uname,$udom)=@_;
     if ($udom eq 'public' && $uname eq 'public') {
@@ -2554,7 +2616,7 @@ sub flush_email_cache {
 
 =pod
 
-=item * screenname($uname,$udom)
+=item * &screenname($uname,$udom)
 
 Gets a users screenname and returns it as a string
 
@@ -2650,7 +2712,7 @@ sub student_image_tag {
 
 =over 4
 
-=item * languageids() 
+=item * &languageids() 
 
 returns list of all language ids
 
@@ -2662,7 +2724,7 @@ sub languageids {
 
 =pod
 
-=item * languagedescription() 
+=item * &languagedescription() 
 
 returns description of a specified language id
 
@@ -2687,7 +2749,7 @@ sub supportedlanguagecode {
 
 =pod
 
-=item * copyrightids() 
+=item * &copyrightids() 
 
 returns list of all copyrights
 
@@ -2699,7 +2761,7 @@ sub copyrightids {
 
 =pod
 
-=item * copyrightdescription() 
+=item * &copyrightdescription() 
 
 returns description of a specified copyright id
 
@@ -2711,7 +2773,7 @@ sub copyrightdescription {
 
 =pod
 
-=item * source_copyrightids() 
+=item * &source_copyrightids() 
 
 returns list of all source copyrights
 
@@ -2723,7 +2785,7 @@ sub source_copyrightids {
 
 =pod
 
-=item * source_copyrightdescription() 
+=item * &source_copyrightdescription() 
 
 returns description of a specified source copyright id
 
@@ -2735,7 +2797,7 @@ sub source_copyrightdescription {
 
 =pod
 
-=item * filecategories() 
+=item * &filecategories() 
 
 returns list of all file categories
 
@@ -2747,7 +2809,7 @@ sub filecategories {
 
 =pod
 
-=item * filecategorytypes() 
+=item * &filecategorytypes() 
 
 returns list of file types belonging to a given file
 category
@@ -2761,7 +2823,7 @@ sub filecategorytypes {
 
 =pod
 
-=item * fileembstyle() 
+=item * &fileembstyle() 
 
 returns embedding style for a specified file type
 
@@ -2785,7 +2847,7 @@ sub filecategoryselect {
 
 =pod
 
-=item * filedescription() 
+=item * &filedescription() 
 
 returns description for a specified file type
 
@@ -2799,7 +2861,7 @@ sub filedescription {
 
 =pod
 
-=item * filedescriptionex() 
+=item * &filedescriptionex() 
 
 returns description for a specified file type with
 extra formatting
@@ -2859,21 +2921,15 @@ sub preferred_languages {
 	    map { (split(/\s*;\s*/,$_))[0] } (split(/\s*,\s*/,$browser));
 	push(@languages,@browser);
     }
-    if (&Apache::lonnet::domain($env{'user.domain'},'lang_def')) {
-	@languages=(@languages,
-		    &Apache::lonnet::domain($env{'user.domain'},
-					    'lang_def'));
-    }
-    if (&Apache::lonnet::domain($env{'request.role.domain'},'lang_def')) {
-	@languages=(@languages,
-		    &Apache::lonnet::domain($env{'request.role.domain'},
-					    'lang_def'));
-    }
-    if (&Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'},
-				'lang_def')) {
-	@languages=(@languages,
-		    &Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'},
-					    'lang_def'));
+
+    foreach my $domtype ($env{'user.domain'},$env{'request.role.domain'},
+                         $Apache::lonnet::perlvar{'lonDefDomain'}) {
+        if ($domtype ne '') {
+            my %domdefs = &Apache::lonnet::get_domain_defaults($domtype);
+            if ($domdefs{'lang_def'} ne '') {
+                push(@languages,$domdefs{'lang_def'});
+            }
+        }
     }
 # turn "en-ca" into "en-ca,en"
     my @genlanguages;
@@ -2923,7 +2979,7 @@ sub languages {
 
 =over 4
 
-=item * get_previous_attempt($symb, $username, $domain, $course,
+=item * &get_previous_attempt($symb, $username, $domain, $course,
     $getattempt, $regexp, $gradesub)
 
 Return string with previous attempt on problem. Arguments:
@@ -3067,7 +3123,7 @@ sub relative_to_absolute {
 
 =pod
 
-=item * get_student_view
+=item * &get_student_view()
 
 show a snapshot of what student was looking at
 
@@ -3086,7 +3142,7 @@ sub get_student_view {
   }
   if (defined($target)) { $form{'grade_target'} = $target; }
   $feedurl=&Apache::lonnet::clutter($feedurl);
-  my $userview=&Apache::lonnet::ssi_body($feedurl,%form);
+  my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
   $userview=~s/\<body[^\>]*\>//gi;
   $userview=~s/\<\/body\>//gi;
   $userview=~s/\<html\>//gi;
@@ -3095,12 +3151,40 @@ sub get_student_view {
   $userview=~s/\<\/head\>//gi;
   $userview=~s/action\s*\=/would_be_action\=/gi;
   $userview=&relative_to_absolute($feedurl,$userview);
-  return $userview;
+  if (wantarray) {
+     return ($userview,$response);
+  } else {
+     return $userview;
+  }
+}
+
+sub get_student_view_with_retries {
+  my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
+
+    my $ok = 0;                 # True if we got a good response.
+    my $content;
+    my $response;
+
+    # Try to get the student_view done. within the retries count:
+    
+    do {
+         ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
+         $ok      = $response->is_success;
+         if (!$ok) {
+            &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
+         }
+         $retries--;
+    } while (!$ok && ($retries > 0));
+    
+    if (!$ok) {
+       $content = '';          # On error return an empty content.
+    }
+    return ($content, $response);
 }
 
 =pod
 
-=item * get_student_answers() 
+=item * &get_student_answers() 
 
 show a snapshot of how student was answering problem
 
@@ -3646,45 +3730,78 @@ sub get_domainconf {
 
     my %domconfig = &Apache::lonnet::get_dom('configuration',
 					     ['login','rolecolors'],$udom);
-    my %designhash;
+    my (%designhash,%legacy);
     if (keys(%domconfig) > 0) {
         if (ref($domconfig{'login'}) eq 'HASH') {
-            foreach my $key (keys(%{$domconfig{'login'}})) {
-                $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
+            if (keys(%{$domconfig{'login'}})) {
+                foreach my $key (keys(%{$domconfig{'login'}})) {
+                    $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
+                }
+            } else {
+                $legacy{'login'} = 1;
             }
+        } else {
+            $legacy{'login'} = 1;
         }
         if (ref($domconfig{'rolecolors'}) eq 'HASH') {
-            foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
-                if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
-                    foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
-                        $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
+            if (keys(%{$domconfig{'rolecolors'}})) {
+                foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
+                    if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
+                        foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
+                            $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
+                        }
                     }
                 }
+            } else {
+                $legacy{'rolecolors'} = 1;
             }
+        } else {
+            $legacy{'rolecolors'} = 1;
         }
-    } else {
-        my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
-        my $designfile =  $designdir.'/'.$udom.'.tab';
-        if (-e $designfile) {
-            if ( open (my $fh,"<$designfile") ) {
-                while (my $line = <$fh>) {
-                    next if ($line =~ /^\#/);
-                    chomp($line);
-                    my ($key,$val)=(split(/\=/,$line));
-                    if ($val) { $designhash{$udom.'.'.$key}=$val; }
+        if (keys(%legacy) > 0) {
+            my %legacyhash = &get_legacy_domconf($udom);
+            foreach my $item (keys(%legacyhash)) {
+                if ($item =~ /^\Q$udom\E\.login/) {
+                    if ($legacy{'login'}) { 
+                        $designhash{$item} = $legacyhash{$item};
+                    }
+                } else {
+                    if ($legacy{'rolecolors'}) {
+                        $designhash{$item} = $legacyhash{$item};
+                    }
                 }
-                close($fh);
             }
         }
-        if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
-            $designhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
-        }
+    } else {
+        %designhash = &get_legacy_domconf($udom); 
     }
     &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
 				  $cachetime);
     return %designhash;
 }
 
+sub get_legacy_domconf {
+    my ($udom) = @_;
+    my %legacyhash;
+    my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
+    my $designfile =  $designdir.'/'.$udom.'.tab';
+    if (-e $designfile) {
+        if ( open (my $fh,"<$designfile") ) {
+            while (my $line = <$fh>) {
+                next if ($line =~ /^\#/);
+                chomp($line);
+                my ($key,$val)=(split(/\=/,$line));
+                if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
+            }
+            close($fh);
+        }
+    }
+    if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
+        $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
+    }
+    return %legacyhash;
+}
+
 =pod
 
 =item * &domainlogo()
@@ -3734,10 +3851,10 @@ Returns: value of designparamter $which
 sub designparm {
     my ($which,$domain)=@_;
     if ($env{'browser.blackwhite'} eq 'on') {
-	if ($which=~/\.(font|alink|vlink|link)$/) {
+	if ($which=~/\.(font|alink|vlink|link|textcol)$/) {
 	    return '#000000';
 	}
-	if ($which=~/\.(pgbg|sidebg)$/) {
+	if ($which=~/\.(pgbg|sidebg|bgcol)$/) {
 	    return '#FFFFFF';
 	}
 	if ($which=~/\.tabbg$/) {
@@ -3756,7 +3873,7 @@ sub designparm {
         $output = $defaultdesign{$which};
     }
     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
-        ($which =~ /login\.(img|logo|domlogo)/)) {
+        ($which =~ /login\.(img|logo|domlogo|login)/)) {
         if ($output =~ m{^/(adm|res)/}) {
 	    if ($output =~ m{^/res/}) {
 		my $local_name = &Apache::lonnet::filelocation('',$output);
@@ -3924,7 +4041,7 @@ ENDROLE
         $dc_info = '('.$dc_info.')';
     }
 
-    if ($env{'environment.remote'} eq 'off') {
+    if (($env{'environment.remote'} eq 'off') || ($args->{'suppress_header_logos'})) {
         # No Remote
 	if ($env{'request.state'} eq 'construct') {
 	    $forcereg=1;
@@ -3947,9 +4064,9 @@ ENDROLE
 		$lastitem = $thisdisfn;
 	    }
 	    $titleinfo = 
-		&Apache::loncommon::help_open_menu('','',3,'Authoring').
-		'<b>Construction Space</b>:&nbsp;'. 
-		'<form name="dirs" method="post" action="'.$formaction
+		&Apache::loncommon::help_open_menu('','',3,'Authoring')
+		.'<b>'.&mt('Construction Space').'</b>:&nbsp;'
+		.'<form name="dirs" method="post" action="'.$formaction
 		.'" target="_top"><tt><b>'
 		.&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<font size=\"+1\">$lastitem</font></b></tt><br />"
 		.&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
@@ -4073,18 +4190,25 @@ sub make_attr_string {
 
 Returns a uniform footer for LON-CAPA web pages.
 
-Inputs: none
+Inputs: 1 - optional reference to an args hash
+If in the hash, key for noredirectlink has a value which evaluates to true,
+a 'Continue' link is not displayed if the page contains an
+internal redirect in the <head></head> section,
+i.e., $env{'internal.head.redirect'} exists   
 
 =cut
 
 sub endbodytag {
+    my ($args) = @_;
     my $endbodytag='</body>';
     $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
     if ( exists( $env{'internal.head.redirect'} ) ) {
-	$endbodytag=
-	    "<br /><a href=\"$env{'internal.head.redirect'}\">".
-	    &mt('Continue').'</a>'.
-	    $endbodytag;
+        if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
+	    $endbodytag=
+	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
+	        &mt('Continue').'</a>'.
+	        $endbodytag;
+        }
     }
     return $endbodytag;
 }
@@ -4347,6 +4471,7 @@ td.LC_menubuttons_img {
 }
 .LC_new_mail {
   font-family: $sans;
+  background: $tabbg;
   font-weight: bold;
 }
 
@@ -4433,6 +4558,10 @@ table.LC_aboutme_port tr.LC_even_row td
 table.LC_data_table tr.LC_data_table_highlight td {
   background-color: $data_table_darker;
 }
+table.LC_data_table tr td.LC_leftcol_header {
+  background-color: $data_table_head;
+  font-weight: bold;
+}
 table.LC_data_table tr.LC_empty_row td,
 table.LC_nested tr.LC_empty_row td {
   background-color: #FFFFFF;
@@ -4708,6 +4837,14 @@ table.LC_pick_box td.LC_pick_box_title {
   width: 184px;
   padding: 8px;
 }
+table.LC_pick_box td.LC_selfenroll_pick_box_title {
+  background: $tabbg;
+  font-weight: bold;
+  text-align: right;
+  width: 350px;
+  padding: 8px;
+}
+
 table.LC_pick_box td.LC_pick_box_value {
   text-align: left;
   padding: 8px;
@@ -4922,6 +5059,11 @@ span.LC_cusr_emph {
   font-style: italic;
 }
 
+span.LC_cusr_subheading {
+  font-weight: normal;
+  font-size: 85%;
+}
+
 table.LC_docs_documents {
   background: #BBBBBB;
   border-width: 0px;
@@ -5038,7 +5180,6 @@ div.LC_clear_float_footer {
 
 
 div.LC_grade_select_mode {
-  float: left;
   font-family: $sans;
 }
 div.LC_grade_select_mode div div {
@@ -5248,10 +5389,7 @@ Inputs: none
 
 sub font_settings {
     my $headerstring='';
-    if (($env{'browser.os'} eq 'mac') && (!$env{'browser.mathml'})) { 
-	$headerstring.=
-	    '<meta Content-Type="text/html; charset=x-mac-roman" />';
-    } elsif (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
+    if (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
 	$headerstring.=
 	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
     }
@@ -5310,8 +5448,15 @@ sub endheadtag {
 
 Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
 
-Inputs: $title - optional title for the page
-        $head_extra - optional extra HTML to put inside the <head>
+Inputs:
+
+=over 4
+
+$title - optional title for the page
+
+$head_extra - optional extra HTML to put inside the <head>
+
+=back
 
 =cut
 
@@ -5326,44 +5471,54 @@ sub head {
 
 Returns a complete <html> .. <body> section for LON-CAPA web pages.
 
-Inputs: $title - optional title for the page
-        $head_extra - optional extra HTML to incude inside the <head>
-        $args - additional optional args supported are:
-                  only_body      -> is true will set &bodytag() onlybodytag
+Inputs:
+
+=over 4
+
+$title - optional title for the page
+
+$head_extra - optional extra HTML to incude inside the <head>
+
+$args - additional optional args supported are:
+
+=over 8
+
+             only_body      -> is true will set &bodytag() onlybodytag
                                     arg on
-                  no_nav_bar     -> is true will set &bodytag() notopbar arg on
-                  add_entries    -> additional attributes to add to the  <body>
-                  domain         -> force to color decorate a page for a 
+             no_nav_bar     -> is true will set &bodytag() notopbar arg on
+             add_entries    -> additional attributes to add to the  <body>
+             domain         -> force to color decorate a page for a 
                                     specific domain
-                  function       -> force usage of a specific rolish color
+             function       -> force usage of a specific rolish color
                                     scheme
-                  redirect       -> see &headtag()
-                  bgcolor        -> override the default page bg color
-                  js_ready       -> return a string ready for being used in 
+             redirect       -> see &headtag()
+             bgcolor        -> override the default page bg color
+             js_ready       -> return a string ready for being used in 
                                     a javascript writeln
-                  html_encode    -> return a string ready for being used in 
+             html_encode    -> return a string ready for being used in 
                                     a html attribute
-                  force_register -> if is true will turn on the &bodytag()
+             force_register -> if is true will turn on the &bodytag()
                                     $forcereg arg
-                  body_title     -> alternate text to use instead of $title
+             body_title     -> alternate text to use instead of $title
                                     in the title box that appears, this text
                                     is not auto translated like the $title is
-                  frameset       -> if true will start with a <frameset>
+             frameset       -> if true will start with a <frameset>
                                     rather than <body>
-                  no_title       -> if true the title bar won't be shown
-                  skip_phases    -> hash ref of 
+             no_title       -> if true the title bar won't be shown
+             skip_phases    -> hash ref of 
                                     head -> skip the <html><head> generation
                                     body -> skip all <body> generation
-
-                  no_inline_link -> if true and in remote mode, don't show the 
+             no_inline_link -> if true and in remote mode, don't show the 
                                     'Switch To Inline Menu' link
-
-                  no_auto_mt_title -> prevent &mt()ing the title arg
-
-                  inherit_jsmath -> when creating popup window in a page,
+             no_auto_mt_title -> prevent &mt()ing the title arg
+             inherit_jsmath -> when creating popup window in a page,
                                     should it have jsmath forced on by the
                                     current page
 
+=back
+
+=back
+
 =cut
 
 sub start_page {
@@ -5450,7 +5605,7 @@ sub end_page {
     if ($args->{'frameset'}) {
 	$result .= '</frameset>';
     } else {
-	$result .= &endbodytag();
+	$result .= &endbodytag($args);
     }
     $result .= "\n</html>";
 
@@ -5828,6 +5983,8 @@ previous, future, or all.
 6. reference to results object (hash of hashes).
 7. reference to optional userdata hash
 8. reference to optional statushash
+9. flag if privileged users (except those set to unhide in
+   course settings) should be excluded    
 Keys of top level results hash are roles.
 Keys of inner hashes are username:domain, with 
 values set to access type.
@@ -5844,7 +6001,7 @@ of the possibility of multiple values fo
 ###############################################
 
 sub get_course_users {
-    my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash) = @_;
+    my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
     my %idx = ();
     my %seclists;
 
@@ -5920,6 +6077,17 @@ sub get_course_users {
                               active   => 'Active',
                               future   => 'Future',
                             );
+        my %nothide;
+        if ($hidepriv) {
+            my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
+            foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
+                if ($user !~ /:/) {
+                    $nothide{join(':',split(/[\@]/,$user))}=1;
+                } else {
+                    $nothide{$user} = 1;
+                }
+            }
+        }
         foreach my $person (sort(keys(%coursepersonnel))) {
             my $match = 0;
             my $secmatch = 0;
@@ -5953,6 +6121,12 @@ sub get_course_users {
                     $usec = 'none';
                 }
                 if ($uname ne '' && $udom ne '') {
+                    if ($hidepriv) {
+                        if ((&Apache::lonnet::privileged($uname,$udom)) &&
+                            (!$nothide{$uname.':'.$udom})) {
+                            next;
+                        }
+                    }
                     if ($end > 0 && $end < $now) {
                         $status = 'previous';
                     } elsif ($start > $now) {
@@ -6147,24 +6321,24 @@ sub default_quota {
     my ($udom,$inststatus) = @_;
     my ($defquota,$settingstatus);
     my %quotahash = &Apache::lonnet::get_dom('configuration',
-                                            ['quota'],$udom);
-    if (ref($quotahash{'quota'}) eq 'HASH') {
+                                            ['quotas'],$udom);
+    if (ref($quotahash{'quotas'}) eq 'HASH') {
         if ($inststatus ne '') {
             my @statuses = split(/:/,$inststatus);
             foreach my $item (@statuses) {
-                if ($quotahash{'quota'}{$item} ne '') {
+                if ($quotahash{'quotas'}{$item} ne '') {
                     if ($defquota eq '') {
-                        $defquota = $quotahash{'quota'}{$item};
+                        $defquota = $quotahash{'quotas'}{$item};
                         $settingstatus = $item;
-                    } elsif ($quotahash{'quota'}{$item} > $defquota) {
-                        $defquota = $quotahash{'quota'}{$item};
+                    } elsif ($quotahash{'quotas'}{$item} > $defquota) {
+                        $defquota = $quotahash{'quotas'}{$item};
                         $settingstatus = $item;
                     }
                 }
             }
         }
         if ($defquota eq '') {
-            $defquota = $quotahash{'quota'}{'default'};
+            $defquota = $quotahash{'quotas'}{'default'};
             $settingstatus = 'default';
         }
     } else {
@@ -6216,14 +6390,14 @@ sub get_secgrprole_info {
 }
 
 sub user_picker {
-    my ($dom,$srch,$forcenewuser,$caller) = @_;
+    my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype) = @_;
     my $currdom = $dom;
     my %curr_selected = (
                         srchin => 'dom',
                         srchby => 'lastname',
                       );
     my $srchterm;
-    if (ref($srch) eq 'HASH') {
+    if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
         if ($srch->{'srchby'} ne '') {
             $curr_selected{'srchby'} = $srch->{'srchby'};
         }
@@ -6310,7 +6484,16 @@ sub user_picker {
     if ($forcenewuser) {
         if (ref($srch) eq 'HASH') {
             if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $env{'request.role.domain'}) {
-	        $new_user_create = '<p> <input type="submit" name="forcenew" value="'.&HTML::Entities::encode(&mt('Make new user "[_1]"',$srchterm),'<>&"').'" onclick="javascript:setSearch(\'1\','.$caller.');" /> </p>';
+                if ($cancreate) {
+                    $new_user_create = '<p> <input type="submit" name="forcenew" value="'.&HTML::Entities::encode(&mt('Make new user "[_1]"',$srchterm),'<>&"').'" onclick="javascript:setSearch(\'1\','.$caller.');" /> </p>';
+                } else {
+                    my $helplink = ' href="javascript:helpMenu('."'display'".')"';
+                    my %usertypetext = (
+                        official   => 'institutional',
+                        unofficial => 'non-institutional',
+                    );
+                    $new_user_create = '<br /><span class="LC_warning">'.&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.").' '.&mt('Contact the <a[_1]>helpdesk</a> for assistance.',$helplink).'</span><br /><br />';
+                }
             }
         }
 
@@ -6578,6 +6761,85 @@ sub instrule_disallow_msg {
     return $response;
 }
 
+sub personal_data_fieldtitles {
+    my %fieldtitles = &Apache::lonlocal::texthash (
+                        id => 'Student/Employee ID',
+                        permanentemail => 'E-mail address',
+                        lastname => 'Last Name',
+                        firstname => 'First Name',
+                        middlename => 'Middle Name',
+                        generation => 'Generation',
+                        gen => 'Generation',
+                   );
+    return %fieldtitles;
+}
+
+sub sorted_inst_types {
+    my ($dom) = @_;
+    my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
+    my $othertitle = &mt('All users');
+    if ($env{'request.course.id'}) {
+        $othertitle  = 'any';
+    }
+    my @types;
+    if (ref($order) eq 'ARRAY') {
+        @types = @{$order};
+    }
+    if (@types == 0) {
+        if (ref($usertypes) eq 'HASH') {
+            @types = sort(keys(%{$usertypes}));
+        }
+    }
+    if (keys(%{$usertypes}) > 0) {
+        $othertitle = &mt('Other users');
+        if ($env{'request.course.id'}) {
+            $othertitle = 'other';
+        }
+    }
+    return ($othertitle,$usertypes,\@types);
+}
+
+sub get_institutional_codes {
+    my ($settings,$allcourses,$LC_code) = @_;
+# Get complete list of course sections to update
+    my @currsections = ();
+    my @currxlists = ();
+    my $coursecode = $$settings{'internal.coursecode'};
+
+    if ($$settings{'internal.sectionnums'} ne '') {
+        @currsections = split(/,/,$$settings{'internal.sectionnums'});
+    }
+
+    if ($$settings{'internal.crosslistings'} ne '') {
+        @currxlists = split(/,/,$$settings{'internal.crosslistings'});
+    }
+
+    if (@currxlists > 0) {
+        foreach (@currxlists) {
+            if (m/^([^:]+):(\w*)$/) {
+                unless (grep/^$1$/,@{$allcourses}) {
+                    push @{$allcourses},$1;
+                    $$LC_code{$1} = $2;
+                }
+            }
+        }
+    }
+ 
+    if (@currsections > 0) {
+        foreach (@currsections) {
+            if (m/^(\w+):(\w*)$/) {
+                my $sec = $coursecode.$1;
+                my $lc_sec = $2;
+                unless (grep/^$sec$/,@{$allcourses}) {
+                    push @{$allcourses},$sec;
+                    $$LC_code{$sec} = $lc_sec;
+                }
+            }
+        }
+    }
+    return;
+}
+
 =pod
 
 =back
@@ -6586,7 +6848,7 @@ sub instrule_disallow_msg {
 
 =over 4
 
-=item * get_unprocessed_cgi($query,$possible_names)
+=item * &get_unprocessed_cgi($query,$possible_names)
 
 Modify the %env hash to contain unprocessed CGI form parameters held in
 $query.  The parameters listed in $possible_names (an array reference),
@@ -6615,7 +6877,7 @@ sub get_unprocessed_cgi {
 
 =pod
 
-=item * cacheheader() 
+=item * &cacheheader() 
 
 returns cache-controlling header code
 
@@ -6632,7 +6894,7 @@ sub cacheheader {
 
 =pod
 
-=item * no_cache($r) 
+=item * &no_cache($r) 
 
 specifies header code to not have cache
 
@@ -6668,7 +6930,7 @@ sub content_type {
 
 =pod
 
-=item * add_to_env($name,$value) 
+=item * &add_to_env($name,$value) 
 
 adds $name to the %env hash with value
 $value, if $name already exists, the entry is converted to an array
@@ -6695,7 +6957,7 @@ sub add_to_env {
 
 =pod
 
-=item * get_env_multiple($name) 
+=item * &get_env_multiple($name) 
 
 gets $name from the %env hash, it seemlessly handles the cases where multiple
 values may be defined and end up as an array ref.
@@ -6727,7 +6989,7 @@ sub get_env_multiple {
 
 =over 4
 
-=item * upfile_store($r)
+=item * &upfile_store($r)
 
 Store uploaded file, $r should be the HTTP Request object,
 needs $env{'form.upfile'}
@@ -6757,7 +7019,7 @@ sub upfile_store {
 
 =pod
 
-=item * load_tmp_file($r)
+=item * &load_tmp_file($r)
 
 Load uploaded file from tmp, $r should be the HTTP Request object,
 needs $env{'form.datatoken'},
@@ -6781,7 +7043,7 @@ sub load_tmp_file {
 
 =pod
 
-=item * upfile_record_sep()
+=item * &upfile_record_sep()
 
 Separate uploaded file into records
 returns array of records,
@@ -6803,7 +7065,7 @@ sub upfile_record_sep {
 
 =pod
 
-=item * record_sep($record)
+=item * &record_sep($record)
 
 Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
 
@@ -6888,7 +7150,7 @@ sub record_sep {
 
 =pod
 
-=item * upfile_select_html()
+=item * &upfile_select_html()
 
 Return HTML code to select a file from the users machine and specify 
 the file type.
@@ -6935,7 +7197,7 @@ sub get_samples {
 
 =pod
 
-=item * csv_print_samples($r,$records)
+=item * &csv_print_samples($r,$records)
 
 Prints a table of sample values from each column uploaded $r is an
 Apache Request ref, $records is an arrayref from
@@ -6971,7 +7233,7 @@ sub csv_print_samples {
 
 =pod
 
-=item * csv_print_select_table($r,$records,$d)
+=item * &csv_print_select_table($r,$records,$d)
 
 Prints a table to create associations between values and table columns.
 
@@ -7017,7 +7279,7 @@ sub csv_print_select_table {
 
 =pod
 
-=item * csv_samples_select_table($r,$records,$d)
+=item * &csv_samples_select_table($r,$records,$d)
 
 Prints a table of sample values from the upload and can make associate samples to internal names.
 
@@ -7067,7 +7329,7 @@ sub csv_samples_select_table {
 
 =pod
 
-=item clean_excel_name($name)
+=item * &clean_excel_name($name)
 
 Returns a replacement for $name which does not contain any illegal characters.
 
@@ -7086,7 +7348,7 @@ sub clean_excel_name {
 
 =pod
 
-=item * check_if_partid_hidden($id,$symb,$udom,$uname)
+=item * &check_if_partid_hidden($id,$symb,$udom,$uname)
 
 Returns either 1 or undef
 
@@ -7127,7 +7389,7 @@ sub check_if_partid_hidden {
 
 =over 4
 
-=item get_cgi_id
+=item * &get_cgi_id()
 
 Inputs: none
 
@@ -7151,7 +7413,7 @@ sub get_cgi_id {
 
 =pod
 
-=item DrawBarGraph
+=item * &DrawBarGraph()
 
 Facilitates the plotting of data in a (stacked) bar graph.
 Puts plot definition data into the users environment in order for 
@@ -7286,7 +7548,7 @@ sub DrawBarGraph {
         $ValuesHash{$id.'.'.$key} = $value;
     }
     #
-    &Apache::lonnet::appenv(%ValuesHash);
+    &Apache::lonnet::appenv(\%ValuesHash);
     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
 }
 
@@ -7295,7 +7557,7 @@ sub DrawBarGraph {
 
 =pod
 
-=item DrawXYGraph
+=item * &DrawXYGraph()
 
 Facilitates the plotting of data in an XY graph.
 Puts plot definition data into the users environment in order for 
@@ -7376,7 +7638,7 @@ sub DrawXYGraph {
         $ValuesHash{$id.'.'.$key} = $value;
     }
     #
-    &Apache::lonnet::appenv(%ValuesHash);
+    &Apache::lonnet::appenv(\%ValuesHash);
     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
 }
 
@@ -7385,7 +7647,7 @@ sub DrawXYGraph {
 
 =pod
 
-=item DrawXYYGraph
+=item * &DrawXYYGraph()
 
 Facilitates the plotting of data in an XY graph with two Y axes.
 Puts plot definition data into the users environment in order for 
@@ -7478,7 +7740,7 @@ sub DrawXYYGraph {
         $ValuesHash{$id.'.'.$key} = $value;
     }
     #
-    &Apache::lonnet::appenv(%ValuesHash);
+    &Apache::lonnet::appenv(\%ValuesHash);
     return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
 }
 
@@ -7495,7 +7757,7 @@ Bad place for them but what the hell.
 
 =over 4
 
-=item &chartlink
+=item * &chartlink()
 
 Returns a link to the chart for a specific student.  
 
@@ -7534,9 +7796,9 @@ sub chartlink {
 
 =over 4
 
-=item &restore_course_settings 
+=item * &restore_course_settings()
 
-=item &store_course_settings
+=item * &store_course_settings()
 
 Restores/Store indicated form parameters from the course environment.
 Will not overwrite existing values of the form parameters.
@@ -7556,6 +7818,8 @@ a hash ref describing the data to be sto
 
 Returns: both routines return nothing
 
+=back
+
 =cut
 
 #######################################################
@@ -7608,7 +7872,7 @@ sub store_settings {
                                  'got error:'.$put_result);
     }
     # Make sure these settings stick around in this session, too
-    &Apache::lonnet::appenv(%AppHash);
+    &Apache::lonnet::appenv(\%AppHash);
     return;
 }
 
@@ -7636,12 +7900,82 @@ sub restore_settings {
     }
 }
 
+#######################################################
+#######################################################
+
+=pod
+
+=head1 Domain E-mail Routines  
+
+=over 4
+
+=item * &build_recipient_list()
+
+Build recipient lists for three types of e-mail:
+(a) Error Reports, (b) Package Updates, (c) Help requests, generated by
+lonerrorhandler.pm, CHECKRPMS and lonsupportreq.pm respectively.
+
+Inputs:
+defmail (scalar - email address of default recipient), 
+mailing type (scalar - errormail, packagesmail, or helpdeskmail), 
+defdom (domain for which to retrieve configuration settings),
+origmail (scalar - email address of recipient from loncapa.conf, 
+i.e., predates configuration by DC via domainprefs.pm 
+
+Returns: comma separated list of addresses to which to send e-mail.   
+
+=cut
+
+############################################################
+############################################################
+sub build_recipient_list {
+    my ($defmail,$mailing,$defdom,$origmail) = @_;
+    my @recipients;
+    my $otheremails;
+    my %domconfig =
+         &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
+    if (ref($domconfig{'contacts'}) eq 'HASH') {
+        if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
+            my @contacts = ('adminemail','supportemail');
+            foreach my $item (@contacts) {
+                if ($domconfig{'contacts'}{$mailing}{$item}) {
+                    my $addr = $domconfig{'contacts'}{$item}; 
+                    if (!grep(/^\Q$addr\E$/,@recipients)) {
+                        push(@recipients,$addr);
+                    }
+                }
+                $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
+            }
+        }
+    } elsif ($origmail ne '') {
+        push(@recipients,$origmail);
+    }
+    if ($defmail ne '') {
+        push(@recipients,$defmail);
+    }
+    if ($otheremails) {
+        my @others;
+        if ($otheremails =~ /,/) {
+            @others = split(/,/,$otheremails);
+        } else {
+            push(@others,$otheremails);
+        }
+        foreach my $addr (@others) {
+            if (!grep(/^\Q$addr\E$/,@recipients)) {
+                push(@recipients,$addr);
+            }
+        }
+    }
+    my $recipientlist = join(',',@recipients); 
+    return $recipientlist;
+}
+
 ############################################################
 ############################################################
 
 sub commit_customrole {
     my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_;
-    my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.'@'.$three.' in '.$url.
+    my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
                          ($start?', '.&mt('starting').' '.localtime($start):'').
                          ($end?', ending '.localtime($end):'').': <b>'.
               &Apache::lonnet::assigncustomrole(
@@ -7662,8 +7996,8 @@ sub commit_standardrole {
         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
                                          $one,$two,$sec,$context);
         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
-            ($result eq 'unknown_course')) {
-            $output = "Error: $result\n"; 
+            ($result eq 'unknown_course') || ($result eq 'refused')) {
+            $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
         } else {
             $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
                ($start?', '.&mt('starting').' '.localtime($start):'').
@@ -7692,7 +8026,7 @@ sub commit_standardrole {
 
 sub commit_studentrole {
     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
-    my ($result,$linefeed);
+    my ($result,$linefeed,$oldsecurl,$newsecurl);
     if ($context eq 'auto') {
         $linefeed = "\n";
     } else {
@@ -7704,15 +8038,36 @@ sub commit_studentrole {
         my $secchange = 0;
         my $expire_role_result;
         my $modify_section_result;
-        unless ($oldsec eq '-1') {
-            unless ($sec eq $oldsec) {
+        if ($oldsec ne '-1') { 
+            if ($oldsec ne $sec) {
                 $secchange = 1;
+                my $now = time;
                 my $uurl='/'.$cid;
                 $uurl=~s/\_/\//g;
                 if ($oldsec) {
                     $uurl.='/'.$oldsec;
                 }
-                $expire_role_result = &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',time);
+                $oldsecurl = $uurl;
+                $expire_role_result = 
+                    &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now);
+                if ($env{'request.course.sec'} ne '') { 
+                    if ($expire_role_result eq 'refused') {
+                        my @roles = ('st');
+                        my @statuses = ('previous');
+                        my @roledoms = ($one);
+                        my $withsec = 1;
+                        my %roleshash = 
+                            &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
+                                              \@statuses,\@roles,\@roledoms,$withsec);
+                        if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
+                            my ($oldstart,$oldend) = 
+                                split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
+                            if ($oldend > 0 && $oldend <= $now) {
+                                $expire_role_result = 'ok';
+                            }
+                        }
+                    }
+                }
                 $result = $expire_role_result;
             }
         }
@@ -7720,21 +8075,55 @@ sub commit_studentrole {
             $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid);
             if ($modify_section_result =~ /^ok/) {
                 if ($secchange == 1) {
-                    $$logmsg .= "Section for $uname switched from old section: $oldsec to new section: $sec".$linefeed;
+                    if ($sec eq '') {
+                        $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
+                    } else {
+                        $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
+                    }
                 } elsif ($oldsec eq '-1') {
-                    $$logmsg .= "New student role for $uname in section $sec in course $cid".$linefeed;
+                    if ($sec eq '') {
+                        $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
+                    } else {
+                        $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
+                    }
                 } else {
-                    $$logmsg .= "Student $uname assigned to unchanged section $sec in course $cid".$linefeed;
+                    if ($sec eq '') {
+                        $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
+                    } else {
+                        $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
+                    }
                 }
             } else {
-                $$logmsg .= "Error when attempting section change for $uname from old section $oldsec to new section: $sec in course $cid -error: $modify_section_result".$linefeed;
+                if ($secchange) {       
+                    $$logmsg .= &mt('Error when attempting section change for [_1] from old section "[_2]" to new section: "[_3]" in course [_4] -error:',$uname,$oldsec,$sec,$cid).' '.$modify_section_result.$linefeed;
+                } else {
+                    $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
+                }
             }
             $result = $modify_section_result;
         } elsif ($secchange == 1) {
-            $$logmsg .= "Error when attempting to expire role for $uname in old section $oldsec in course $cid -error: $expire_role_result".$linefeed;
+            if ($oldsec eq '') {
+                $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_3] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
+            } else {
+                $$logmsg .= &mt('Error when attempting to expire existing role for [_1] in section [_2] in course [_3] -error: ',$uname,$oldsec,$cid).' '.$expire_role_result.$linefeed;
+            }
+            if ($expire_role_result eq 'refused') {
+                my $newsecurl = '/'.$cid;
+                $newsecurl =~ s/\_/\//g;
+                if ($sec ne '') {
+                    $newsecurl.='/'.$sec;
+                }
+                if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
+                    if ($sec eq '') {
+                        $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments unaffiliated with any section.',$sec).$linefeed;
+                    } else {
+                        $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments in other sections.',$sec).$linefeed;
+                    }
+                }
+            }
         }
     } else {
-        $$logmsg .= "Incomplete course id defined.  Addition of user $uname from domain $udom to course $one\_$two, section $sec not completed.$linefeed";
+        $$logmsg .= &mt('Incomplete course id defined.').$linefeed.&mt('Addition of user [_1] from domain [_2] to course [_3], section [_4] not completed.',$uname,$udom,$one.'_'.$two,$sec).$linefeed;
         $result = "error: incomplete course id\n";
     }
     return $result;
@@ -7846,19 +8235,26 @@ sub construct_course {
 	$outcome .= $clonemsg.$linefeed;
 	my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
 # Copy all files
-	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid);
+	&Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
 # Restore URL
 	$cenv{'url'}=$oldcenv{'url'};
 # Restore title
 	$cenv{'description'}=$oldcenv{'description'};
-# restore grading mode
-	if (defined($oldcenv{'grading'})) {
-	    $cenv{'grading'}=$oldcenv{'grading'};
-	}
 # Mark as cloned
 	$cenv{'clonedfrom'}=$cloneid;
-	delete($cenv{'default_enrollment_start_date'});
-	delete($cenv{'default_enrollment_end_date'});
+# Need to clone grading mode
+        my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
+        $cenv{'grading'}=$newenv{'grading'};
+# Do not clone these environment entries
+        &Apache::lonnet::del('environment',
+                  ['default_enrollment_start_date',
+                   'default_enrollment_end_date',
+                   'question.email',
+                   'policy.email',
+                   'comment.email',
+                   'pch.users.denied',
+                   'plc.users.denied'],
+                   $$crsudom,$$crsunum);
     }
 
 #
@@ -7886,7 +8282,6 @@ sub construct_course {
     } else {
         $cenv{'internal.courseowner'} = $args->{'curruser'};
     }
-
     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
     if ($args->{'crssections'}) {
         $cenv{'internal.sectionnums'} = '';
@@ -7946,7 +8341,7 @@ sub construct_course {
     }
     if ($args->{'notify_dc'}) {
         if ($uname ne '') { 
-            push(@notified,$uname.'@'.$udom);
+            push(@notified,$uname.':'.$udom);
         }
     }
     if (@notified > 0) {