--- loncom/interface/loncommon.pm	2007/08/31 17:58:47	1.577
+++ loncom/interface/loncommon.pm	2007/10/18 21:57:59	1.593
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.577 2007/08/31 17:58:47 raeburn Exp $
+# $Id: loncommon.pm,v 1.593 2007/10/18 21:57:59 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -860,7 +860,7 @@ sub help_menu_js {
 					'js_ready'    => 1,
 					'add_entries' => {
 					    'border' => '0',
-					    'rows'   => "105,*",},});
+					    'rows'   => "110,*",},});
     my $end_page =
         &Apache::loncommon::end_page({'frameset' => 1,
 				      'js_ready' => 1,});
@@ -1074,6 +1074,77 @@ sub changable_area {
 
 =pod
 
+=item * viewport_geometry_js {
+
+Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
+
+=cut
+
+
+sub viewport_geometry_js { 
+    return <<"GEOMETRY";
+var Geometry = {};
+function init_geometry() {
+    if (Geometry.init) { return };
+    Geometry.init=1;
+    if (window.innerHeight) {
+        Geometry.getViewportHeight   = function() { return window.innerHeight; };
+        Geometry.getViewportWidth   = function() { return window.innerWidth; };
+        Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
+        Geometry.getVerticalScroll   = function() { return window.pageYOffset; };
+    }
+    else if (document.documentElement && document.documentElement.clientHeight) {
+        Geometry.getViewportHeight =
+            function() { return document.documentElement.clientHeight; };
+        Geometry.getViewportWidth =
+            function() { return document.documentElement.clientWidth; };
+
+        Geometry.getHorizontalScroll =
+            function() { return document.documentElement.scrollLeft; };
+        Geometry.getVerticalScroll =
+            function() { return document.documentElement.scrollTop; };
+    }
+    else if (document.body.clientHeight) {
+        Geometry.getViewportHeight =
+            function() { return document.body.clientHeight; };
+        Geometry.getViewportWidth =
+            function() { return document.body.clientWidth; };
+        Geometry.getHorizontalScroll =
+            function() { return document.body.scrollLeft; };
+        Geometry.getVerticalScroll =
+            function() { return document.body.scrollTop; };
+    }
+}
+
+GEOMETRY
+}
+
+=pod
+
+=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. 
+
+=cut
+
+sub viewport_size_js {
+    my $geometry = &viewport_geometry_js();
+    return <<"DIMS";
+
+$geometry
+
+function getViewportDims(width,height) {
+    init_geometry();
+    width.value = Geometry.getViewportWidth();
+    height.value = Geometry.getViewportHeight();
+    return;
+}
+
+DIMS
+}
+
+=pod
+
 =item * resize_textarea_js
 
 emits the needed javascript to resize a textarea to be as big as possible
@@ -1087,37 +1158,41 @@ to be attached to the <body> for the onl
 =cut
 
 sub resize_textarea_js {
+    my $geometry = &viewport_geometry_js();
     return <<"RESIZE";
     <script type="text/javascript">
-var Geometry = {};
-function init_geometry() {
-    if (Geometry.init) { return };
-    Geometry.init=1;
-    if (window.innerHeight) {
-	Geometry.getViewportHeight = function() { return window.innerHeight; };
-    }
-    else if (document.documentElement && document.documentElement.clientHeight) {
-	Geometry.getViewportHeight = 
-	    function() { return document.documentElement.clientHeight; };
-    }
-    else if (document.body.clientHeight) {
-	Geometry.getViewportHeight = 
-	    function() { return document.body.clientHeight; };
+$geometry
+
+function getX(element) {
+    var x = 0;
+    while (element) {
+	x += element.offsetLeft;
+	element = element.offsetParent;
+    }
+    return x;
+}
+function getY(element) {
+    var y = 0;
+    while (element) {
+	y += element.offsetTop;
+	element = element.offsetParent;
     }
+    return y;
 }
 
+
 function resize_textarea(textarea_id,bottom_id) {
     init_geometry();
     var textarea        = document.getElementById(textarea_id);
     //alert(textarea);
 
-    var textarea_top    = textarea.offsetTop;
+    var textarea_top    = getY(textarea);
     var textarea_height = textarea.offsetHeight;
     var bottom          = document.getElementById(bottom_id);
-    var bottom_top      = bottom.offsetTop;
+    var bottom_top      = getY(bottom);
     var bottom_height   = bottom.offsetHeight;
     var window_height   = Geometry.getViewportHeight();
-    var fudge           = 23; 
+    var fudge           = 23;
     var new_height      = window_height-fudge-textarea_top-bottom_height;
     if (new_height < 300) {
 	new_height = 300;
@@ -1332,8 +1407,6 @@ sub domain_select {
 
 =over 4
 
-=cut
-
 =item * multiple_select_form($name,$value,$size,$hash,$order)
 
 Returns a string containing a <select> element int multiple mode
@@ -1511,24 +1584,68 @@ sub select_dom_form {
 
 =pod
 
-=item * home_server_option_list($domain)
+=item * home_server_form_item($domain,$name,$defaultflag)
+
+input: 4 arguments (two required, two optional) - 
+    $domain - domain of new user
+    $name - name of form element
+    $default - Value of 'default' causes a default item to be first 
+                            option, and selected by default. 
+    $hide - Value of 'hide' causes hiding of the name of the server, 
+                            if 1 server found, or default, if 0 found.
+output: returns 1 items: 
+(a) form element which contains either:
+   (i) <select name="$name">
+        <option value="$hostid1">$hostid $servers{$hostid}</option>
+        <option value="$hostid2">$hostid $servers{$hostid}</option>       
+       </select>
+       form item if there are multiple library servers in $domain, or
+   (ii) an <input type="hidden" name="$name" value="$hostid" /> form item 
+       if there is only one library server in $domain.
 
-returns a string which contains an <option> list to be used in a 
-<select> form input.  See loncreateuser.pm for an example.
+(b) number of library servers found.
+
+See loncreateuser.pm for example of use.
 
 =cut
 
 #-------------------------------------------
-sub home_server_option_list {
-    my $domain = shift;
+sub home_server_form_item {
+    my ($domain,$name,$default,$hide) = @_;
     my %servers = &Apache::lonnet::get_servers($domain,'library');
-    my $result = '';
-    foreach my $hostid (sort(keys(%servers))) {
-        $result.=
-            '<option value="'.$hostid.'">'.
-	    $hostid.' '.$servers{$hostid}."</option>\n";
+    my $result;
+    my $numlib = keys(%servers);
+    if ($numlib > 1) {
+        $result .= '<select name="'.$name.'" />'."\n";
+        if ($default) {
+            $result .= '<option value="default" selected>'.&mt('default').
+                       '</option>'."\n";
+        }
+        foreach my $hostid (sort(keys(%servers))) {
+            $result.= '<option value="'.$hostid.'">'.
+	              $hostid.' '.$servers{$hostid}."</option>\n";
+        }
+        $result .= '</select>'."\n";
+    } elsif ($numlib == 1) {
+        my $hostid;
+        foreach my $item (keys(%servers)) {
+            $hostid = $item;
+        }
+        $result .= '<input type="hidden" name="'.$name.'" value="'.
+                   $hostid.'" />';
+                   if (!$hide) {
+                       $result .= $hostid.' '.$servers{$hostid};
+                   }
+                   $result .= "\n";
+    } elsif ($default) {
+        $result .= '<input type="hidden" name="'.$name.
+                   '" value="default" />';
+                   if (!$hide) {
+                       $result .= &mt('default');
+                   }
+                   $result .= "\n";
     }
-    return $result;
+    return ($result,$numlib);
 }
 
 =pod
@@ -1681,19 +1798,16 @@ END
     }
 
     my $radioval = "'nochange'";
-    if (exists($in{'curr_authtype'}) &&
-        defined($in{'curr_authtype'}) &&
-        $in{'curr_authtype'} ne '') {
-        $radioval = "'$in{'curr_authtype'}arg'";
+    if (defined($in{'curr_authtype'})) {
+        if ($in{'curr_authtype'} ne '') {
+            $radioval = "'".$in{'curr_authtype'}."arg'";
+        }
     }
     my $argfield = 'null';
-    if ( grep/^mode$/,(keys %in) ) {
+    if (defined($in{'mode'})) {
         if ($in{'mode'} eq 'modifycourse')  {
-            if ( grep/^curr_authtype$/,(keys %in) ) {
-                $radioval = "'$in{'curr_authtype'}'";
-            }
-            if ( grep/^curr_autharg$/,(keys %in) ) {
-                unless ($in{'curr_autharg'} eq '') {
+            if (defined($in{'curr_autharg'})) {
+                if ($in{'curr_autharg'} ne '') {
                     $argfield = "'$in{'curr_autharg'}'";
                 }
             }
@@ -1776,79 +1890,170 @@ sub authform_nochange{
               kerb_def_dom => 'MSU.EDU',
               @_,
           );
-    my $result = '<label>'.&mt('[_1] Do not change login data',
-                     '<input type="radio" name="login" value="nochange" '.
-                     'checked="checked" onclick="'.
+    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'}); 
+    my $result;
+    if (keys(%can_assign) == 0) {
+        $result = &mt('Under you current role you are not permitted to change login settings for this user');  
+    } else {
+        $result = '<label>'.&mt('[_1] Do not change login data',
+                  '<input type="radio" name="login" value="nochange" '.
+                  'checked="checked" onclick="'.
             "javascript:changed_radio('nochange',$in{'formname'});".'" />').
 	    '</label>';
+    }
     return $result;
 }
 
-sub authform_kerberos{  
+sub authform_kerberos {
     my %in = (
               formname => 'document.cu',
               kerb_def_dom => 'MSU.EDU',
               kerb_def_auth => 'krb4',
               @_,
               );
-    my ($check4,$check5,$krbarg);
+    my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
+        $autharg,$jscall);
+    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
     if ($in{'kerb_def_auth'} eq 'krb5') {
-       $check5 = " checked=\"on\"";
+       $check5 = ' checked="on"';
     } else {
-       $check4 = " checked=\"on\"";
+       $check4 = ' checked="on"';
     }
     $krbarg = $in{'kerb_def_dom'};
-
-    my $krbcheck = "";
-    if ( grep/^curr_authtype$/,(keys %in) ) {
-        if ($in{'curr_authtype'} =~ m/^krb/) {
-            $krbcheck = " checked=\"on\"";
-            if ( grep/^curr_autharg$/,(keys %in) ) {
+    if (defined($in{'curr_authtype'})) {
+        if ($in{'curr_authtype'} eq 'krb') {
+            $krbcheck = ' checked="on"';
+            if (defined($in{'curr_kerb_ver'})) {
+                if ($in{'curr_krb_ver'} eq '5') {
+                    $check5 = ' checked="on"';
+                    $check4 = '';
+                } else {
+                    $check4 = ' checked="on"';
+                    $check5 = '';
+                }
+            }
+            if (defined($in{'curr_autharg'})) {
                 $krbarg = $in{'curr_autharg'};
             }
+            if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
+                if (defined($in{'curr_autharg'})) {
+                    $result = 
+    &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
+        $in{'curr_autharg'},$krbver);
+                } else {
+                    $result =
+    &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
+                }
+                return $result; 
+            }
+        }
+    } else {
+        if ($authnum == 1) {
+            $authtype = '<input type="hidden" name="login" value="krb">';
         }
     }
-
-    my $jscall = "javascript:changed_radio('krb',$in{'formname'});";
-    my $result .= &mt
+    if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
+        return;
+    } elsif ($authtype eq '') {
+        if (defined($in{'mode'})) {
+            if ($in{'mode'} eq 'modifycourse') {
+                if ($authnum == 1) {
+                    $authtype = '<input type="hidden" name="login" value="krb">';
+                }
+            }
+        }
+    }
+    $jscall = "javascript:changed_radio('krb',$in{'formname'});";
+    if ($authtype eq '') {
+        $authtype = '<input type="radio" name="login" value="krb" '.
+                    'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
+                    $krbcheck.' />';
+    }
+    if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
+        ($can_assign{'krb4'} && !$can_assign{'krb5'} && 
+         $in{'curr_authtype'} eq 'krb5') ||
+        (!$can_assign{'krb4'} && $can_assign{'krb5'} && 
+         $in{'curr_authtype'} eq 'krb4')) {
+        $result .= &mt
         ('[_1] Kerberos authenticated with domain [_2] '.
          '[_3] Version 4 [_4] Version 5 [_5]',
-         '<label><input type="radio" name="login" value="krb" '.
-             'onclick="'.$jscall.'" onchange="'.$jscall.'"'.$krbcheck.' />',
+         '<label>'.$authtype,
          '</label><input type="text" size="10" name="krbarg" '.
              'value="'.$krbarg.'" '.
              'onchange="'.$jscall.'" />',
          '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
          '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
 	 '</label>');
+    } elsif ($can_assign{'krb4'}) {
+        $result .= &mt
+        ('[_1] Kerberos authenticated with domain [_2] '.
+         '[_3] Version 4 [_4]',
+         '<label>'.$authtype,
+         '</label><input type="text" size="10" name="krbarg" '.
+             'value="'.$krbarg.'" '.
+             'onchange="'.$jscall.'" />',
+         '<label><input type="hidden" name="krbver" value="4" />',
+         '</label>');
+    } elsif ($can_assign{'krb5'}) {
+        $result .= &mt
+        ('[_1] Kerberos authenticated with domain [_2] '.
+         '[_3] Version 5 [_4]',
+         '<label>'.$authtype,
+         '</label><input type="text" size="10" name="krbarg" '.
+             'value="'.$krbarg.'" '.
+             'onchange="'.$jscall.'" />',
+         '<label><input type="hidden" name="krbver" value="5" />',
+         '</label>');
+    }
     return $result;
 }
 
 sub authform_internal{  
-    my %args = (
+    my %in = (
                 formname => 'document.cu',
                 kerb_def_dom => 'MSU.EDU',
                 @_,
                 );
-
-    my $intcheck = "";
-    my $intarg = 'value=""';
-    if ( grep/^curr_authtype$/,(keys %args) ) {
-        if ($args{'curr_authtype'} eq 'int') {
-            $intcheck = " checked=\"on\"";
-            if ( grep/^curr_autharg$/,(keys %args) ) {
-                $intarg = "value=\"$args{'curr_autharg'}\"";
+    my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
+    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
+    if (defined($in{'curr_authtype'})) {
+        if ($in{'curr_authtype'} eq 'int') {
+            if ($can_assign{'int'}) {
+                $intcheck = 'checked="on" ';
+                if (defined($in{'curr_autharg'})) {
+                    $intarg = $in{'curr_autharg'};
+                }
+            } else {
+                $result = &mt('Currently internally authenticated.');
+                return $result;
             }
         }
+    } else {
+        if ($authnum == 1) {
+            $authtype = '<input type="hidden" name="login" value="int">';
+        }
     }
-
-    my $jscall = "javascript:changed_radio('int',$args{'formname'});";
-    my $result.=&mt
+    if (!$can_assign{'int'}) {
+        return;
+    } elsif ($authtype eq '') {
+        if (defined($in{'mode'})) {
+            if ($in{'mode'} eq 'modifycourse') {
+                if ($authnum == 1) {
+                    $authtype = '<input type="hidden" name="login" value="int">';
+                }
+            }
+        }
+    }
+    $jscall = "javascript:changed_radio('int',$in{'formname'});";
+    if ($authtype eq '') {
+        $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
+                    ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
+    }
+    $autharg = '<input type="text" size="10" name="intarg" value="'.
+               $intarg.'" onchange="'.$jscall.'" />';
+    $result = &mt
         ('[_1] Internally authenticated (with initial password [_2])',
-         '<label><input type="radio" name="login" value="int" '.$intcheck.
-             ' onchange="'.$jscall.'" onclick="'.$jscall.'" />',
-         '</label><input type="text" size="10" name="intarg" '.$intarg.
-             ' onchange="'.$jscall.'" />');
+         '<label>'.$authtype,'</label>'.$autharg);
     return $result;
 }
 
@@ -1858,24 +2063,46 @@ sub authform_local{
               kerb_def_dom => 'MSU.EDU',
               @_,
               );
-
-    my $loccheck = "";
-    my $locarg = 'value=""';
-    if ( grep/^curr_authtype$/,(keys %in) ) {
+    my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
+    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
+    if (defined($in{'curr_authtype'})) {
         if ($in{'curr_authtype'} eq 'loc') {
-            $loccheck = " checked=\"on\"";
-            if ( grep/^curr_autharg$/,(keys %in) ) {
-                $locarg = "value=\"$in{'curr_autharg'}\"";
+            if ($can_assign{'loc'}) {
+                $loccheck = 'checked="on" ';
+                if (defined($in{'curr_autharg'})) {
+                    $locarg = $in{'curr_autharg'};
+                }
+            } else {
+                $result = &mt('Currently using local (institutional) authentication.');
+                return $result;
             }
         }
+    } else {
+        if ($authnum == 1) {
+            $authtype = '<input type="hidden" name="login" value="loc">';
+        }
     }
-
-    my $jscall = "javascript:changed_radio('loc',$in{'formname'});";
-    my $result.=&mt('[_1] Local Authentication with argument [_2]',
-                    '<label><input type="radio" name="login" value="loc" '.$loccheck.
-                        ' onchange="'.$jscall.'" onclick="'.$jscall.'" />',
-                    '</label><input type="text" size="10" name="locarg" '.$locarg.
-                        ' onchange="'.$jscall.'" />');
+    if (!$can_assign{'loc'}) {
+        return;
+    } elsif ($authtype eq '') {
+        if (defined($in{'mode'})) {
+            if ($in{'mode'} eq 'modifycourse') {
+                if ($authnum == 1) {
+                    $authtype = '<input type="hidden" name="login" value="loc">';
+                }
+            }
+        }
+    }
+    $jscall = "javascript:changed_radio('loc',$in{'formname'});";
+    if ($authtype eq '') {
+        $authtype = '<input type="radio" name="login" value="loc" '.
+                    $loccheck.' onchange="'.$jscall.'" onclick="'.
+                    $jscall.'" />';
+    }
+    $autharg = '<input type="text" size="10" name="locarg" value="'.
+               $locarg.'" onchange="'.$jscall.'" />';
+    $result = &mt('[_1] Local Authentication with argument [_2]',
+                  '<label>'.$authtype,'</label>'.$autharg);
     return $result;
 }
 
@@ -1885,16 +2112,92 @@ sub authform_filesystem{
               kerb_def_dom => 'MSU.EDU',
               @_,
               );
-    my $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
-    my $result.= &mt
+    my ($fsyscheck,$result,$authtype,$autharg,$jscall);
+    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
+    if (defined($in{'curr_authtype'})) {
+        if ($in{'curr_authtype'} eq 'fsys') {
+            if ($can_assign{'fsys'}) {
+                $fsyscheck = 'checked="on" ';
+            } else {
+                $result = &mt('Currently Filesystem Authenticated.');
+                return $result;
+            }           
+        }
+    } else {
+        if ($authnum == 1) {
+            $authtype = '<input type="hidden" name="login" value="fsys">';
+        }
+    }
+    if (!$can_assign{'fsys'}) {
+        return;
+    } elsif ($authtype eq '') {
+        if (defined($in{'mode'})) {
+            if ($in{'mode'} eq 'modifycourse') {
+                if ($authnum == 1) {
+                    $authtype = '<input type="hidden" name="login" value="fsys">';
+                }
+            }
+        }
+    }
+    $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
+    if ($authtype eq '') {
+        $authtype = '<input type="radio" name="login" value="fsys" '.
+                    $fsyscheck.' onchange="'.$jscall.'" onclick="'.
+                    $jscall.'" />';
+    }
+    $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
+               ' onchange="'.$jscall.'" />';
+    $result = &mt
         ('[_1] Filesystem Authenticated (with initial password [_2])',
          '<label><input type="radio" name="login" value="fsys" '.
-         'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
+         $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
          '</label><input type="text" size="10" name="fsysarg" value="" '.
                   'onchange="'.$jscall.'" />');
     return $result;
 }
 
+sub get_assignable_auth {
+    my ($dom) = @_;
+    if ($dom eq '') {
+        $dom = $env{'request.role.domain'};
+    }
+    my %can_assign = (
+                          krb4 => 1,
+                          krb5 => 1,
+                          int  => 1,
+                          loc  => 1,
+                     );
+    my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
+    if (ref($domconfig{'usercreation'}) eq 'HASH') {
+        if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
+            my $authhash = $domconfig{'usercreation'}{'authtypes'};
+            my $context;
+            if ($env{'request.role'} =~ /^au/) {
+                $context = 'author';
+            } elsif ($env{'request.role'} =~ /^dc/) {
+                $context = 'domain';
+            } elsif ($env{'request.course.id'}) {
+                $context = 'course';
+            }
+            if ($context) {
+                if (ref($authhash->{$context}) eq 'HASH') {
+                   %can_assign = %{$authhash->{$context}}; 
+                }
+            }
+        }
+    }
+    my $authnum = 0;
+    foreach my $key (keys(%can_assign)) {
+        if ($can_assign{$key}) {
+            $authnum ++;
+        }
+    }
+    if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
+        $authnum --;
+    }
+    return ($authnum,%can_assign);
+}
+
 ###############################################################
 ##    Get Authentication Defaults for Domain                 ##
 ###############################################################
@@ -2020,7 +2323,7 @@ sub initialize_keywords {
     # Remove special values from %Keywords.
     foreach my $value ('total.count','average.count') {
         delete($Keywords{$value}) if (exists($Keywords{$value}));
-    }
+  }
     return 1;
 }
 
@@ -2531,9 +2834,11 @@ sub preferred_languages {
 	@languages=(@languages,
 		    split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'}));
     }
-    my $browser=(split(/\;/,$ENV{'HTTP_ACCEPT_LANGUAGE'}))[0];
+    my $browser=$ENV{'HTTP_ACCEPT_LANGUAGE'};
     if ($browser) {
-	@languages=(@languages,split(/\s*(\,|\;|\:)\s*/,$browser));
+	my @browser = 
+	    map { (split(/\s*;\s*/,$_))[0] } (split(/\s*,\s*/,$browser));
+	push(@languages,@browser);
     }
     if (&Apache::lonnet::domain($env{'user.domain'},'lang_def')) {
 	@languages=(@languages,
@@ -2555,14 +2860,40 @@ sub preferred_languages {
     my @genlanguages;
     foreach my $lang (@languages) {
 	unless ($lang=~/\w/) { next; }
-	push (@genlanguages,$lang);
+	push(@genlanguages,$lang);
 	if ($lang=~/(\-|\_)/) {
 	    push(@genlanguages,(split(/(\-|\_)/,$lang))[0]);
 	}
     }
+    #uniqueify the languages list
+    my %count;
+    @genlanguages = map { $count{$_}++ == 0 ? $_ : () } @genlanguages;
     return @genlanguages;
 }
 
+sub languages {
+    my ($possible_langs) = @_;
+    my @preferred_langs = &preferred_languages();
+    if (!ref($possible_langs)) {
+	if( wantarray ) {
+	    return @preferred_langs;
+	} else {
+	    return $preferred_langs[0];
+	}
+    }
+    my %possibilities = map { $_ => 1 } (@$possible_langs);
+    my @preferred_possibilities;
+    foreach my $preferred_lang (@preferred_langs) {
+	if (exists($possibilities{$preferred_lang})) {
+	    push(@preferred_possibilities, $preferred_lang);
+	}
+    }
+    if( wantarray ) {
+	return @preferred_possibilities;
+    }
+    return $preferred_possibilities[0];
+}
+
 ###############################################################
 ##               Student Answer Attempts                     ##
 ###############################################################
@@ -2637,25 +2968,15 @@ sub get_previous_attempt {
 	for ($version=1;$version<=$returnhash{'version'};$version++) {
 	  $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Transaction '.$version.'</td>';
 	    foreach my $key (sort(keys(%lasthash))) {
-	       my $value;
-	       if ($key =~ /timestamp/) {
-		  $value=scalar(localtime($returnhash{$version.':'.$key}));
-	       } else {
-		  $value=$returnhash{$version.':'.$key};
-	       }
-	       $prevattempts.='<td>'.&unescape($value).'&nbsp;</td>';   
+		my $value = &format_previous_attempt_value($key,
+							   $returnhash{$version.':'.$key});
+		$prevattempts.='<td>'.$value.'&nbsp;</td>';   
 	    }
 	 }
       }
       $prevattempts.='</tr><tr bgcolor="#ffffe6"><td>Current</td>';
       foreach my $key (sort(keys(%lasthash))) {
-	my $value;
-	if ($key =~ /timestamp/) {
-	  $value=scalar(localtime($lasthash{$key}));
-	} else {
-	  $value=$lasthash{$key};
-	}
-	$value=&unescape($value);
+	my $value = &format_previous_attempt_value($key,$lasthash{$key});
 	if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
 	$prevattempts.='<td>'.$value.'&nbsp;</td>';
       }
@@ -2668,6 +2989,19 @@ sub get_previous_attempt {
   }
 }
 
+sub format_previous_attempt_value {
+    my ($key,$value) = @_;
+    if ($key =~ /timestamp/) {
+	$value = &Apache::lonlocal::locallocaltime($value);
+    } elsif (ref($value) eq 'ARRAY') {
+	$value = '('.join(', ', @{ $value }).')';
+    } else {
+	$value = &unescape($value);
+    }
+    return $value;
+}
+
+
 sub relative_to_absolute {
     my ($url,$output)=@_;
     my $parser=HTML::TokeParser->new(\$output);
@@ -3795,7 +4129,7 @@ table.thinborder tr td {
 
 form, .inline { display: inline; }
 .center { text-align: center; }
-.LC_filename {font-family: $mono;}
+.LC_filename {font-family: $mono; white-space:pre;}
 .LC_error {
   color: red;
   font-size: larger;
@@ -4041,13 +4375,13 @@ table.LC_data_table, table.LC_mail_list
 }
 table.LC_nested_outer {
   border: 1px solid #000000;
-  border-collapse: separate;
+  border-collapse: collapse;
   border-spacing: 0px;
   width: 100%;
 }
 table.LC_nested {
   border: 0px;
-  border-collapse: separate;
+  border-collapse: collapse;
   border-spacing: 0px;
   width: 100%;
 }
@@ -4099,7 +4433,8 @@ table.LC_nested tr.LC_info_row td {
   font-size: small;
   text-align: center;
 }
-table.LC_nested tr.LC_info_row td.LC_left_item {
+table.LC_nested tr.LC_info_row td.LC_left_item,
+table.LC_nested_outer tr th.LC_left_item {
   text-align: left;
 }
 table.LC_nested td {
@@ -4330,9 +4665,7 @@ table#LC_helpmenu_links a:hover {
   border: 1px solid #8888FF;
   background: #CCCCFF;
 }
-
 table.LC_pick_box {
-  width: 100%;
   border-collapse: separate;
   background: white;
   border: 1px solid black;
@@ -4345,6 +4678,14 @@ table.LC_pick_box td.LC_pick_box_title {
   width: 184px;
   padding: 8px;
 }
+table.LC_pick_box td.LC_pick_box_value {
+  text-align: left;
+  padding: 8px;
+}
+table.LC_pick_box td.LC_pick_box_select {
+  text-align: left;
+  padding: 8px;
+}
 table.LC_pick_box td.LC_pick_box_separator {
   padding: 0px;
   height: 1px;
@@ -4353,7 +4694,48 @@ table.LC_pick_box td.LC_pick_box_separat
 table.LC_pick_box td.LC_pick_box_submit {
   text-align: right;
 }
-
+table.LC_pick_box td.LC_evenrow_value {
+  text-align: left;
+  padding: 8px;
+  background-color: $data_table_light;
+}
+table.LC_pick_box td.LC_oddrow_value {
+  text-align: left;
+  padding: 8px;
+  background-color: $data_table_light;
+}
+table.LC_helpform_receipt {
+  width: 620px;
+  border-collapse: separate;
+  background: white;
+  border: 1px solid black;
+  border-spacing: 1px;
+}
+table.LC_helpform_receipt td.LC_pick_box_title {
+  background: $tabbg;
+  font-weight: bold;
+  text-align: right;
+  width: 184px;
+  padding: 8px;
+}
+table.LC_helpform_receipt td.LC_evenrow_value {
+  text-align: left;
+  padding: 8px;
+  background-color: $data_table_light;
+}
+table.LC_helpform_receipt td.LC_oddrow_value {
+  text-align: left;
+  padding: 8px;
+  background-color: $data_table_light;
+}
+table.LC_helpform_receipt td.LC_pick_box_separator {
+  padding: 0px;
+  height: 1px;
+  background: black;
+}
+span.LC_helpform_receipt_cat {
+  font-weight: bold;
+}
 table.LC_group_priv_box {
   background: white;
   border: 1px solid black;
@@ -4569,6 +4951,49 @@ table.LC_docs_adddocs th {
   background: #DDDDDD;
 }
 
+table.LC_sty_begin {
+  background: #BBFFBB;
+}
+table.LC_sty_end {
+  background: #FFBBBB;
+}
+
+table.LC_double_column {
+  border-width: 0px;
+  border-collapse: collapse;
+  width: 100%;
+  padding: 2px;
+}
+
+table.LC_double_column tr td.LC_left_col {
+  top: 2px;
+  left: 2px;
+  width: 47%;
+  vertical-align: top;
+}
+
+table.LC_double_column tr td.LC_right_col {
+  top: 2px;
+  right: 2px; 
+  width: 47%;
+  vertical-align: top;
+}
+
+div.LC_left_float {
+  float: left;
+  padding-right: 5%;
+  padding:bottom: 4px;
+}
+
+div.LC_clear_float_header {
+  padding:bottom: 2px;
+}
+
+div.LC_clear_float_footer {
+  padding:top: 10px;
+  clear: both;
+}
+
 END
 }
 
@@ -4685,7 +5110,9 @@ Inputs: none
 sub xml_begin {
     my $output='';
 
-    &Apache::lonhtmlcommon::init_htmlareafields();
+    if ($env{'internal.start_page'}==1) {
+	&Apache::lonhtmlcommon::init_htmlareafields();
+    }
 
     if ($env{'browser.mathml'}) {
 	$output='<?xml version="1.0"?>'
@@ -5604,7 +6031,7 @@ sub user_picker {
     my $currdom = $dom;
     my %curr_selected = (
                         srchin => 'dom',
-                        srchby => 'uname',
+                        srchby => 'lastname',
                       );
     my $srchterm;
     if (ref($srch) eq 'HASH') {
@@ -5666,7 +6093,7 @@ sub user_picker {
     $srchinsel .= "\n  </select>\n";
 
     my $srchbysel =  ' <select name="srchby">';
-    foreach my $option ('uname','lastname','lastfirst') {
+    foreach my $option ('lastname','lastfirst','uname') {
         if ($curr_selected{'srchby'} eq $option) {
             $srchbysel .= '
    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
@@ -5678,7 +6105,7 @@ sub user_picker {
     $srchbysel .= "\n  </select>\n";
 
     my $srchtypesel = ' <select name="srchtype">';
-    foreach my $option ('exact','begins','contains') {
+    foreach my $option ('begins','contains','exact') {
         if ($curr_selected{'srchtype'} eq $option) {
             $srchtypesel .= '
    <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
@@ -5823,6 +6250,59 @@ END_BLOCK
     return $output;
 }
 
+sub username_rule_check {
+    my ($srch,$caller) = @_;
+    my ($response,@curr_rules,%inst_results,$rulematch);
+    my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($srch->{'srchdomain'});
+    if (ref($srch) eq 'HASH') {
+        (my $inst_response,%inst_results) = 
+            &Apache::lonnet::get_instuser($srch->{'srchdomain'},
+                                          $srch->{'srchterm'});
+        my %domconfig = &Apache::lonnet::get_dom('configuration',
+                              ['usercreation'],$srch->{'srchdomain'});
+        if (ref($domconfig{'usercreation'}) eq 'HASH') {
+            if (ref($domconfig{'usercreation'}{'username_rule'}) eq 'ARRAY') {
+                @curr_rules = @{$domconfig{'usercreation'}{'username_rule'}};
+            }
+        }
+        if (@curr_rules > 0) {
+            my $domdesc = &Apache::lonnet::domain($srch->{'srchdomain'},'description');
+            my $instuser_reqd;
+            my %rule_check = &Apache::lonnet::inst_rulecheck($srch->{'srchdomain'},$srch->{'srchterm'},\@curr_rules);
+            foreach my $rule (@curr_rules) {
+                if ($rule_check{$rule}) {
+                    $rulematch = $rule;
+                    if ($inst_response eq 'ok') {
+                        if (keys(%inst_results) == 0) {
+                            if ($caller eq 'new') {
+                                $response = &mt('The username you chose matches the format of usernames defined for <span class="LC_cusr_emph">[_1]</span>, but the user does not exist in the institutional directory.',$domdesc).'<br />'.&mt("You must choose a username with a different format -- one that will not conflict with 'official' institutional usernames.");
+                            }
+                        }
+                    }
+                    last;
+                }
+            }
+            if ($response) {
+                if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
+                    if (@{$ruleorder} > 0) {
+                        $response .= '<br />'.&mt('Usernames with the following format(s) may <span class="LC_cusr_emph">only</span> be used for verified users at [_1]:',$domdesc).' <ul>';
+                        foreach my $rule (@{$ruleorder}) {
+                            if (grep(/^\Q$rule\E$/,@curr_rules)) {
+                                if (ref($rules->{$rule}) eq 'HASH') {
+                                    $response .= '<li>'.$rules->{$rule}{'name'}.': '.
+                                                 $rules->{$rule}{'desc'}.'</li>';
+                                }
+                            }
+                        }
+                    }
+                    $response .= '</ul>';
+                }
+            }
+        }
+    }
+    return ($response,$rulematch,$rules,%inst_results);
+}
+
 =pod
 
 =back
@@ -6983,7 +7463,7 @@ sub commit_studentrole {
 ############################################################
 
 sub check_clone {
-    my ($args) = @_;
+    my ($args,$linefeed) = @_;
     my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
     my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
@@ -6991,8 +7471,7 @@ sub check_clone {
     my $can_clone = 0;
 
     if ($clonehome eq 'no_host') {
-	$clonemsg = &mt('Attempting to clone non-existing [_1]',
-			$args->{'crstype'});
+        $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});     
     } else {
 	my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
 	if ($env{'request.role.domain'} eq $args->{'clonedomain'}) {
@@ -7001,18 +7480,24 @@ sub check_clone {
 	    my %clonehash = &Apache::lonnet::get('environment',['cloners'],
 						 $args->{'clonedomain'},$args->{'clonecourse'});
 	    my @cloners = split(/,/,$clonehash{'cloners'});
-	    my %roleshash =
-		&Apache::lonnet::get_my_roles($args->{'ccuname'},
-					      $args->{'ccdomain'},'userroles',['active'],['cc'],
-					      [$args->{'clonedomain'}]);
-	    if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':cc'}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
-		$can_clone = 1;
-	    } else {
-		$clonemsg = &mt('The new course was not cloned from an existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
+            if (grep(/^\*$/,@cloners)) {
+                $can_clone = 1;
+            } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
+                $can_clone = 1;
+            } else {
+	        my %roleshash =
+		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
+					 $args->{'ccdomain'},
+                                         'userroles',['active'],['cc'],
+					 [$args->{'clonedomain'}]);
+	        if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':cc'}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
+		    $can_clone = 1;
+	        } else {
+                    $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
+	        }
 	    }
-	}
+        }
     }
-
     return ($can_clone, $clonemsg, $cloneid, $clonehome);
 }
 
@@ -7029,9 +7514,11 @@ sub construct_course {
 #
     my ($can_clone, $clonemsg, $cloneid, $clonehome);
     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
-	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args);
+	($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
 	if ($context ne 'auto') {
-	    $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
+            if ($clonemsg ne '') {
+	        $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
+            }
 	}
 	$outcome .= $clonemsg.$linefeed;