--- loncom/interface/loncommon.pm	2008/01/21 00:35:58	1.636
+++ loncom/interface/loncommon.pm	2013/04/11 15:30:37	1.1121
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.636 2008/01/21 00:35:58 raeburn Exp $
+# $Id: loncommon.pm,v 1.1121 2013/04/11 15:30:37 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -61,12 +61,20 @@ use POSIX qw(strftime mktime);
 use Apache::lonmenu();
 use Apache::lonenc();
 use Apache::lonlocal;
+use Apache::lonnet();
 use HTML::Entities;
 use Apache::lonhtmlcommon();
 use Apache::loncoursedata();
 use Apache::lontexconvert();
 use Apache::lonclonecourse();
+use Apache::lonuserutils();
+use Apache::lonuserstate();
 use LONCAPA qw(:DEFAULT :match);
+use DateTime::TimeZone;
+use DateTime::Locale::Catalog;
+use Text::Aspell;
+use Authen::Captcha;
+use Captcha::reCAPTCHA;
 
 # ---------------------------------------------- Designs
 use vars qw(%defaultdesign);
@@ -78,9 +86,82 @@ 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;
+my %supported_codes;
+my %latex_language;		# For choosing hyphenation in <transl..>
+my %latex_language_bykey;	# for choosing hyphenation from metadata
 my %cprtag;
 my %scprtag;
 my %fe; my %fd; my %fm;
@@ -113,11 +194,16 @@ BEGIN {
             while (my $line = <$fh>) {
                 next if ($line=~/^\#/);
                 chomp($line);
-                my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$line));
+                my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
                 $language{$key}=$val.' - '.$enc;
                 if ($sup) {
                     $supported_language{$key}=$sup;
+		    $supported_codes{$key}   = $code;
                 }
+		if ($latex) {
+		    $latex_language_bykey{$key} = $latex;
+		    $latex_language{$code} = $latex;
+		}
             }
             close($fh);
         }
@@ -198,7 +284,7 @@ BEGIN {
         }
     }
     &Apache::lonnet::logthis(
-              "<font color=yellow>INFO: Read file types</font>");
+             "<span style='color:yellow;'>INFO: Read file types</span>");
     $readit=1;
     }  # end of unless($readit) 
     
@@ -214,14 +300,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 +320,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 +405,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;
 }
 
@@ -333,9 +419,10 @@ sub studentbrowser_javascript {
          || ($env{'request.role'}=~/^(au|dc|su)/)
           ) { return ''; }  
    return (<<'ENDSTDBRW');
-<script type="text/javascript" language="Javascript" >
+<script type="text/javascript" language="Javascript">
+// <![CDATA[
     var stdeditbrowser;
-    function openstdbrowser(formname,uname,udom,roleflag,ignorefilter) {
+    function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadvonly) {
         var url = '/adm/pickstudent?';
         var filter;
 	if (!ignorefilter) {
@@ -347,58 +434,120 @@ sub studentbrowser_javascript {
 	   }
         }
         url += 'form=' + formname + '&unameelement='+uname+
-                                    '&udomelement='+udom;
+                                    '&udomelement='+udom+
+                                    '&clicker='+clicker;
 	if (roleflag) { url+="&roles=1"; }
+        if (courseadvonly) { url+="&courseadvonly=1"; }
         var title = 'Student_Browser';
         var options = 'scrollbars=1,resizable=1,menubar=0';
         options += ',width=700,height=600';
         stdeditbrowser = open(url,title,options,'1');
         stdeditbrowser.focus();
     }
+// ]]>
 </script>
 ENDSTDBRW
 }
 
+sub resourcebrowser_javascript {
+   unless ($env{'request.course.id'}) { return ''; }
+   return (<<'ENDRESBRW');
+<script type="text/javascript" language="Javascript">
+// <![CDATA[
+    var reseditbrowser;
+    function openresbrowser(formname,reslink) {
+        var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
+        var title = 'Resource_Browser';
+        var options = 'scrollbars=1,resizable=1,menubar=0';
+        options += ',width=700,height=500';
+        reseditbrowser = open(url,title,options,'1');
+        reseditbrowser.focus();
+    }
+// ]]>
+</script>
+ENDRESBRW
+}
+
 sub selectstudent_link {
-   my ($form,$unameele,$udomele)=@_;
+   my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
+   my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
+                      &Apache::lonhtmlcommon::entity_encode($unameele)."','".
+                      &Apache::lonhtmlcommon::entity_encode($udomele)."'";
    if ($env{'request.course.id'}) {  
        if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
 	   && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
 					'/'.$env{'request.course.sec'})) {
 	   return '';
        }
-       return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
-        '","'.$udomele.'");'."'>".&mt('Select User')."</a>";
+       $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
+       if ($courseadvonly)  {
+           $callargs .= ",'',1,1";
+       }
+       return '<span class="LC_nobreak">'.
+              '<a href="javascript:openstdbrowser('.$callargs.');">'.
+              &mt('Select User').'</a></span>';
    }
    if ($env{'request.role'}=~/^(au|dc|su)/) {
-       return "<a href='".'javascript:openstdbrowser("'.$form.'","'.$unameele.
-        '","'.$udomele.'",1);'."'>".&mt('Select User')."</a>";
+       $callargs .= ",'',1"; 
+       return '<span class="LC_nobreak">'.
+              '<a href="javascript:openstdbrowser('.$callargs.');">'.
+              &mt('Select User').'</a></span>';
    }
    return '';
 }
 
+sub selectresource_link {
+   my ($form,$reslink,$arg)=@_;
+   
+   my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
+                      &Apache::lonhtmlcommon::entity_encode($reslink)."'";
+   unless ($env{'request.course.id'}) { return $arg; }
+   return '<span class="LC_nobreak">'.
+              '<a href="javascript:openresbrowser('.$callargs.');">'.
+              $arg.'</a></span>';
+}
+
+
+
+sub authorbrowser_javascript {
+    return <<"ENDAUTHORBRW";
+<script type="text/javascript" language="JavaScript">
+// <![CDATA[
+var stdeditbrowser;
+
+function openauthorbrowser(formname,udom) {
+    var url = '/adm/pickauthor?';
+    url += 'form='+formname+'&roledom='+udom;
+    var title = 'Author_Browser';
+    var options = 'scrollbars=1,resizable=1,menubar=0';
+    options += ',width=700,height=600';
+    stdeditbrowser = open(url,title,options,'1');
+    stdeditbrowser.focus();
+}
+
+// ]]>
+</script>
+ENDAUTHORBRW
+}
+
 sub coursebrowser_javascript {
-    my ($domainfilter,$sec_element,$formname)=@_;
-    my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Group - for which you wish to add/modify a user role');
-   my $output = '
-<script type="text/javascript">
+    my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
+        $credits_element) = @_;
+    my $wintitle = 'Course_Browser';
+    if ($crstype eq 'Community') {
+        $wintitle = 'Community_Browser';
+    }
+    my $id_functions = &javascript_index_functions();
+    my $output = '
+<script type="text/javascript" language="JavaScript">
+// <![CDATA[
     var stdeditbrowser;'."\n";
-   $output .= <<"ENDSTDBRW";
-    function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,crstype) {
+
+    $output .= <<"ENDSTDBRW";
+    function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
         var url = '/adm/pickcourse?';
-        var domainfilter = '';
         var formid = getFormIdByName(formname);
-        if (formid > -1) {
-            var domid = getIndexByName(formid,udom);
-            if (domid > -1) {
-                if (document.forms[formid].elements[domid].type == 'select-one') {
-                    domainfilter=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
-                }
-                if (document.forms[formid].elements[domid].type == 'hidden') {
-                    domainfilter=document.forms[formid].elements[domid].value;
-                }
-            }
-        }
+        var domainfilter = getDomainFromSelectbox(formname,udom);
         if (domainfilter != null) {
            if (domainfilter != '') {
                url += 'domainfilter='+domainfilter+'&';
@@ -417,59 +566,216 @@ sub coursebrowser_javascript {
             else {
                 if (formname == 'portform') {
                     url += '&setroles='+extra_element;
+                } else {
+                    if (formname == 'rules') {
+                        url += '&fixeddom='+extra_element; 
+                    }
                 }
             }     
         }
-        if (multflag !=null && multflag != '') {
-            url += '&multiple='+multflag;
+        if (type != null && type != '') {
+            url += '&type='+type;
         }
-        if (crstype == 'Course/Group') {
-            if (formname == 'cu') {
-                crstype = document.cu.crstype.options[document.cu.crstype.selectedIndex].value; 
-                if (crstype == "") {
-                    alert("$crs_or_grp_alert");
-                    return;
-                }
-            }
+        if (type_elem != null && type_elem != '') {
+            url += '&typeelement='+type_elem;
         }
-        if (crstype !=null && crstype != '') {
-            url += '&type='+crstype;
+        if (formname == 'ccrs') {
+            var ownername = document.forms[formid].ccuname.value;
+            var ownerdom =  document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
+            url += '&cloner='+ownername+':'+ownerdom;
+        }
+        if (multflag !=null && multflag != '') {
+            url += '&multiple='+multflag;
         }
-        var title = 'Course_Browser';
+        var title = '$wintitle';
         var options = 'scrollbars=1,resizable=1,menubar=0';
         options += ',width=700,height=600';
         stdeditbrowser = open(url,title,options,'1');
         stdeditbrowser.focus();
     }
+$id_functions
+ENDSTDBRW
+    if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
+        $output .= &setsec_javascript($sec_element,$formname,$role_element,
+                                      $credits_element);
+    }
+    $output .= '
+// ]]>
+</script>';
+    return $output;
+}
+
+sub javascript_index_functions {
+    return <<"ENDJS";
+
+function getFormIdByName(formname) {
+    for (var i=0;i<document.forms.length;i++) {
+        if (document.forms[i].name == formname) {
+            return i;
+        }
+    }
+    return -1;
+}
 
-    function getFormIdByName(formname) {
-        for (var i=0;i<document.forms.length;i++) {
-            if (document.forms[i].name == formname) {
-                return i;
+function getIndexByName(formid,item) {
+    for (var i=0;i<document.forms[formid].elements.length;i++) {
+        if (document.forms[formid].elements[i].name == item) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+function getDomainFromSelectbox(formname,udom) {
+    var userdom;
+    var formid = getFormIdByName(formname);
+    if (formid > -1) {
+        var domid = getIndexByName(formid,udom);
+        if (domid > -1) {
+            if (document.forms[formid].elements[domid].type == 'select-one') {
+                userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
+            }
+            if (document.forms[formid].elements[domid].type == 'hidden') {
+                userdom=document.forms[formid].elements[domid].value;
             }
         }
-        return -1; 
     }
+    return userdom;
+}
+
+ENDJS
 
-    function getIndexByName(formid,item) {
-        for (var i=0;i<document.forms[formid].elements.length;i++) {
-            if (document.forms[formid].elements[i].name == item) {
-                return i;
+}
+
+sub javascript_array_indexof {
+    return <<ENDJS;
+<script type="text/javascript" language="JavaScript">
+// <![CDATA[
+
+if (!Array.prototype.indexOf) {
+    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
+        "use strict";
+        if (this === void 0 || this === null) {
+            throw new TypeError();
+        }
+        var t = Object(this);
+        var len = t.length >>> 0;
+        if (len === 0) {
+            return -1;
+        }
+        var n = 0;
+        if (arguments.length > 0) {
+            n = Number(arguments[1]);
+            if (n !== n) { // shortcut for verifying if it is NaN
+                n = 0;
+            } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
+                n = (n > 0 || -1) * Math.floor(Math.abs(n));
+            }
+        }
+        if (n >= len) {
+            return -1;
+        }
+        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
+        for (; k < len; k++) {
+            if (k in t && t[k] === searchElement) {
+                return k;
             }
         }
         return -1;
     }
-ENDSTDBRW
-    if ($sec_element ne '') {
-        $output .= &setsec_javascript($sec_element,$formname);
+}
+
+// ]]>
+</script>
+
+ENDJS
+
+}
+
+sub userbrowser_javascript {
+    my $id_functions = &javascript_index_functions();
+    return <<"ENDUSERBRW";
+
+function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
+    var url = '/adm/pickuser?';
+    var userdom = getDomainFromSelectbox(formname,udom);
+    if (userdom != null) {
+       if (userdom != '') {
+           url += 'srchdom='+userdom+'&';
+       }
     }
-    $output .= '
-</script>';
-    return $output;
+    url += 'form=' + formname + '&unameelement='+uname+
+                                '&udomelement='+udom+
+                                '&ulastelement='+ulast+
+                                '&ufirstelement='+ufirst+
+                                '&uemailelement='+uemail+
+                                '&hideudomelement='+hideudom+
+                                '&coursedom='+crsdom;
+    if ((caller != null) && (caller != undefined)) {
+        url += '&caller='+caller;
+    }
+    var title = 'User_Browser';
+    var options = 'scrollbars=1,resizable=1,menubar=0';
+    options += ',width=700,height=600';
+    var stdeditbrowser = open(url,title,options,'1');
+    stdeditbrowser.focus();
+}
+
+function fix_domain (formname,udom,origdom,uname) {
+    var formid = getFormIdByName(formname);
+    if (formid > -1) {
+        var unameid = getIndexByName(formid,uname);
+        var domid = getIndexByName(formid,udom);
+        var hidedomid = getIndexByName(formid,origdom);
+        if (hidedomid > -1) {
+            var fixeddom = document.forms[formid].elements[hidedomid].value;
+            var unameval = document.forms[formid].elements[unameid].value;
+            if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
+                if (domid > -1) {
+                    var slct = document.forms[formid].elements[domid];
+                    if (slct.type == 'select-one') {
+                        var i;
+                        for (i=0;i<slct.length;i++) {
+                            if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
+                        }
+                    }
+                    if (slct.type == 'hidden') {
+                        slct.value = fixeddom;
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
+$id_functions
+ENDUSERBRW
 }
 
 sub setsec_javascript {
-    my ($sec_element,$formname) = @_;
+    my ($sec_element,$formname,$role_element,$credits_element) = @_;
+    my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
+        $communityrolestr);
+    if ($role_element ne '') {
+        my @allroles = ('st','ta','ep','in','ad');
+        foreach my $crstype ('Course','Community') {
+            if ($crstype eq 'Community') {
+                foreach my $role (@allroles) {
+                    push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
+                }
+                push(@communityrolenames,&Apache::lonnet::plaintext('co'));
+            } else {
+                foreach my $role (@allroles) {
+                    push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
+                }
+                push(@courserolenames,&Apache::lonnet::plaintext('cc'));
+            }
+        }
+        $rolestr = '"'.join('","',@allroles).'"';
+        $courserolestr = '"'.join('","',@courserolenames).'"';
+        $communityrolestr = '"'.join('","',@communityrolenames).'"';
+    }
     my $setsections = qq|
 function setSect(sectionlist) {
     var sectionsArray = new Array();
@@ -503,15 +809,92 @@ function setSect(sectionlist) {
         }
     }
 }
+
+function setRole(crstype) {
+|;
+    if ($role_element eq '') {
+        $setsections .= '    return;
+}
+';
+    } else {
+        $setsections .= qq|
+    var elementLength = document.$formname.$role_element.length;
+    var allroles = Array($rolestr);
+    var courserolenames = Array($courserolestr);
+    var communityrolenames = Array($communityrolestr);
+    if (elementLength != undefined) {
+        if (document.$formname.$role_element.options[5].value == 'cc') {
+            if (crstype == 'Course') {
+                return;
+            } else {
+                allroles[5] = 'co';
+                for (var i=0; i<6; i++) {
+                    document.$formname.$role_element.options[i].value = allroles[i];
+                    document.$formname.$role_element.options[i].text = communityrolenames[i];
+                }
+            }
+        } else {
+            if (crstype == 'Community') {
+                return;
+            } else {
+                allroles[5] = 'cc';
+                for (var i=0; i<6; i++) {
+                    document.$formname.$role_element.options[i].value = allroles[i];
+                    document.$formname.$role_element.options[i].text = courserolenames[i];
+                }
+            }
+        }
+    }
+    return;
+}
+|;
+    }
+    if ($credits_element) {
+        $setsections .= qq|
+function setCredits(defaultcredits) {
+    document.$formname.$credits_element.value = defaultcredits;
+    return;
+}
 |;
+    }
     return $setsections;
 }
 
-
 sub selectcourse_link {
-   my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype)=@_;
-   return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele.
-        '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course')."</a>";
+   my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
+       $typeelement) = @_;
+   my $type = $selecttype;
+   my $linktext = &mt('Select Course');
+   if ($selecttype eq 'Community') {
+       $linktext = &mt('Select Community');
+   } elsif ($selecttype eq 'Course/Community') {
+       $linktext = &mt('Select Course/Community');
+       $type = '';
+   } elsif ($selecttype eq 'Select') {
+       $linktext = &mt('Select');
+       $type = '';
+   }
+   return '<span class="LC_nobreak">'
+         ."<a href='"
+         .'javascript:opencrsbrowser("'.$form.'","'.$unameele
+         .'","'.$udomele.'","'.$desc.'","'.$extra_element
+         .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
+         ."'>".$linktext.'</a>'
+         .'</span>';
+}
+
+sub selectauthor_link {
+   my ($form,$udom)=@_;
+   return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
+          &mt('Select Author').'</a>';
+}
+
+sub selectuser_link {
+    my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
+        $coursedom,$linktext,$caller) = @_;
+    return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
+           "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
+           ');">'.$linktext.'</a>';
 }
 
 sub check_uncheck_jscript {
@@ -519,10 +902,14 @@ sub check_uncheck_jscript {
 function checkAll(field) {
     if (field.length > 0) {
         for (i = 0; i < field.length; i++) {
-            field[i].checked = true ;
+            if (!field[i].disabled) { 
+                field[i].checked = true;
+            }
         }
     } else {
-        field.checked = true
+        if (!field.disabled) { 
+            field.checked = true;
+        }
     }
 }
  
@@ -539,10 +926,125 @@ ENDSCRT
     return $jscript;
 }
 
+sub select_timezone {
+   my ($name,$selected,$onchange,$includeempty)=@_;
+   my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
+   if ($includeempty) {
+       $output .= '<option value=""';
+       if (($selected eq '') || ($selected eq 'local')) {
+           $output .= ' selected="selected" ';
+       }
+       $output .= '> </option>';
+   }
+   my @timezones = DateTime::TimeZone->all_names;
+   foreach my $tzone (@timezones) {
+       $output.= '<option value="'.$tzone.'"';
+       if ($tzone eq $selected) {
+           $output.=' selected="selected"';
+       }
+       $output.=">$tzone</option>\n";
+   }
+   $output.="</select>";
+   return $output;
+}
+
+sub select_datelocale {
+    my ($name,$selected,$onchange,$includeempty)=@_;
+    my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
+    if ($includeempty) {
+        $output .= '<option value=""';
+        if ($selected eq '') {
+            $output .= ' selected="selected" ';
+        }
+        $output .= '> </option>';
+    }
+    my (@possibles,%locale_names);
+    my @locales = DateTime::Locale::Catalog::Locales;
+    foreach my $locale (@locales) {
+        if (ref($locale) eq 'HASH') {
+            my $id = $locale->{'id'};
+            if ($id ne '') {
+                my $en_terr = $locale->{'en_territory'};
+                my $native_terr = $locale->{'native_territory'};
+                my @languages = &Apache::lonlocal::preferred_languages();
+                if (grep(/^en$/,@languages) || !@languages) {
+                    if ($en_terr ne '') {
+                        $locale_names{$id} = '('.$en_terr.')';
+                    } elsif ($native_terr ne '') {
+                        $locale_names{$id} = $native_terr;
+                    }
+                } else {
+                    if ($native_terr ne '') {
+                        $locale_names{$id} = $native_terr.' ';
+                    } elsif ($en_terr ne '') {
+                        $locale_names{$id} = '('.$en_terr.')';
+                    }
+                }
+                push (@possibles,$id);
+            }
+        }
+    }
+    foreach my $item (sort(@possibles)) {
+        $output.= '<option value="'.$item.'"';
+        if ($item eq $selected) {
+            $output.=' selected="selected"';
+        }
+        $output.=">$item";
+        if ($locale_names{$item} ne '') {
+            $output.="  $locale_names{$item}</option>\n";
+        }
+        $output.="</option>\n";
+    }
+    $output.="</select>";
+    return $output;
+}
+
+sub select_language {
+    my ($name,$selected,$includeempty) = @_;
+    my %langchoices;
+    if ($includeempty) {
+        %langchoices = ('' => 'No language preference');
+    }
+    foreach my $id (&languageids()) {
+        my $code = &supportedlanguagecode($id);
+        if ($code) {
+            $langchoices{$code} = &plainlanguagedescription($id);
+        }
+    }
+    %langchoices = &Apache::lonlocal::texthash(%langchoices);
+    return &select_form($selected,$name,\%langchoices);
+}
+
+=pod
+
+
+=item * &list_languages()
+
+Returns an array reference that is suitable for use in language prompters.
+Each array element is itself a two element array.  The first element
+is the language code.  The second element a descsriptiuon of the 
+language itself.  This is suitable for use in e.g.
+&Apache::edit::select_arg (once dereferenced that is).
+
+=cut 
+
+sub list_languages {
+    my @lang_choices;
+
+    foreach my $id (&languageids()) {
+	my $code = &supportedlanguagecode($id);
+	if ($code) {
+	    my $selector    = $supported_codes{$id};
+	    my $description = &plainlanguagedescription($id);
+	    push (@lang_choices, [$selector, $description]);
+	}
+    }
+    return \@lang_choices;
+}
 
 =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
@@ -568,6 +1070,12 @@ linked_select_forms takes the following
 
 =item * $menuorder, the order of values in the first menu
 
+=item * $onchangefirst, additional javascript call to execute for an onchange
+        event for the first <select> tag
+
+=item * $onchangesecond, additional javascript call to execute for an onchange
+        event for the second <select> tag
+
 =back 
 
 Below is an example of such a hash.  Only the 'text', 'default', and 
@@ -621,12 +1129,15 @@ sub linked_select_forms {
         $secondselectname, 
         $hashref,
         $menuorder,
+        $onchangefirst,
+        $onchangesecond
         ) = @_;
     my $second = "document.$formname.$secondselectname";
     my $first = "document.$formname.$firstselectname";
     # output the javascript to do the changing
     my $result = '';
-    $result.="<script type=\"text/javascript\">\n";
+    $result.='<script type="text/javascript" language="JavaScript">'."\n";
+    $result.="// <![CDATA[\n";
     $result.="var select2data = new Object();\n";
     $" = '","';
     my $debug = '';
@@ -672,10 +1183,11 @@ function select1_changed() {
         }
     }
 }
+// ]]>
 </script>
 END
     # output the initial values for the selection lists
-    $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed()\">\n";
+    $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed();$onchangefirst\">\n";
     my @order = sort(keys(%{$hashref}));
     if (ref($menuorder) eq 'ARRAY') {
         @order = @{$menuorder};
@@ -688,7 +1200,11 @@ END
     $result .= "</select>\n";
     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
     $result .= $middletext;
-    $result .= "<select size=\"1\" name=\"$secondselectname\">\n";
+    $result .= "<select size=\"1\" name=\"$secondselectname\"";
+    if ($onchangesecond) {
+        $result .= ' onchange="'.$onchangesecond.'"';
+    }
+    $result .= ">\n";
     my $seconddefault = $hashref->{$firstdefault}->{'default'};
     
     my @secondorder = sort(keys(%select2));
@@ -707,7 +1223,7 @@ END
 
 =pod
 
-=item * help_open_topic($topic, $text, $stayOnPage, $width, $height)
+=item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)
 
 Returns a string corresponding to an HTML link to the given help
 $topic, where $topic corresponds to the name of a .tex file in
@@ -725,18 +1241,19 @@ a new window using Javascript. (Default
 
 $width and $height are optional numerical parameters that will
 override the width and height of the popped up window, which may
-be useful for certain help topics with big pictures included. 
+be useful for certain help topics with big pictures included.
+
+$imgid is the id of the img tag used for the help icon. This may be
+used in a javascript call to switch the image src.  See 
+lonhtmlcommon::htmlareaselectactive() for an example.
 
 =cut
 
 sub help_open_topic {
-    my ($topic, $text, $stayOnPage, $width, $height) = @_;
+    my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
     $text = "" if (not defined $text);
     $stayOnPage = 0 if (not defined $stayOnPage);
-    if ($env{'browser.interface'} eq 'textual') {
-	$stayOnPage=1;
-    }
-    $width = 350 if (not defined $width);
+    $width = 500 if (not defined $width);
     $height = 400 if (not defined $height);
     my $filename = $topic;
     $filename =~ s/ /_/g;
@@ -747,25 +1264,34 @@ sub help_open_topic {
     $topic=~s/\W/\_/g;
 
     if (!$stayOnPage) {
-	$link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
+	$link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
+    } elsif ($stayOnPage eq 'popup') {
+        $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
     } else {
 	$link = "/adm/help/${filename}.hlp";
     }
 
     # Add the text
-    if ($text ne "") {
-	$template .= 
-            "<table bgcolor='#3333AA' cellspacing='1' cellpadding='1' border='0'><tr>".
-            "<td bgcolor='#5555FF'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
+    if ($text ne "") {	
+	$template.='<span class="LC_help_open_topic">'
+                  .'<a target="_top" href="'.$link.'">'
+                  .$text.'</a>';
     }
 
-    # Add the graphic
+    # (Always) Add the graphic
     my $title = &mt('Online Help');
-    my $helpicon=&lonhttpdurl("/adm/help/gif/smallHelp.gif");
-    $template .= <<"ENDTEMPLATE";
- <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a>
-ENDTEMPLATE
-    if ($text ne '') { $template.='</td></tr></table>' };
+    my $helpicon=&lonhttpdurl("/adm/help/help.png");
+    if ($imgid ne '') {
+        $imgid = ' id="'.$imgid.'"';
+    }
+    $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'
+              .'<img src="'.$helpicon.'" border="0"'
+              .' alt="'.&mt('Help: [_1]',$topic).'"'
+              .' title="'.$title.'" style="vertical-align:middle;"'.$imgid 
+              .' /></a>';
+    if ($text ne "") {	
+        $template.='</span>';
+    }
     return $template;
 
 }
@@ -773,29 +1299,36 @@ ENDTEMPLATE
 # This is a quicky function for Latex cheatsheet editing, since it 
 # appears in at least four places
 sub helpLatexCheatsheet {
-    my $other = shift;
+    my ($topic,$text,$not_author,$stayOnPage) = @_;
+    my $out;
     my $addOther = '';
-    if ($other) {
-	$addOther = Apache::loncommon::help_open_topic($other, shift,
-						       undef, undef, 600) .
-							   '</td><td>';
-    }
-    return '<table><tr><td>'.
-	$addOther .
-	&Apache::loncommon::help_open_topic("Greek_Symbols",&mt('Greek Symbols'),
-					    undef,undef,600)
-	.'</td><td>'.
-	&Apache::loncommon::help_open_topic("Other_Symbols",&mt('Other Symbols'),
-					    undef,undef,600)
-	.'</td></tr></table>';
+    if ($topic) {
+	$addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
+    }
+    $out = '<span>' # Start cheatsheet
+	  .$addOther
+          .'<span>'
+	  .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
+	  .'</span> <span>'
+	  .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
+	  .'</span>';
+    unless ($not_author) {
+        $out .= ' <span>'
+	       .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
+	       .'</span>';
+    }
+    $out .= '</span>'; # End cheatsheet
+    return $out;
 }
 
 sub general_help {
     my $helptopic='Student_Intro';
     if ($env{'request.role'}=~/^(ca|au)/) {
 	$helptopic='Authoring_Intro';
-    } elsif ($env{'request.role'}=~/^cc/) {
+    } elsif ($env{'request.role'}=~/^(cc|co)/) {
 	$helptopic='Course_Coordination_Intro';
+    } elsif ($env{'request.role'}=~/^dc/) {
+        $helptopic='Domain_Coordination_Intro';
     }
     return $helptopic;
 }
@@ -812,7 +1345,9 @@ sub update_help_link {
     my $banner_link = "/adm/helpmenu?page=banner&amp;topic=$topic&amp;component_help=$component_help&amp;faq=$faq&amp;bug=$bug&amp;origurl=$origurl&amp;stamp=$timestamp&amp;stayonpage=$stayOnPage";
     my $output .= <<"ENDOUTPUT";
 <script type="text/javascript">
+// <![CDATA[
 banner_link = '$banner_link';
+// ]]>
 </script>
 ENDOUTPUT
     return $output;
@@ -822,13 +1357,7 @@ ENDOUTPUT
 sub help_open_menu {
     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text) 
 	= @_;    
-    $stayOnPage = 0 if (not defined $stayOnPage);
-    # only use pop-up help (stayOnPage == 0)
-    # if environment.remote is on (using remote control UI)
-    if ($env{'browser.interface'} eq 'textual' ||
-    	$env{'environment.remote'} eq 'off' ) {
-        $stayOnPage=1;
-    }
+    $stayOnPage = 1;
     my $output;
     if ($component_help) {
 	if (!$text) {
@@ -849,9 +1378,8 @@ sub help_open_menu {
 sub top_nav_help {
     my ($text) = @_;
     $text = &mt($text);
-    my $stay_on_page = 
-	($env{'browser.interface'}  eq 'textual' ||
-	 $env{'environment.remote'} eq 'off' );
+    my $stay_on_page = 1;
+
     my $link = ($stay_on_page) ? "javascript:helpMenu('display')"
 	                     : "javascript:helpMenu('open')";
     my $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
@@ -866,11 +1394,7 @@ END
 
 sub help_menu_js {
     my ($text) = @_;
-
-    my $stayOnPage = 
-	($env{'browser.interface'}  eq 'textual' ||
-	 $env{'environment.remote'} eq 'off' );
-
+    my $stayOnPage = 1;
     my $width = 620;
     my $height = 600;
     my $helptopic=&general_help();
@@ -889,8 +1413,8 @@ sub help_menu_js {
 
     my $template .= <<"ENDTEMPLATE";
 <script type="text/javascript">
-// <!-- BEGIN LON-CAPA Internal
 // <![CDATA[
+// <!-- BEGIN LON-CAPA Internal
 var banner_link = '';
 function helpMenu(target) {
     var caller = this;
@@ -911,12 +1435,12 @@ function helpMenu(target) {
     return;
 }
 function writeHelp(caller) {
-    caller.document.writeln('$start_page<frame name="bannerframe"  src="'+banner_link+'" /><frame name="bodyframe" src="$details_link" /> $end_page')
+    caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" />\\n<frame name="bodyframe" src="$details_link" />\\n$end_page')
     caller.document.close()
     caller.focus()
 }
-// ]]>
 // END LON-CAPA Internal -->
+// ]]>
 </script>
 ENDTEMPLATE
     return $template;
@@ -927,11 +1451,7 @@ sub help_open_bug {
     unless ($env{'user.adv'}) { return ''; }
     unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
     $text = "" if (not defined $text);
-    $stayOnPage = 0 if (not defined $stayOnPage);
-    if ($env{'browser.interface'} eq 'textual' ||
-	$env{'environment.remote'} eq 'off' ) {
 	$stayOnPage=1;
-    }
     $width = 600 if (not defined $width);
     $height = 600 if (not defined $height);
 
@@ -953,7 +1473,7 @@ sub help_open_bug {
     {
 	$template .= 
   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
-  "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
+  "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
     }
 
     # Add the graphic
@@ -972,11 +1492,7 @@ sub help_open_faq {
     unless ($env{'user.adv'}) { return ''; }
     unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
     $text = "" if (not defined $text);
-    $stayOnPage = 0 if (not defined $stayOnPage);
-    if ($env{'browser.interface'} eq 'textual' ||
-	$env{'environment.remote'} eq 'off' ) {
 	$stayOnPage=1;
-    }
     $width = 350 if (not defined $width);
     $height = 400 if (not defined $height);
 
@@ -998,7 +1514,7 @@ sub help_open_faq {
     {
 	$template .= 
   "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
-  "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>";
+  "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
     }
 
     # Add the graphic
@@ -1017,7 +1533,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
@@ -1072,7 +1588,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
@@ -1096,7 +1612,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.
 
@@ -1143,7 +1659,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. 
 
@@ -1167,7 +1683,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
 
@@ -1176,6 +1692,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
 
@@ -1183,6 +1700,7 @@ sub resize_textarea_js {
     my $geometry = &viewport_geometry_js();
     return <<"RESIZE";
     <script type="text/javascript">
+// <![CDATA[
 $geometry
 
 function getX(element) {
@@ -1221,6 +1739,7 @@ function resize_textarea(textarea_id,bot
     }
     textarea.style.height=new_height+'px';
 }
+// ]]>
 </script>
 RESIZE
 
@@ -1228,8 +1747,6 @@ RESIZE
 
 =pod
 
-=back
- 
 =head1 Excel and CSV file utility routines
 
 =over 4
@@ -1241,7 +1758,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.
@@ -1262,7 +1779,7 @@ sub csv_translate {
 
 =pod
 
-=item * define_excel_formats
+=item * &define_excel_formats()
 
 Define some commonly used Excel cell formats.
 
@@ -1292,6 +1809,7 @@ Inputs: $workbook
 
 Returns: $format, a hash reference.
 
+
 =cut
 
 ###############################################################
@@ -1318,7 +1836,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.
@@ -1343,14 +1861,17 @@ sub create_workbook {
     my $workbook  = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
     if (! defined($workbook)) {
         $r->log_error("Error creating excel spreadsheet $filename: $!");
-        $r->print('<p>'.&mt("Unable to create new Excel file.  ".
-                            "This error has been logged.  ".
-                            "Please alert your LON-CAPA administrator").
-                  '</p>');
+        $r->print(
+            '<p class="LC_error">'
+           .&mt('Problems occurred in creating the new Excel file.')
+           .' '.&mt('This error has been logged.')
+           .' '.&mt('Please alert your LON-CAPA administrator.')
+           .'</p>'
+        );
         return (undef);
     }
     #
-    $workbook->set_tempdir('/home/httpd/perl/tmp');
+    $workbook->set_tempdir(LONCAPA::tempdir());
     #
     my $format = &Apache::loncommon::define_excel_formats($workbook);
     return ($workbook,$filename,$format);
@@ -1361,7 +1882,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 
@@ -1386,9 +1907,13 @@ sub create_text_file {
     $fh = Apache::File->new('>/home/httpd'.$filename);
     if (! defined($fh)) {
         $r->log_error("Couldn't open $filename for output $!");
-        $r->print("Problems occured in creating the output file.  ".
-                  "This error has been logged.  ".
-                  "Please alert your LON-CAPA administrator.");
+        $r->print(
+            '<p class="LC_error">'
+           .&mt('Problems occurred in creating the output file.')
+           .' '.&mt('This error has been logged.')
+           .' '.&mt('Please alert your LON-CAPA administrator.')
+           .'</p>'
+        );
     }
     return ($fh,$filename)
 }
@@ -1417,7 +1942,7 @@ sub domain_select {
 	return &multiple_select_form($name,$value,4,\%domains);
     } else {
 	$domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
-	return &select_form($name,$value,%domains);
+	return &select_form($name,$value,\%domains);
     }
 }
 
@@ -1429,7 +1954,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
 
@@ -1455,7 +1980,7 @@ sub multiple_select_form {
             $size = scalar(keys(%$hash));
         }
     }
-    $output.="\n<select name='$name' size='$size' multiple='1'>";
+    $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
     my @order;
     if (ref($order) eq 'ARRAY')  {
         @order = @{$order};
@@ -1479,29 +2004,36 @@ sub multiple_select_form {
 
 =pod
 
-=item * select_form($defdom,$name,%hash)
+=item * &select_form($defdom,$name,$hashref,$onchange)
 
 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.  
+allow a user to select options from a ref to a hash containing:
+option_name => displayed text. An optional $onchange can include
+a javascript onchange item, e.g., onchange="this.form.submit();"  
+
 See lonrights.pm for an example invocation and use.
 
 =cut
 
 #-------------------------------------------
 sub select_form {
-    my ($def,$name,%hash) = @_;
-    my $selectform = "<select name=\"$name\" size=\"1\">\n";
+    my ($def,$name,$hashref,$onchange) = @_;
+    return unless (ref($hashref) eq 'HASH');
+    if ($onchange) {
+        $onchange = ' onchange="'.$onchange.'"';
+    }
+    my $selectform = "<select name=\"$name\" size=\"1\"$onchange>\n";
     my @keys;
-    if (exists($hash{'select_form_order'})) {
-	@keys=@{$hash{'select_form_order'}};
+    if (exists($hashref->{'select_form_order'})) {
+	@keys=@{$hashref->{'select_form_order'}};
     } else {
-	@keys=sort(keys(%hash));
+	@keys=sort(keys(%{$hashref}));
     }
     foreach my $key (@keys) {
         $selectform.=
 	    '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
             ($key eq $def ? 'selected="selected" ' : '').
-                ">".&mt($hash{$key})."</option>\n";
+                ">".$hashref->{$key}."</option>\n";
     }
     $selectform.="</select>";
     return $selectform;
@@ -1510,19 +2042,112 @@ sub select_form {
 # For display filters
 
 sub display_filter {
+    my ($context) = @_;
     if (!$env{'form.show'}) { $env{'form.show'}=10; }
     if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
-    return '<nobr><label>'.&mt('Records [_1]',
+    my $phraseinput = 'hidden';
+    my $includeinput = 'hidden';
+    my ($checked,$includetypestext);
+    if ($env{'form.displayfilter'} eq 'containing') {
+        $phraseinput = 'text'; 
+        if ($context eq 'parmslog') {
+            $includeinput = 'checkbox';
+            if ($env{'form.includetypes'}) {
+                $checked = ' checked="checked"';
+            }
+            $includetypestext = &mt('Include parameter types');
+        }
+    } else {
+        $includetypestext = '&nbsp;';
+    }
+    my ($additional,$secondid,$thirdid);
+    if ($context eq 'parmslog') {
+        $additional = 
+            '<label><input type="'.$includeinput.'" name="includetypes"'. 
+            $checked.' name="includetypes" value="1" id="includetypes" />'.
+            '&nbsp;<span id="includetypestext">'.$includetypestext.'</span>'.
+            '</label>';
+        $secondid = 'includetypes';
+        $thirdid = 'includetypestext';
+    }
+    my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
+                                                    '$secondid','$thirdid')";
+    return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
 			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
 							   (&mt('all'),10,20,50,100,1000,10000))).
-	   '</label></nobr> <nobr>'.
-           &mt('Filter [_1]',
+	   '</label></span> <span class="LC_nobreak">'.
+           &mt('Filter: [_1]',
 	   &select_form($env{'form.displayfilter'},
 			'displayfilter',
-			('currentfolder' => 'Current folder/page',
+			{'currentfolder' => 'Current folder/page',
 			 'containing' => 'Containing phrase',
-			 'none' => 'None'))).
-			 '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></nobr>';
+			 'none' => 'None'},$onchange)).'&nbsp;'.
+			 '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
+                         &HTML::Entities::encode($env{'form.containingphrase'}).
+                         '" />'.$additional;
+}
+
+sub display_filter_js {
+    my $includetext = &mt('Include parameter types');
+    return <<"ENDJS";
+  
+function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
+    var firstType = 'hidden';
+    if (setter.options[setter.selectedIndex].value == 'containing') {
+        firstType = 'text';
+    }
+    firstObject = document.getElementById(firstid);
+    if (typeof(firstObject) == 'object') {
+        if (firstObject.type != firstType) {
+            changeInputType(firstObject,firstType);
+        }
+    }
+    if (context == 'parmslog') {
+        var secondType = 'hidden';
+        if (firstType == 'text') {
+            secondType = 'checkbox';
+        }
+        secondObject = document.getElementById(secondid);  
+        if (typeof(secondObject) == 'object') {
+            if (secondObject.type != secondType) {
+                changeInputType(secondObject,secondType);
+            }
+        }
+        var textItem = document.getElementById(thirdid);
+        var currtext = textItem.innerHTML;
+        var newtext;
+        if (firstType == 'text') {
+            newtext = '$includetext';
+        } else {
+            newtext = '&nbsp;';
+        }
+        if (currtext != newtext) {
+            textItem.innerHTML = newtext;
+        }
+    }
+    return;
+}
+
+function changeInputType(oldObject,newType) {
+    var newObject = document.createElement('input');
+    newObject.type = newType;
+    if (oldObject.size) {
+        newObject.size = oldObject.size;
+    }
+    if (oldObject.value) {
+        newObject.value = oldObject.value;
+    }
+    if (oldObject.name) {
+        newObject.name = oldObject.name;
+    }
+    if (oldObject.id) {
+        newObject.id = oldObject.id;
+    }
+    oldObject.parentNode.replaceChild(newObject,oldObject);
+    return;
+}
+
+ENDJS
 }
 
 sub gradeleveldescription {
@@ -1566,7 +2191,7 @@ sub select_level_form {
 
 =pod
 
-=item * select_dom_form($defdom,$name,$includeempty,$showdomdesc)
+=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms)
 
 Returns a string containing a <select name='$name' size='1'> form to 
 allow a user to select the domain to preform an operation in.  
@@ -1575,17 +2200,35 @@ See loncreateuser.pm for an example invo
 If the $includeempty flag is set, it also includes an empty choice ("no domain
 selected");
 
-If the $showdomdesc flag is set, the domain name is followed by the domain description. 
+If the $showdomdesc flag is set, the domain name is followed by the domain description.
+
+The optional $onchange argument specifies what should occur if the domain selector is changed, e.g., 'this.form.submit()' if the form is to be automatically submitted.
+
+The optional $incdoms is a reference to an array of domains which will be the only available options.
+
+The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
 
 =cut
 
 #-------------------------------------------
 sub select_dom_form {
-    my ($defdom,$name,$includeempty,$showdomdesc) = @_;
-    my @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
+    my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms) = @_;
+    if ($onchange) {
+        $onchange = ' onchange="'.$onchange.'"';
+    }
+    my (@domains,%exclude);
+    if (ref($incdoms) eq 'ARRAY') {
+        @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
+    } else {
+        @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
+    }
     if ($includeempty) { @domains=('',@domains); }
-    my $selectdomain = "<select name=\"$name\" size=\"1\">\n";
+    if (ref($excdoms) eq 'ARRAY') {
+        map { $exclude{$_} = 1; } @{$excdoms}; 
+    }
+    my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange>\n";
     foreach my $dom (@domains) {
+        next if ($exclude{$dom});
         $selectdomain.="<option value=\"$dom\" ".
             ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
         if ($showdomdesc) {
@@ -1606,7 +2249,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
@@ -1640,7 +2283,7 @@ sub home_server_form_item {
     if ($numlib > 1) {
         $result .= '<select name="'.$name.'" />'."\n";
         if ($default) {
-            $result .= '<option value="default" selected>'.&mt('default').
+            $result .= '<option value="default" selected="selected">'.&mt('default').
                        '</option>'."\n";
         }
         foreach my $hostid (sort(keys(%servers))) {
@@ -1766,14 +2409,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
@@ -1790,7 +2431,7 @@ See loncreateuser.pm for invocation and
 
 =back
 
-=back 
+See loncreateuser.pm for invocation and use examples.
 
 =cut
 
@@ -1881,12 +2522,16 @@ function changed_text(choice,currentform
 }
 
 function set_auth_radio_buttons(newvalue,currentform) {
+    var numauthchoices = currentform.login.length;
+    if (typeof numauthchoices  == "undefined") {
+        return;
+    } 
     var i=0;
-    while (i < currentform.login.length) {
+    while (i < numauthchoices) {
         if (currentform.login[i].value == newvalue) { break; }
         i++;
     }
-    if (i == currentform.login.length) {
+    if (i == numauthchoices) {
         return;
     }
     current.radiovalue = newvalue;
@@ -1897,7 +2542,7 @@ END
     return $result;
 }
 
-sub authform_authorwarning{
+sub authform_authorwarning {
     my $result='';
     $result='<i>'.
         &mt('As a general rule, only authors or co-authors should be '.
@@ -1906,16 +2551,16 @@ sub authform_authorwarning{
     return $result;
 }
 
-sub authform_nochange{  
+sub authform_nochange {
     my %in = (
               formname => 'document.cu',
               kerb_def_dom => 'MSU.EDU',
               @_,
           );
-    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'}); 
+    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');  
+    if (!$authnum) {
+        $result = &mt('Under your 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" '.
@@ -1935,16 +2580,16 @@ sub authform_kerberos {
               );
     my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
         $autharg,$jscall);
-    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
+    my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
     if ($in{'kerb_def_auth'} eq 'krb5') {
-       $check5 = ' checked="on"';
+       $check5 = ' checked="checked"';
     } else {
-       $check4 = ' checked="on"';
+       $check4 = ' checked="checked"';
     }
     $krbarg = $in{'kerb_def_dom'};
     if (defined($in{'curr_authtype'})) {
         if ($in{'curr_authtype'} eq 'krb') {
-            $krbcheck = ' checked="on"';
+            $krbcheck = ' checked="checked"';
             if (defined($in{'mode'})) {
                 if ($in{'mode'} eq 'modifyuser') {
                     $krbcheck = '';
@@ -1952,10 +2597,10 @@ sub authform_kerberos {
             }
             if (defined($in{'curr_kerb_ver'})) {
                 if ($in{'curr_krb_ver'} eq '5') {
-                    $check5 = ' checked="on"';
+                    $check5 = ' checked="checked"';
                     $check4 = '';
                 } else {
-                    $check4 = ' checked="on"';
+                    $check4 = ' checked="checked"';
                     $check5 = '';
                 }
             }
@@ -1976,7 +2621,7 @@ sub authform_kerberos {
         }
     } else {
         if ($authnum == 1) {
-            $authtype = '<input type="hidden" name="login" value="krb">';
+            $authtype = '<input type="hidden" name="login" value="krb" />';
         }
     }
     if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
@@ -1985,7 +2630,7 @@ sub authform_kerberos {
         if (defined($in{'mode'})) {
             if ($in{'mode'} eq 'modifycourse') {
                 if ($authnum == 1) {
-                    $authtype = '<input type="hidden" name="login" value="krb">';
+                    $authtype = '<input type="radio" name="login" value="krb" />';
                 }
             }
         }
@@ -1997,9 +2642,9 @@ sub authform_kerberos {
                     $krbcheck.' />';
     }
     if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
-        ($can_assign{'krb4'} && !$can_assign{'krb5'} && 
+        ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
          $in{'curr_authtype'} eq 'krb5') ||
-        (!$can_assign{'krb4'} && $can_assign{'krb5'} && 
+        (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
          $in{'curr_authtype'} eq 'krb4')) {
         $result .= &mt
         ('[_1] Kerberos authenticated with domain [_2] '.
@@ -2035,18 +2680,18 @@ sub authform_kerberos {
     return $result;
 }
 
-sub authform_internal{  
+sub authform_internal {
     my %in = (
                 formname => 'document.cu',
                 kerb_def_dom => 'MSU.EDU',
                 @_,
                 );
     my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
-    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
+    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" ';
+                $intcheck = 'checked="checked" ';
                 if (defined($in{'mode'})) {
                     if ($in{'mode'} eq 'modifyuser') {
                         $intcheck = '';
@@ -2062,7 +2707,7 @@ sub authform_internal{
         }
     } else {
         if ($authnum == 1) {
-            $authtype = '<input type="hidden" name="login" value="int">';
+            $authtype = '<input type="hidden" name="login" value="int" />';
         }
     }
     if (!$can_assign{'int'}) {
@@ -2071,7 +2716,7 @@ sub authform_internal{
         if (defined($in{'mode'})) {
             if ($in{'mode'} eq 'modifycourse') {
                 if ($authnum == 1) {
-                    $authtype = '<input type="hidden" name="login" value="int">';
+                    $authtype = '<input type="radio" name="login" value="int" />';
                 }
             }
         }
@@ -2086,22 +2731,22 @@ 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>';
+    $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;
 }
 
-sub authform_local{  
+sub authform_local {
     my %in = (
               formname => 'document.cu',
               kerb_def_dom => 'MSU.EDU',
               @_,
               );
     my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
-    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
+    my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
     if (defined($in{'curr_authtype'})) {
         if ($in{'curr_authtype'} eq 'loc') {
             if ($can_assign{'loc'}) {
-                $loccheck = 'checked="on" ';
+                $loccheck = 'checked="checked" ';
                 if (defined($in{'mode'})) {
                     if ($in{'mode'} eq 'modifyuser') {
                         $loccheck = '';
@@ -2117,7 +2762,7 @@ sub authform_local{
         }
     } else {
         if ($authnum == 1) {
-            $authtype = '<input type="hidden" name="login" value="loc">';
+            $authtype = '<input type="hidden" name="login" value="loc" />';
         }
     }
     if (!$can_assign{'loc'}) {
@@ -2126,7 +2771,7 @@ sub authform_local{
         if (defined($in{'mode'})) {
             if ($in{'mode'} eq 'modifycourse') {
                 if ($authnum == 1) {
-                    $authtype = '<input type="hidden" name="login" value="loc">';
+                    $authtype = '<input type="radio" name="login" value="loc" />';
                 }
             }
         }
@@ -2144,18 +2789,18 @@ sub authform_local{
     return $result;
 }
 
-sub authform_filesystem{  
+sub authform_filesystem {
     my %in = (
               formname => 'document.cu',
               kerb_def_dom => 'MSU.EDU',
               @_,
               );
     my ($fsyscheck,$result,$authtype,$autharg,$jscall);
-    my ($authnum,%can_assign) =  &get_assignable_auth($in{'domain'});
+    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" ';
+                $fsyscheck = 'checked="checked" ';
                 if (defined($in{'mode'})) {
                     if ($in{'mode'} eq 'modifyuser') {
                         $fsyscheck = '';
@@ -2168,7 +2813,7 @@ sub authform_filesystem{
         }
     } else {
         if ($authnum == 1) {
-            $authtype = '<input type="hidden" name="login" value="fsys">';
+            $authtype = '<input type="hidden" name="login" value="fsys" />';
         }
     }
     if (!$can_assign{'fsys'}) {
@@ -2177,7 +2822,7 @@ sub authform_filesystem{
         if (defined($in{'mode'})) {
             if ($in{'mode'} eq 'modifycourse') {
                 if ($authnum == 1) {
-                    $authtype = '<input type="hidden" name="login" value="fsys">';
+                    $authtype = '<input type="radio" name="login" value="fsys" />';
                 }
             }
         }
@@ -2242,42 +2887,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                 ##
 ###############################################################
 ##
@@ -2289,22 +2898,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/;
@@ -2313,11 +2931,6 @@ sub get_kerberos_defaults {
     return ($krbdef,$krbdefdom);
 }
 
-=pod
-
-=back
-
-=cut
 
 ###############################################################
 ##                Thesaurus Functions                        ##
@@ -2329,7 +2942,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.
@@ -2374,7 +2987,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 
@@ -2395,7 +3008,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
@@ -2404,6 +3017,7 @@ database which holds them.
 
 Uses global $thesaurus_db_file.
 
+
 =cut
 
 ###############################################################
@@ -2439,6 +3053,45 @@ sub get_related_words {
     untie %thesaurus_db;
     return @Words;
 }
+###############################################################
+#
+#  Spell checking
+#
+
+=pod
+
+=head1 Spell checking
+
+=over 4
+
+=item * &check_spelling($wordlist $language)
+
+Takes a string containing words and feeds it to an external
+spellcheck program via a pipeline. Returns a string containing
+them mis-spelled words.
+
+Parameters:
+
+=over 4
+
+=item - $wordlist
+
+String that will be fed into the spellcheck program.
+
+=item - $language
+
+Language string that specifies the language for which the spell
+check will be performed.
+
+=back
+
+=back
+
+Note: This sub assumes that aspell is installed.
+
+
+=cut
+
 
 =pod
 
@@ -2446,6 +3099,31 @@ sub get_related_words {
 
 =cut
 
+sub check_spelling {
+    my ($wordlist, $language) = @_;
+    my @misspellings;
+    
+    # Generate the speller and set the langauge.
+    # if explicitly selected:
+
+    my $speller = Text::Aspell->new;
+    if ($language) {
+	$speller->set_option('lang', $language);
+    }
+
+    # Turn the word list into an array of words by splittingon whitespace
+
+    my @words = split(/\s+/, $wordlist);
+
+    foreach my $word (@words) {
+	if(! $speller->check($word)) {
+	    push(@misspellings, $word);
+	}
+    }
+    return join(' ', @misspellings);
+    
+}
+
 # -------------------------------------------------------------- Plaintext name
 =pod
 
@@ -2453,7 +3131,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 
@@ -2482,7 +3160,7 @@ sub plainname {
 # -------------------------------------------------------------------- Nickname
 =pod
 
-=item * nickname($uname,$udom)
+=item * &nickname($uname,$udom)
 
 Gets a users name and returns it as a string as
 
@@ -2532,18 +3210,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') {
@@ -2574,11 +3255,48 @@ sub flush_email_cache {
     &Apache::lonnet::devalidate_cache_new('emailscache',$id);
 }
 
+# -------------------------------------------------------------------- getlangs
+
+=pod
+
+=item * &getlangs($uname,$udom)
+
+Gets a user's language preference and returns it as a hash with key:
+language.
+
+=cut
+
+
+sub getlangs {
+    my ($uname,$udom) = @_;
+    if (!$udom)  { $udom =$env{'user.domain'}; }
+    if (!$uname) { $uname=$env{'user.name'};   }
+    my $id=$uname.':'.$udom;
+    my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
+    if ($cached) {
+        return %{$langs};
+    } else {
+        my %loadlangs=&Apache::lonnet::get('environment',['languages'],
+                                           $udom,$uname);
+        &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
+        return %loadlangs;
+    }
+}
+
+sub flush_langs_cache {
+    my ($uname,$udom)=@_;
+    if (!$udom)  { $udom =$env{'user.domain'}; }
+    if (!$uname) { $uname=$env{'user.name'};   }
+    return if ($udom eq 'public' && $uname eq 'public');
+    my $id=$uname.':'.$udom;
+    &Apache::lonnet::devalidate_cache_new('userlangs',$id);
+}
+
 # ------------------------------------------------------------------ Screenname
 
 =pod
 
-=item * screenname($uname,$udom)
+=item * &screenname($uname,$udom)
 
 Gets a users screenname and returns it as a string
 
@@ -2593,6 +3311,26 @@ sub screenname {
 }
 
 
+# ------------------------------------------------------------- Confirm Wrapper
+=pod
+
+=item confirmwrapper
+
+Wrap messages about completion of operation in box
+
+=cut
+
+sub confirmwrapper {
+    my ($message)=@_;
+    if ($message) {
+        return "\n".'<div class="LC_confirm_box">'."\n"
+               .$message."\n"
+               .'</div>'."\n";
+    } else {
+        return $message;
+    }
+}
+
 # ------------------------------------------------------------- Message Wrapper
 
 sub messagewrapper {
@@ -2603,37 +3341,37 @@ sub messagewrapper {
 	'&amp;subject='.&escape($subject).'&amp;text='.&escape($text).'" '.
         'title="'.&mt('Send message').'">'.$link.'</a>';
 }
+
 # --------------------------------------------------------------- Notes Wrapper
 
 sub noteswrapper {
     my ($link,$un,$do)=@_;
     return 
-"<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
+"<a href='/adm/email?recordftf=retrieve&amp;recname=$un&amp;recdom=$do'>$link</a>";
 }
+
 # ------------------------------------------------------------- Aboutme Wrapper
 
 sub aboutmewrapper {
-    my ($link,$username,$domain,$target)=@_;
+    my ($link,$username,$domain,$target,$class)=@_;
     if (!defined($username)  && !defined($domain)) {
         return;
     }
     return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
-	($target?' target="$target"':'').' title="'.&mt("View this user's personal page").'">'.$link.'</a>';
+	($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
 }
 
 # ------------------------------------------------------------ Syllabus Wrapper
 
-
 sub syllabuswrapper {
-    my ($linktext,$coursedir,$domain,$fontcolor)=@_;
-    if ($fontcolor) { 
-        $linktext='<font color="'.$fontcolor.'">'.$linktext.'</font>'; 
-    }
+    my ($linktext,$coursedir,$domain)=@_;
     return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
 }
 
+# -----------------------------------------------------------------------------
+
 sub track_student_link {
-    my ($linktext,$sname,$sdom,$target,$start) = @_;
+    my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
     my $link ="/adm/trackstudent?";
     my $title = 'View recent activity';
     if (defined($sname) && $sname !~ /^\s*$/ &&
@@ -2647,12 +3385,34 @@ sub track_student_link {
         $target = '';
     }
     if ($start) { $link.='&amp;start='.$start; }
+    if ($only_body) { $link .= '&amp;only_body=1'; }
     $title = &mt($title);
     $linktext = &mt($linktext);
     return qq{<a href="$link" title="$title" $target>$linktext</a>}.
 	&help_open_topic('View_recent_activity');
 }
 
+sub slot_reservations_link {
+    my ($linktext,$sname,$sdom,$target) = @_;
+    my $link ="/adm/slotrequest?command=showresv&amp;origin=aboutme";
+    my $title = 'View slot reservation history';
+    if (defined($sname) && $sname !~ /^\s*$/ &&
+        defined($sdom)  && $sdom  !~ /^\s*$/) {
+        $link .= "&amp;uname=$sname&amp;udom=$sdom";
+        $title .= ' of this student';
+    }
+    if (defined($target) && $target !~ /^\s*$/) {
+        $target = qq{target="$target"};
+    } else {
+        $target = '';
+    }
+    $title = &mt($title);
+    $linktext = &mt($linktext);
+    return qq{<a href="$link" title="$title" $target>$linktext</a>};
+# FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
+
+}
+
 # ===================================================== Display a student photo
 
 
@@ -2674,7 +3434,7 @@ sub student_image_tag {
 
 =over 4
 
-=item * languageids() 
+=item * &languageids() 
 
 returns list of all language ids
 
@@ -2686,7 +3446,7 @@ sub languageids {
 
 =pod
 
-=item * languagedescription() 
+=item * &languagedescription() 
 
 returns description of a specified language id
 
@@ -2699,11 +3459,29 @@ sub languagedescription {
 	    ($supported_language{$code}?' ('.&mt('interface available').')':'');
 }
 
+=pod
+
+=item * &plainlanguagedescription
+
+Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
+and the language character encoding (e.g. ISO) separated by a ' - ' string.
+
+=cut
+
 sub plainlanguagedescription {
     my $code=shift;
     return $language{$code};
 }
 
+=pod
+
+=item * &supportedlanguagecode
+
+Returns the supported language code (e.g. sptutf maps to pt) given a language
+code.
+
+=cut
+
 sub supportedlanguagecode {
     my $code=shift;
     return $supported_language{$code};
@@ -2711,7 +3489,36 @@ sub supportedlanguagecode {
 
 =pod
 
-=item * copyrightids() 
+=item * &latexlanguage()
+
+Given a language key code returns the correspondnig language to use
+to select the correct hyphenation on LaTeX printouts.  This is undef if there
+is no supported hyphenation for the language code.
+
+=cut
+
+sub latexlanguage {
+    my $code = shift;
+    return $latex_language{$code};
+}
+
+=pod
+
+=item * &latexhyphenation()
+
+Same as above but what's supplied is the language as it might be stored
+in the metadata.
+
+=cut
+
+sub latexhyphenation {
+    my $key = shift;
+    return $latex_language_bykey{$key};
+}
+
+=pod
+
+=item * &copyrightids() 
 
 returns list of all copyrights
 
@@ -2723,7 +3530,7 @@ sub copyrightids {
 
 =pod
 
-=item * copyrightdescription() 
+=item * &copyrightdescription() 
 
 returns description of a specified copyright id
 
@@ -2735,7 +3542,7 @@ sub copyrightdescription {
 
 =pod
 
-=item * source_copyrightids() 
+=item * &source_copyrightids() 
 
 returns list of all source copyrights
 
@@ -2747,7 +3554,7 @@ sub source_copyrightids {
 
 =pod
 
-=item * source_copyrightdescription() 
+=item * &source_copyrightdescription() 
 
 returns description of a specified source copyright id
 
@@ -2759,7 +3566,7 @@ sub source_copyrightdescription {
 
 =pod
 
-=item * filecategories() 
+=item * &filecategories() 
 
 returns list of all file categories
 
@@ -2771,7 +3578,7 @@ sub filecategories {
 
 =pod
 
-=item * filecategorytypes() 
+=item * &filecategorytypes() 
 
 returns list of file types belonging to a given file
 category
@@ -2785,7 +3592,7 @@ sub filecategorytypes {
 
 =pod
 
-=item * fileembstyle() 
+=item * &fileembstyle() 
 
 returns embedding style for a specified file type
 
@@ -2803,13 +3610,12 @@ sub filemimetype {
 sub filecategoryselect {
     my ($name,$value)=@_;
     return &select_form($value,$name,
-			'' => &mt('Any category'),
-			map { $_,$_ } sort(keys(%category_extensions)));
+                        {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
 }
 
 =pod
 
-=item * filedescription() 
+=item * &filedescription() 
 
 returns description for a specified file type
 
@@ -2823,7 +3629,7 @@ sub filedescription {
 
 =pod
 
-=item * filedescriptionex() 
+=item * &filedescriptionex() 
 
 returns description for a specified file type with
 extra formatting
@@ -2855,7 +3661,7 @@ sub fileextensions {
 
 sub display_languages {
     my %languages=();
-    foreach my $lang (&preferred_languages()) {
+    foreach my $lang (&Apache::lonlocal::preferred_languages()) {
 	$languages{$lang}=1;
     }
     &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
@@ -2867,56 +3673,9 @@ sub display_languages {
     return %languages;
 }
 
-sub preferred_languages {
-    my @languages=();
-    if ($env{'course.'.$env{'request.course.id'}.'.languages'}) {
-	@languages=(@languages,split(/\s*(\,|\;|\:)\s*/,
-	         $env{'course.'.$env{'request.course.id'}.'.languages'}));
-    }
-    if ($env{'environment.languages'}) {
-	@languages=(@languages,
-		    split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'}));
-    }
-    my $browser=$ENV{'HTTP_ACCEPT_LANGUAGE'};
-    if ($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,
-		    &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'));
-    }
-# turn "en-ca" into "en-ca,en"
-    my @genlanguages;
-    foreach my $lang (@languages) {
-	unless ($lang=~/\w/) { next; }
-	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();
+    my @preferred_langs = &Apache::lonlocal::preferred_languages();
     if (!ref($possible_langs)) {
 	if( wantarray ) {
 	    return @preferred_langs;
@@ -2937,6 +3696,29 @@ sub languages {
     return $preferred_possibilities[0];
 }
 
+sub user_lang {
+    my ($touname,$toudom,$fromcid) = @_;
+    my @userlangs;
+    if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
+        @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
+                    $env{'course.'.$fromcid.'.languages'}));
+    } else {
+        my %langhash = &getlangs($touname,$toudom);
+        if ($langhash{'languages'} ne '') {
+            @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
+        } else {
+            my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
+            if ($domdefs{'lang_def'} ne '') {
+                @userlangs = ($domdefs{'lang_def'});
+            }
+        }
+    }
+    my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
+    my $user_lh = Apache::localize->get_handle(@languages);
+    return $user_lh;
+}
+
+
 ###############################################################
 ##               Student Answer Attempts                     ##
 ###############################################################
@@ -2947,7 +3729,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:
@@ -2993,12 +3775,24 @@ sub get_previous_attempt {
       }
       $prevattempts=&start_data_table().&start_data_table_header_row();
       $prevattempts.='<th>'.&mt('History').'</th>';
+      my (%typeparts,%lasthidden);
+      my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
       foreach my $key (sort(keys(%lasthash))) {
 	my ($ign,@parts) = split(/\./,$key);
 	if ($#parts > 0) {
 	  my $data=$parts[-1];
+          next if ($data eq 'foilorder');
 	  pop(@parts);
-	  $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
+          $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.'&nbsp;</th>';
+          if ($data eq 'type') {
+              unless ($showsurv) {
+                  my $id = join(',',@parts);
+                  $typeparts{$ign.'.'.$id} = $lasthash{$key};
+                  if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
+                      $lasthidden{$ign.'.'.$id} = 1;
+                  }
+              }
+          } 
 	} else {
 	  if ($#parts == 0) {
 	    $prevattempts.='<th>'.$parts[0].'</th>';
@@ -3010,21 +3804,93 @@ sub get_previous_attempt {
       $prevattempts.=&end_data_table_header_row();
       if ($getattempt eq '') {
 	for ($version=1;$version<=$returnhash{'version'};$version++) {
-	  $prevattempts.=&start_data_table_row().
-	      '<td>'.&mt('Transaction [_1]',$version).'</td>';
-	    foreach my $key (sort(keys(%lasthash))) {
-		my $value = &format_previous_attempt_value($key,
-							   $returnhash{$version.':'.$key});
-		$prevattempts.='<td>'.$value.'&nbsp;</td>';   
-	    }
-	  $prevattempts.=&end_data_table_row();
+            my @hidden;
+            if (%typeparts) {
+                foreach my $id (keys(%typeparts)) {
+                    if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
+                        push(@hidden,$id);
+                    }
+                }
+            }
+            $prevattempts.=&start_data_table_row().
+                           '<td>'.&mt('Transaction [_1]',$version).'</td>';
+            if (@hidden) {
+                foreach my $key (sort(keys(%lasthash))) {
+                    next if ($key =~ /\.foilorder$/);
+                    my $hide;
+                    foreach my $id (@hidden) {
+                        if ($key =~ /^\Q$id\E/) {
+                            $hide = 1;
+                            last;
+                        }
+                    }
+                    if ($hide) {
+                        my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
+                        if (($data eq 'award') || ($data eq 'awarddetail')) {
+                            my $value = &format_previous_attempt_value($key,
+                                             $returnhash{$version.':'.$key});
+                            $prevattempts.='<td>'.$value.'&nbsp;</td>';
+                        } else {
+                            $prevattempts.='<td>&nbsp;</td>';
+                        }
+                    } else {
+                        if ($key =~ /\./) {
+                            my $value = &format_previous_attempt_value($key,
+                                              $returnhash{$version.':'.$key});
+                            $prevattempts.='<td>'.$value.'&nbsp;</td>';
+                        } else {
+                            $prevattempts.='<td>&nbsp;</td>';
+                        }
+                    }
+                }
+            } else {
+	        foreach my $key (sort(keys(%lasthash))) {
+                    next if ($key =~ /\.foilorder$/);
+		    my $value = &format_previous_attempt_value($key,
+			            $returnhash{$version.':'.$key});
+		    $prevattempts.='<td>'.$value.'&nbsp;</td>';
+	        }
+            }
+	    $prevattempts.=&end_data_table_row();
 	 }
       }
+      my @currhidden = keys(%lasthidden);
       $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
       foreach my $key (sort(keys(%lasthash))) {
-	my $value = &format_previous_attempt_value($key,$lasthash{$key});
-	if ($key =~/$regexp$/ && (defined &$gradesub)) {$value = &$gradesub($value)}
-	$prevattempts.='<td>'.$value.'&nbsp;</td>';
+          next if ($key =~ /\.foilorder$/);
+          if (%typeparts) {
+              my $hidden;
+              foreach my $id (@currhidden) {
+                  if ($key =~ /^\Q$id\E/) {
+                      $hidden = 1;
+                      last;
+                  }
+              }
+              if ($hidden) {
+                  my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
+                  if (($data eq 'award') || ($data eq 'awarddetail')) {
+                      my $value = &format_previous_attempt_value($key,$lasthash{$key});
+                      if ($key =~/$regexp$/ && (defined &$gradesub)) {
+                          $value = &$gradesub($value);
+                      }
+                      $prevattempts.='<td>'.$value.'&nbsp;</td>';
+                  } else {
+                      $prevattempts.='<td>&nbsp;</td>';
+                  }
+              } else {
+                  my $value = &format_previous_attempt_value($key,$lasthash{$key});
+                  if ($key =~/$regexp$/ && (defined &$gradesub)) {
+                      $value = &$gradesub($value);
+                  }
+                  $prevattempts.='<td>'.$value.'&nbsp;</td>';
+              }
+          } else {
+	      my $value = &format_previous_attempt_value($key,$lasthash{$key});
+	      if ($key =~/$regexp$/ && (defined &$gradesub)) {
+                  $value = &$gradesub($value);
+              }
+	      $prevattempts.='<td>'.$value.'&nbsp;</td>';
+          }
       }
       $prevattempts.= &end_data_table_row().&end_data_table();
     } else {
@@ -3043,10 +3909,33 @@ sub get_previous_attempt {
 
 sub format_previous_attempt_value {
     my ($key,$value) = @_;
-    if ($key =~ /timestamp/) {
+    if (($key =~ /timestamp/) || ($key=~/duedate/)) {
 	$value = &Apache::lonlocal::locallocaltime($value);
     } elsif (ref($value) eq 'ARRAY') {
 	$value = '('.join(', ', @{ $value }).')';
+    } elsif ($key =~ /answerstring$/) {
+        my %answers = &Apache::lonnet::str2hash($value);
+        my @anskeys = sort(keys(%answers));
+        if (@anskeys == 1) {
+            my $answer = $answers{$anskeys[0]};
+            if ($answer =~ m{\0}) {
+                $answer =~ s{\0}{,}g;
+            }
+            my $tag_internal_answer_name = 'INTERNAL';
+            if ($anskeys[0] eq $tag_internal_answer_name) {
+                $value = $answer; 
+            } else {
+                $value = $anskeys[0].'='.$answer;
+            }
+        } else {
+            foreach my $ans (@anskeys) {
+                my $answer = $answers{$ans};
+                if ($answer =~ m{\0}) {
+                    $answer =~ s{\0}{,}g;
+                }
+                $value .=  $ans.'='.$answer.'<br />';;
+            } 
+        }
     } else {
 	$value = &unescape($value);
     }
@@ -3075,7 +3964,7 @@ sub relative_to_absolute {
     }
     $thisdir=~s-/[^/]*$--;
     foreach my $link (@rlinks) {
-	unless (($link=~/^http:\/\//i) ||
+	unless (($link=~/^https?\:\/\//i) ||
 		($link=~/^\//) ||
 		($link=~/^javascript:/i) ||
 		($link=~/^mailto:/i) ||
@@ -3091,7 +3980,7 @@ sub relative_to_absolute {
 
 =pod
 
-=item * get_student_view
+=item * &get_student_view()
 
 show a snapshot of what student was looking at
 
@@ -3110,7 +3999,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;
@@ -3119,12 +4008,44 @@ 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.
+    }
+    if (wantarray) {
+       return ($content, $response);
+    } else {
+       return $content;
+    }
 }
 
 =pod
 
-=item * get_student_answers() 
+=item * &get_student_answers() 
 
 show a snapshot of how student was answering problem
 
@@ -3165,10 +4086,13 @@ sub submlink {
     }
     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
     $symb=&escape($symb);
-    if ($target) { $target="target=\"$target\""; }
-    return '<a href="/adm/grades?&command=submission&'.
-	'symb='.$symb.'&student='.$uname.
-	'&userdom='.$udom.'" '.$target.'>'.$text.'</a>';
+    if ($target) { $target=" target=\"$target\""; }
+    return
+        '<a href="/adm/grades?command=submission'.
+        '&amp;symb='.$symb.
+        '&amp;student='.$uname.
+        '&amp;userdom='.$udom.'"'.
+        $target.'>'.$text.'</a>';
 }
 ##############################################
 
@@ -3228,16 +4152,21 @@ sub pprmlink {
 
 
 sub timehash {
-    my @ltime=localtime(shift);
-    return ( 'seconds' => $ltime[0],
-             'minutes' => $ltime[1],
-             'hours'   => $ltime[2],
-             'day'     => $ltime[3],
-             'month'   => $ltime[4]+1,
-             'year'    => $ltime[5]+1900,
-             'weekday' => $ltime[6],
-             'dayyear' => $ltime[7]+1,
-             'dlsav'   => $ltime[8] );
+    my ($thistime) = @_;
+    my $timezone = &Apache::lonlocal::gettimezone();
+    my $dt = DateTime->from_epoch(epoch => $thistime)
+                     ->set_time_zone($timezone);
+    my $wday = $dt->day_of_week();
+    if ($wday == 7) { $wday = 0; }
+    return ( 'second' => $dt->second(),
+             'minute' => $dt->minute(),
+             'hour'   => $dt->hour(),
+             'day'     => $dt->day_of_month(),
+             'month'   => $dt->month(),
+             'year'    => $dt->year(),
+             'weekday' => $wday,
+             'dayyear' => $dt->day_of_year(),
+             'dlsav'   => $dt->is_dst() );
 }
 
 sub utc_string {
@@ -3247,6 +4176,24 @@ sub utc_string {
 
 sub maketime {
     my %th=@_;
+    my ($epoch_time,$timezone,$dt);
+    $timezone = &Apache::lonlocal::gettimezone();
+    eval {
+        $dt = DateTime->new( year   => $th{'year'},
+                             month  => $th{'month'},
+                             day    => $th{'day'},
+                             hour   => $th{'hour'},
+                             minute => $th{'minute'},
+                             second => $th{'second'},
+                             time_zone => $timezone,
+                         );
+    };
+    if (!$@) {
+        $epoch_time = $dt->epoch;
+        if ($epoch_time) {
+            return $epoch_time;
+        }
+    }
     return POSIX::mktime(
         ($th{'seconds'},$th{'minutes'},$th{'hours'},
          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
@@ -3271,6 +4218,7 @@ sub findallcourses {
         if (!%roles) {
             %roles = (
                        cc => 1,
+                       co => 1,
                        in => 1,
                        ep => 1,
                        ta => 1,
@@ -3291,18 +4239,25 @@ sub findallcourses {
             if ($tstart) {
                 next if ($tstart > $now);
             }
-            my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
+            my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
             (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
+            my $value = $trole.'/'.$cdom.'/';
             if ($secpart eq '') {
                 ($cnum,$role) = split(/_/,$cnumpart); 
                 $sec = 'none';
-                $realsec = '';
+                $value .= $cnum.'/';
             } else {
                 $cnum = $cnumpart;
                 ($sec,$role) = split(/_/,$secpart);
-                $realsec = $sec;
+                $value .= $cnum.'/'.$sec;
+            }
+            if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
+                unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
+                    push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
+                }
+            } else {
+                @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
             }
-            $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
         }
     } else {
         foreach my $key (keys(%env)) {
@@ -3320,11 +4275,19 @@ sub findallcourses {
                     if ($now>$endtime) { $active=0; }
                 }
                 if ($active) {
+                    my $value = $role.'/'.$cdom.'/'.$cnum.'/';
                     if ($sec eq '') {
                         $sec = 'none';
+                    } else {
+                        $value .= $sec;
+                    }
+                    if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
+                        unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
+                            push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
+                        }
+                    } else {
+                        @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
                     }
-                    $courses{$cdom.'_'.$cnum}{$sec} = 
-                                     $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
                 }
             }
         }
@@ -3335,7 +4298,7 @@ sub findallcourses {
 ###############################################
 
 sub blockcheck {
-    my ($setters,$activity,$uname,$udom) = @_;
+    my ($setters,$activity,$uname,$udom,$url) = @_;
 
     if (!defined($udom)) {
         $udom = $env{'user.domain'};
@@ -3347,13 +4310,14 @@ sub blockcheck {
     # If uname and udom are for a course, check for blocks in the course.
 
     if (&Apache::lonnet::is_course($udom,$uname)) {
-        my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
-        my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
-        return ($startblock,$endblock);
+        my ($startblock,$endblock,$triggerblock) = 
+            &get_blocks($setters,$activity,$udom,$uname,$url);
+        return ($startblock,$endblock,$triggerblock);
     }
 
     my $startblock = 0;
     my $endblock = 0;
+    my $triggerblock = '';
     my %live_courses = &findallcourses(undef,$uname,$udom);
 
     # If uname is for a user, and activity is course-specific, i.e.,
@@ -3417,34 +4381,38 @@ sub blockcheck {
             if ($otheruser) {
                 # Resource belongs to user other than current user.
                 # Assemble privs for that user, and check for 'evb' priv.
-                my ($trole,$tdom,$tnum,$tsec);
-                my $entry = $live_courses{$course}{$sec};
-                if ($entry =~ /^cr/) {
-                    ($trole,$tdom,$tnum,$tsec) = 
-                      ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
-                } else {
-                    ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
-                }
-                my ($spec,$area,$trest,%allroles,%userroles);
-                $area = '/'.$tdom.'/'.$tnum;
-                $trest = $tnum;
-                if ($tsec ne '') {
-                    $area .= '/'.$tsec;
-                    $trest .= '/'.$tsec;
-                }
-                $spec = $trole.'.'.$area;
-                if ($trole =~ /^cr/) {
-                    &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
-                                                      $tdom,$spec,$trest,$area);
-                } else {
-                    &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
-                                                       $tdom,$spec,$trest,$area);
-                }
-                my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
-                if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
-                    if ($1) {
-                        $no_userblock = 1;
-                        last;
+                my (%allroles,%userroles);
+                if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
+                    foreach my $entry (@{$live_courses{$course}{$sec}}) { 
+                        my ($trole,$tdom,$tnum,$tsec);
+                        if ($entry =~ /^cr/) {
+                            ($trole,$tdom,$tnum,$tsec) = 
+                                ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
+                        } else {
+                           ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
+                        }
+                        my ($spec,$area,$trest);
+                        $area = '/'.$tdom.'/'.$tnum;
+                        $trest = $tnum;
+                        if ($tsec ne '') {
+                            $area .= '/'.$tsec;
+                            $trest .= '/'.$tsec;
+                        }
+                        $spec = $trole.'.'.$area;
+                        if ($trole =~ /^cr/) {
+                            &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
+                                                              $tdom,$spec,$trest,$area);
+                        } else {
+                            &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
+                                                                $tdom,$spec,$trest,$area);
+                        }
+                    }
+                    my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
+                    if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
+                        if ($1) {
+                            $no_userblock = 1;
+                            last;
+                        }
                     }
                 }
             } else {
@@ -3461,49 +4429,142 @@ sub blockcheck {
                  ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
         next if ($no_userblock);
 
-        # Retrieve blocking times and identity of blocker for course
+        # Retrieve blocking times and identity of locker for course
         # of specified user, unless user has 'evb' privilege.
         
-        my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
+        my ($start,$end,$trigger) = 
+            &get_blocks($setters,$activity,$cdom,$cnum,$url);
         if (($start != 0) && 
             (($startblock == 0) || ($startblock > $start))) {
             $startblock = $start;
+            if ($trigger ne '') {
+                $triggerblock = $trigger;
+            }
         }
         if (($end != 0)  &&
             (($endblock == 0) || ($endblock < $end))) {
             $endblock = $end;
+            if ($trigger ne '') {
+                $triggerblock = $trigger;
+            }
         }
     }
-    return ($startblock,$endblock);
+    return ($startblock,$endblock,$triggerblock);
 }
 
 sub get_blocks {
-    my ($setters,$activity,$cdom,$cnum) = @_;
+    my ($setters,$activity,$cdom,$cnum,$url) = @_;
     my $startblock = 0;
     my $endblock = 0;
+    my $triggerblock = '';
     my $course = $cdom.'_'.$cnum;
     $setters->{$course} = {};
     $setters->{$course}{'staff'} = [];
     $setters->{$course}{'times'} = [];
-    my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
-    foreach my $record (keys(%records)) {
-        my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
-        if ($start <= time && $end >= time) {
-            my ($staff_name,$staff_dom,$title,$blocks) =
-                &parse_block_record($records{$record});
-            if ($blocks->{$activity} eq 'on') {
-                push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
-                push(@{$$setters{$course}{'times'}}, [$start,$end]);
-                if ( ($startblock == 0) || ($startblock > $start) ) {
-                    $startblock = $start;
+    $setters->{$course}{'triggers'} = [];
+    my (@blockers,%triggered);
+    my $now = time;
+    my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
+    if ($activity eq 'docs') {
+        @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks);
+        foreach my $block (@blockers) {
+            if ($block =~ /^firstaccess____(.+)$/) {
+                my $item = $1;
+                my $type = 'map';
+                my $timersymb = $item;
+                if ($item eq 'course') {
+                    $type = 'course';
+                } elsif ($item =~ /___\d+___/) {
+                    $type = 'resource';
+                } else {
+                    $timersymb = &Apache::lonnet::symbread($item);
+                }
+                my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
+                my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
+                $triggered{$block} = {
+                                       start => $start,
+                                       end   => $end,
+                                       type  => $type,
+                                     };
+            }
+        }
+    } else {
+        foreach my $block (keys(%commblocks)) {
+            if ($block =~ m/^(\d+)____(\d+)$/) { 
+                my ($start,$end) = ($1,$2);
+                if ($start <= time && $end >= time) {
+                    if (ref($commblocks{$block}) eq 'HASH') {
+                        if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
+                            if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
+                                unless(grep(/^\Q$block\E$/,@blockers)) {
+                                    push(@blockers,$block);
+                                }
+                            }
+                        }
+                    }
                 }
-                if ( ($endblock == 0) || ($endblock < $end) ) {
-                    $endblock = $end;
+            } elsif ($block =~ /^firstaccess____(.+)$/) {
+                my $item = $1;
+                my $timersymb = $item; 
+                my $type = 'map';
+                if ($item eq 'course') {
+                    $type = 'course';
+                } elsif ($item =~ /___\d+___/) {
+                    $type = 'resource';
+                } else {
+                    $timersymb = &Apache::lonnet::symbread($item);
+                }
+                my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
+                my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb}; 
+                if ($start && $end) {
+                    if (($start <= time) && ($end >= time)) {
+                        unless (grep(/^\Q$block\E$/,@blockers)) {
+                            push(@blockers,$block);
+                            $triggered{$block} = {
+                                                   start => $start,
+                                                   end   => $end,
+                                                   type  => $type,
+                                                 };
+                        }
+                    }
+                }
+            }
+        }
+    }
+    foreach my $blocker (@blockers) {
+        my ($staff_name,$staff_dom,$title,$blocks) =
+            &parse_block_record($commblocks{$blocker});
+        push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
+        my ($start,$end,$triggertype);
+        if ($blocker =~ m/^(\d+)____(\d+)$/) {
+            ($start,$end) = ($1,$2);
+        } elsif (ref($triggered{$blocker}) eq 'HASH') {
+            $start = $triggered{$blocker}{'start'};
+            $end = $triggered{$blocker}{'end'};
+            $triggertype = $triggered{$blocker}{'type'};
+        }
+        if ($start) {
+            push(@{$$setters{$course}{'times'}}, [$start,$end]);
+            if ($triggertype) {
+                push(@{$$setters{$course}{'triggers'}},$triggertype);
+            } else {
+                push(@{$$setters{$course}{'triggers'}},0);
+            }
+            if ( ($startblock == 0) || ($startblock > $start) ) {
+                $startblock = $start;
+                if ($triggertype) {
+                    $triggerblock = $blocker;
                 }
             }
+            if ( ($endblock == 0) || ($endblock < $end) ) {
+               $endblock = $end;
+               if ($triggertype) {
+                   $triggerblock = $blocker;
+               }
+            }
         }
     }
-    return ($startblock,$endblock);
+    return ($startblock,$endblock,$triggerblock);
 }
 
 sub parse_block_record {
@@ -3526,103 +4587,116 @@ sub parse_block_record {
     return ($setuname,$setudom,$title,$blocks);
 }
 
-sub build_block_table {
-    my ($startblock,$endblock,$setters) = @_;
-    my %lt = &Apache::lonlocal::texthash(
-        'cacb' => 'Currently active communication blocks',
-        'cour' => 'Course',
-        'dura' => 'Duration',
-        'blse' => 'Block set by'
-    );
-    my $output;
-    $output = '<br />'.$lt{'cacb'}.':<br />';
-    $output .= &start_data_table();
-    $output .= '
-<tr>
- <th>'.$lt{'cour'}.'</th>
- <th>'.$lt{'dura'}.'</th>
- <th>'.$lt{'blse'}.'</th>
-</tr>
-';
-    foreach my $course (keys(%{$setters})) {
-        my %courseinfo=&Apache::lonnet::coursedescription($course);
-        for (my $i=0; $i<@{$$setters{$course}{staff}}; $i++) {
-            my ($uname,$udom) = @{$$setters{$course}{staff}[$i]};
-            my $fullname = &plainname($uname,$udom);
-            if (defined($env{'user.name'}) && defined($env{'user.domain'})
-                && $env{'user.name'} ne 'public' 
-                && $env{'user.domain'} ne 'public') {
-                $fullname = &aboutmewrapper($fullname,$uname,$udom);
-            }
-            my ($openblock,$closeblock) = @{$$setters{$course}{times}[$i]};
-            $openblock = &Apache::lonlocal::locallocaltime($openblock);
-            $closeblock= &Apache::lonlocal::locallocaltime($closeblock);
-            $output .= &Apache::loncommon::start_data_table_row().
-                       '<td>'.$courseinfo{'description'}.'</td>'.
-                       '<td>'.$openblock.' to '.$closeblock.'</td>'.
-                       '<td>'.$fullname.'</td>'.
-                        &Apache::loncommon::end_data_table_row();
-        }
-    }
-    $output .= &end_data_table();
-}
-
 sub blocking_status {
-    my ($activity,$uname,$udom) = @_;
+    my ($activity,$uname,$udom,$url) = @_;
     my %setters;
-    my ($blocked,$output,$ownitem,$is_course);
-    my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
+
+# check for active blocking
+    my ($startblock,$endblock,$triggerblock) = 
+        &blockcheck(\%setters,$activity,$uname,$udom,$url);
+    my $blocked = 0;
     if ($startblock && $endblock) {
         $blocked = 1;
-        if (wantarray) {
-            my $category;
-            if ($activity eq 'boards') {
-                $category = 'Discussion posts in this course';
-            } elsif ($activity eq 'blogs') {
-                $category = 'Blogs';
-            } elsif ($activity eq 'port') {
-                if (defined($uname) && defined($udom)) {
-                    if ($uname eq $env{'user.name'} &&
-                        $udom eq $env{'user.domain'}) {
-                        $ownitem = 1;
-                    }
-                }
-                $is_course = &Apache::lonnet::is_course($udom,$uname);
-                if ($ownitem) { 
-                    $category = 'Your portfolio files';  
-                } elsif ($is_course) {
-                    my $coursedesc;
-                    foreach my $course (keys(%setters)) {
-                        my %courseinfo =
-                             &Apache::lonnet::coursedescription($course);
-                        $coursedesc = $courseinfo{'description'};
-                    }
-                    $category = "Group files in the course '$coursedesc'";
-                } else {
-                    $category = 'Portfolio files belonging to ';
-                    if ($env{'user.name'} eq 'public' && 
-                        $env{'user.domain'} eq 'public') {
-                        $category .= &plainname($uname,$udom);
-                    } else {
-                        $category .= &aboutmewrapper(&plainname($uname,$udom),$uname,$udom);  
-                    }
-                }
-            } elsif ($activity eq 'groups') {
-                $category = 'Groups in this course';
-            }
-            my $showstart = &Apache::lonlocal::locallocaltime($startblock);
-            my $showend = &Apache::lonlocal::locallocaltime($endblock);
-            $output = '<br />'.&mt('[_1] will be inaccessible between [_2] and [_3] because communication is being blocked.',$category,$showstart,$showend).'<br />';
-            if (!($activity eq 'port' && !($ownitem) && !($is_course))) { 
-                $output .= &build_block_table($startblock,$endblock,\%setters);
+    }
+
+# caller just wants to know whether a block is active
+    if (!wantarray) { return $blocked; }
+
+# build a link to a popup window containing the details
+    my $querystring  = "?activity=$activity";
+# $uname and $udom decide whose portfolio the user is trying to look at
+    if ($activity eq 'port') {
+        $querystring .= "&amp;udom=$udom"      if $udom;
+        $querystring .= "&amp;uname=$uname"    if $uname;
+    } elsif ($activity eq 'docs') {
+        $querystring .= '&amp;url='.&HTML::Entities::encode($url,'&"');
+    }
+
+    my $output .= <<'END_MYBLOCK';
+function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
+    var options = "width=" + w + ",height=" + h + ",";
+    options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
+    options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
+    var newWin = window.open(url, wdwName, options);
+    newWin.focus();
+}
+END_MYBLOCK
+
+    $output = Apache::lonhtmlcommon::scripttag($output);
+  
+    my $popupUrl = "/adm/blockingstatus/$querystring";
+    my $text = &mt('Communication Blocked');
+    if ($activity eq 'docs') {
+        $text = &mt('Content Access Blocked');
+    } elsif ($activity eq 'printout') {
+        $text = &mt('Printing Blocked');
+    }
+    $output .= <<"END_BLOCK";
+<div class='LC_comblock'>
+  <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
+  title='$text'>
+  <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
+  <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring' 
+  title='$text'>$text</a>
+</div>
+
+END_BLOCK
+
+    return ($blocked, $output);
+}
+
+###############################################
+
+sub check_ip_acc {
+    my ($acc)=@_;
+    &Apache::lonxml::debug("acc is $acc");
+    if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
+        return 1;
+    }
+    my $allowed=0;
+    my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'};
+
+    my $name;
+    foreach my $pattern (split(',',$acc)) {
+        $pattern =~ s/^\s*//;
+        $pattern =~ s/\s*$//;
+        if ($pattern =~ /\*$/) {
+            #35.8.*
+            $pattern=~s/\*//;
+            if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
+        } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
+            #35.8.3.[34-56]
+            my $low=$2;
+            my $high=$3;
+            $pattern=$1;
+            if ($ip =~ /^\Q$pattern\E/) {
+                my $last=(split(/\./,$ip))[3];
+                if ($last <=$high && $last >=$low) { $allowed=1; }
+            }
+        } elsif ($pattern =~ /^\*/) {
+            #*.msu.edu
+            $pattern=~s/\*//;
+            if (!defined($name)) {
+                use Socket;
+                my $netaddr=inet_aton($ip);
+                ($name)=gethostbyaddr($netaddr,AF_INET);
+            }
+            if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
+        } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
+            #127.0.0.1
+            if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
+        } else {
+            #some.name.com
+            if (!defined($name)) {
+                use Socket;
+                my $netaddr=inet_aton($ip);
+                ($name)=gethostbyaddr($netaddr,AF_INET);
             }
+            if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
         }
+        if ($allowed) { last; }
     }
-    if (wantarray) {
-        return ($blocked,$output);
-    } else {
-        return $blocked;
-    }
+    return $allowed;
 }
 
 ###############################################
@@ -3646,7 +4720,7 @@ sub determinedomain {
     my $domain=shift;
     if (! $domain) {
         # Determine domain if we have not been given one
-        $domain = $Apache::lonnet::perlvar{'lonDefDomain'};
+        $domain = &Apache::lonnet::default_login_domain();
         if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
         if ($env{'request.role.domain'}) { 
             $domain=$env{'request.role.domain'}; 
@@ -3669,13 +4743,42 @@ sub get_domainconf {
     if (defined($cached)) { return %{$result}; }
 
     my %domconfig = &Apache::lonnet::get_dom('configuration',
-					     ['login','rolecolors'],$udom);
+					     ['login','rolecolors','autoenroll'],$udom);
     my (%designhash,%legacy);
     if (keys(%domconfig) > 0) {
         if (ref($domconfig{'login'}) eq 'HASH') {
             if (keys(%{$domconfig{'login'}})) {
                 foreach my $key (keys(%{$domconfig{'login'}})) {
-                    $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
+                    if (ref($domconfig{'login'}{$key}) eq 'HASH') {
+                        if ($key eq 'loginvia') {
+                            if (ref($domconfig{'login'}{'loginvia'}) eq 'HASH') {
+                                foreach my $hostname (keys(%{$domconfig{'login'}{'loginvia'}})) {
+                                    if (ref($domconfig{'login'}{'loginvia'}{$hostname}) eq 'HASH') {
+                                        if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
+                                            my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
+                                            $designhash{$udom.'.login.loginvia'} = $server;
+                                            if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
+
+                                                $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
+                                            } else {
+                                                $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
+                                            }
+                                            if ($domconfig{'login'}{'loginvia'}{$hostname}{'exempt'}) {
+                                                $designhash{$udom.'.login.loginvia_exempt_'.$hostname} = $domconfig{'login'}{'loginvia'}{$hostname}{'exempt'};
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        } else {
+                            foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
+                                $designhash{$udom.'.login.'.$key.'_'.$img} = 
+                                    $domconfig{'login'}{$key}{$img};
+                            }
+                        }
+                    } else {
+                        $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
+                    }
                 }
             } else {
                 $legacy{'login'} = 1;
@@ -3698,6 +4801,11 @@ sub get_domainconf {
         } else {
             $legacy{'rolecolors'} = 1;
         }
+        if (ref($domconfig{'autoenroll'}) eq 'HASH') {
+            if ($domconfig{'autoenroll'}{'co-owners'}) {
+                $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
+            }
+        }
         if (keys(%legacy) > 0) {
             my %legacyhash = &get_legacy_domconf($udom);
             foreach my $item (keys(%legacyhash)) {
@@ -3736,7 +4844,7 @@ sub get_legacy_domconf {
             close($fh);
         }
     }
-    if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
+    if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
         $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
     }
     return %legacyhash;
@@ -3790,41 +4898,156 @@ Returns: value of designparamter $which
 ##############################################
 sub designparm {
     my ($which,$domain)=@_;
-    if ($env{'browser.blackwhite'} eq 'on') {
-	if ($which=~/\.(font|alink|vlink|link|textcol)$/) {
-	    return '#000000';
-	}
-	if ($which=~/\.(pgbg|sidebg|bgcol)$/) {
-	    return '#FFFFFF';
-	}
-	if ($which=~/\.tabbg$/) {
-	    return '#CCCCCC';
-	}
-    }
     if (exists($env{'environment.color.'.$which})) {
-	return $env{'environment.color.'.$which};
+        return $env{'environment.color.'.$which};
     }
     $domain=&determinedomain($domain);
-    my %domdesign = &get_domainconf($domain);
+    my %domdesign;
+    unless ($domain eq 'public') {
+        %domdesign = &get_domainconf($domain);
+    }
     my $output;
     if ($domdesign{$domain.'.'.$which} ne '') {
-	$output = $domdesign{$domain.'.'.$which};
+        $output = $domdesign{$domain.'.'.$which};
     } else {
         $output = $defaultdesign{$which};
     }
     if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
         ($which =~ /login\.(img|logo|domlogo|login)/)) {
         if ($output =~ m{^/(adm|res)/}) {
-	    if ($output =~ m{^/res/}) {
-		my $local_name = &Apache::lonnet::filelocation('',$output);
-		&Apache::lonnet::repcopy($local_name);
-	    }
+            if ($output =~ m{^/res/}) {
+                my $local_name = &Apache::lonnet::filelocation('',$output);
+                &Apache::lonnet::repcopy($local_name);
+            }
             $output = &lonhttpdurl($output);
         }
     }
     return $output;
 }
 
+##############################################
+=pod
+
+=item * &authorspace()
+
+Inputs: $url (usually will be undef).
+
+Returns: Path to Construction Space containing the resource or 
+         directory being viewed (or for which action is being taken). 
+         If $url is provided, and begins /priv/<domain>/<uname>
+         the path will be that portion of the $context argument.
+         Otherwise the path will be for the author space of the current
+         user when the current role is author, or for that of the 
+         co-author/assistant co-author space when the current role 
+         is co-author or assistant co-author.
+
+=cut
+
+sub authorspace {
+    my ($url) = @_;
+    if ($url ne '') {
+        if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
+           return $1;
+        }
+    }
+    my $caname = '';
+    my $cadom = '';
+    if ($env{'request.role'} =~ /^(?:ca|aa)/) {
+        ($cadom,$caname) =
+            ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
+    } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
+        $caname = $env{'user.name'};
+        $cadom = $env{'user.domain'};
+    }
+    if (($caname ne '') && ($cadom ne '')) {
+        return "/priv/$cadom/$caname/";
+    }
+    return;
+}
+
+##############################################
+=pod
+
+=item * &head_subbox()
+
+Inputs: $content (contains HTML code with page functions, etc.)
+
+Returns: HTML div with $content
+         To be included in page header
+
+=cut
+
+sub head_subbox {
+    my ($content)=@_;
+    my $output =
+        '<div class="LC_head_subbox">'
+       .$content
+       .'</div>'
+}
+
+##############################################
+=pod
+
+=item * &CSTR_pageheader()
+
+Input: (optional) filename from which breadcrumb trail is built.
+       In most cases no input as needed, as $env{'request.filename'}
+       is appropriate for use in building the breadcrumb trail.
+
+Returns: HTML div with CSTR path and recent box
+         To be included on Construction Space pages
+
+=cut
+
+sub CSTR_pageheader {
+    my ($trailfile) = @_;
+    if ($trailfile eq '') {
+        $trailfile = $env{'request.filename'};
+    }
+
+# this is for resources; directories have customtitle, and crumbs
+# and select recent are created in lonpubdir.pm
+
+    my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
+    my ($udom,$uname,$thisdisfn)=
+        ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
+    my $formaction = "/priv/$udom/$uname/$thisdisfn";
+    $formaction =~ s{/+}{/}g;
+
+    my $parentpath = '';
+    my $lastitem = '';
+    if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
+        $parentpath = $1;
+        $lastitem = $2;
+    } else {
+        $lastitem = $thisdisfn;
+    }
+
+    my $output =
+         '<div>'
+        .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
+        .'<b>'.&mt('Construction Space:').'</b> '
+        .'<form name="dirs" method="post" action="'.$formaction
+        .'" target="_top">' #FIXME lonpubdir: target="_parent"
+        .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef);
+
+    if ($lastitem) {
+        $output .=
+             '<span class="LC_filename">'
+            .$lastitem
+            .'</span>';
+    }
+    $output .=
+         '<br />'
+        #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"
+        .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
+        .'</form>'
+        .&Apache::lonmenu::constspaceform()
+        .'</div>';
+
+    return $output;
+}
+
 ###############################################
 ###############################################
 
@@ -3857,26 +5080,21 @@ Inputs:
 =item * $forcereg, if page should register as content page (relevant for 
             text interface only)
 
-=item * $customtitle, alternate text to use instead of $title
-                      in the title box that appears, this text
-                      is not auto translated like the $title is
-
-=item * $notopbar, if true, keep the 'what is this' info but remove the
-                   navigational links
+=item * $no_nav_bar, if true, keep the 'what is this' info but remove the
+                     navigational links
 
 =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
 
-=item * $notitle, if true keep the nav controls, but remove the title bar
-
-=item * $no_inline_link, if true and in remote mode, don't show the 
-         'Switch To Inline Menu' link
-
 =item * $args, optional argument valid values are
             no_auto_mt_title -> prevents &mt()ing the title arg
             inherit_jsmath -> when creating popup window in a page,
                               should it have jsmath forced on by the
                               current page
 
+=item * $advtoolsref, optional argument, ref to an array containing
+            inlineremote items to be added in "Functions" menu below
+            breadcrumbs.
+
 =back
 
 Returns: A uniform header for LON-CAPA web pages.  
@@ -3887,9 +5105,14 @@ other decorations will be returned.
 =cut
 
 sub bodytag {
-    my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,$customtitle,
-	$notopbar,$bgcolor,$notitle,$no_inline_link,$args)=@_;
+    my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
+        $no_nav_bar,$bgcolor,$args,$advtoolsref)=@_;
 
+    my $public;
+    if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
+        || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
+        $public = 1;
+    }
     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
 
     $function = &get_users_function() if (!$function);
@@ -3897,7 +5120,7 @@ sub bodytag {
     my $font =   &designparm($function.'.font',$domain);
     my $pgbg   = $bgcolor || &designparm($function.'.pgbg',$domain);
 
-    my %design = ( 'style'   => 'margin-top: 0px',
+    my %design = ( 'style'   => 'margin-top: 0',
 		   'bgcolor' => $pgbg,
 		   'text'    => $font,
                    'alink'   => &designparm($function.'.alink',$domain),
@@ -3916,14 +5139,15 @@ sub bodytag {
         if ($env{'request.role'} !~ /^cr/) {
             $role = &Apache::lonnet::plaintext($role,&course_type());
         }
+        if ($env{'request.course.sec'}) {
+            $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$env{'request.course.sec'};
+        }   
 	$realm = $env{'course.'.$env{'request.course.id'}.'.description'};
     } else {
         $role = &Apache::lonnet::plaintext($role);
     }
 
     if (!$realm) { $realm='&nbsp;'; }
-# Set messages
-    my $messages=&domainlogo($domain);
 
     my $extra_body_attr = &make_attr_string($forcereg,\%design);
 
@@ -3933,42 +5157,17 @@ sub bodytag {
 
     if ($bodyonly) {
         return $bodytag;
-    } elsif ($env{'browser.interface'} eq 'textual') {
-# Accessibility
-          
-	$bodytag.=&Apache::lonmenu::menubuttons($forcereg,$forcereg);
-	if (!$notitle) {
-	    $bodytag.='<h1>LON-CAPA: '.$title.'</h1>';
-	}
-	return $bodytag;
-    }
+    } 
 
     my $name = &plainname($env{'user.name'},$env{'user.domain'});
-    if ($env{'user.name'} eq 'public' && $env{'user.domain'} eq 'public') {
+    if ($public) {
 	undef($role);
     } else {
-	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'});
+	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'},
+                                undef,'LC_menubuttons_link');
     }
     
-    my $roleinfo=(<<ENDROLE);
-<td class="LC_title_bar_who">
-<div class="LC_title_bar_name">
-    $name
-    &nbsp;
-</div>
-<div class="LC_title_bar_role">
-$role&nbsp;
-</div>
-<div class="LC_title_bar_realm">
-$realm&nbsp;
-</div>
-</td>
-ENDROLE
-
-    my $titleinfo = '<span class="LC_title_bar_title">'.$title.'</span>';
-    if ($customtitle) {
-        $titleinfo = $customtitle;
-    }
+    my $titleinfo = '<h1>'.$title.'</h1>';
     #
     # Extra info if you are the DC
     my $dc_info = '';
@@ -3976,93 +5175,82 @@ ENDROLE
                         $env{'course.'.$env{'request.course.id'}.
                                  '.domain'}.'/'})) {
         my $cid = $env{'request.course.id'};
-        $dc_info.= $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
+        $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
         $dc_info =~ s/\s+$//;
-        $dc_info = '('.$dc_info.')';
     }
 
-    if ($env{'environment.remote'} eq 'off') {
-        # No Remote
-	if ($env{'request.state'} eq 'construct') {
-	    $forcereg=1;
-	}
-
-	if (!$customtitle && $env{'request.state'} eq 'construct') {
-	    # this is for resources; directories have customtitle, and crumbs
-            # and select recent are created in lonpubdir.pm  
-	    my ($uname,$thisdisfn)=
-		($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
-	    my $formaction='/priv/'.$uname.'/'.$thisdisfn;
-	    $formaction=~s/\/+/\//g;
-
-	    my $parentpath = '';
-	    my $lastitem = '';
-	    if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
-		$parentpath = $1;
-		$lastitem = $2;
-	    } else {
-		$lastitem = $thisdisfn;
-	    }
-	    $titleinfo = 
-		&Apache::loncommon::help_open_menu('','',3,'Authoring').
-		'<b>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()')
-		.'</form>'
-		.&Apache::lonmenu::constspaceform();
-        }
-
-        my $titletable;
-	if (!$notitle) {
-	    $titletable =
-		'<table id="LC_title_bar">'.
-                         "<tr><td> $titleinfo $dc_info</td>".$roleinfo.
-			 '</tr></table>';
-	}
-	if ($notopbar) {
-	    $bodytag .= $titletable;
-	} else {
-	    if ($env{'request.state'} eq 'construct') {
-                $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg,
-							  $titletable);
+    $role = '<span class="LC_nobreak">('.$role.')</span>' if $role;
+    &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
+
+        if ($no_nav_bar || $env{'form.inhibitmenu'} eq 'yes') { 
+            return $bodytag; 
+        } 
+
+        if ($env{'request.state'} eq 'construct') { $forcereg=1; }
+
+        #    if ($env{'request.state'} eq 'construct') {
+        #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
+        #    }
+
+
+
+        if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
+             if ($dc_info) {
+                 $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
+             }
+             $bodytag .= qq|<div id="LC_nav_bar">$name $role<br />
+                <em>$realm</em> $dc_info</div>|;
+            return $bodytag;
+        }
+
+        unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
+            $bodytag .= qq|<div id="LC_nav_bar">$name $role</div>|;
+        }
+
+        $bodytag .= Apache::lonhtmlcommon::scripttag(
+            Apache::lonmenu::utilityfunctions(), 'start');
+
+        $bodytag .= Apache::lonmenu::primary_menu();
+
+        if ($dc_info) {
+            $dc_info = &dc_courseid_toggle($dc_info);
+        }
+        $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
+
+        #don't show menus for public users
+        if (!$public){
+            $bodytag .= Apache::lonmenu::secondary_menu();
+            $bodytag .= Apache::lonmenu::serverform();
+            $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
+            if ($env{'request.state'} eq 'construct') {
+                $bodytag .= &Apache::lonmenu::innerregister($forcereg,
+                                $args->{'bread_crumbs'});
+            } elsif ($forcereg) {
+                $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
+                                                            $args->{'group'});
             } else {
-                $bodytag .= &Apache::lonmenu::menubuttons($forcereg,$forcereg).
-		    $titletable;
-            }
+                $bodytag .= 
+                    &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
+                                                        $forcereg,$args->{'group'},
+                                                        $args->{'bread_crumbs'},
+                                                        $advtoolsref);
+            }
+        }else{
+            # this is to seperate menu from content when there's no secondary
+            # menu. Especially needed for public accessible ressources.
+            $bodytag .= '<hr style="clear:both" />';
+            $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
         }
-        return $bodytag;
-    }
 
-#
-# Top frame rendering, Remote is up
-#
+        return $bodytag;
+}
 
-    my $imgsrc = $img;
-    if ($img =~ /^\/adm/) {
-        $imgsrc = &lonhttpdurl($img);
-    }
-    my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';
-
-    # Explicit link to get inline menu
-    my $menu= ($no_inline_link?''
-	       :'<br /><a href="/adm/remote?action=collapse">'.&mt('Switch to Inline Menu Mode').'</a>');
-    #
-    if ($notitle) {
-	return $bodytag;
-    }
-    return(<<ENDBODY);
-$bodytag
-<table id="LC_title_bar" class="LC_with_remote">
-<tr><td class="LC_title_bar_role_logo">$upperleft</td>
-    <td class="LC_title_bar_domain_logo">$messages&nbsp;</td>
-</tr>
-<tr><td>$titleinfo $dc_info $menu</td>
-$roleinfo
-</tr>
-</table>
-ENDBODY
+sub dc_courseid_toggle {
+    my ($dc_info) = @_;
+    return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
+           '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
+           &mt('(More ...)').'</a></span>'.
+           '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
 }
 
 sub make_attr_string {
@@ -4086,31 +5274,8 @@ sub make_attr_string {
 		delete($attr_ref->{$key});
 	    }
 	}
-	$attr_ref->{'onload'}  =
-	    &Apache::lonmenu::loadevents().  $on_load;
-	$attr_ref->{'onunload'}=
-	    &Apache::lonmenu::unloadevents().$on_unload;
-    }
-
-# Accessibility font enhance
-    if ($env{'browser.fontenhance'} eq 'on') {
-	my $style;
-	foreach my $key (keys(%{$attr_ref})) {
-	    if (lc($key) eq 'style') {
-		$style.=$attr_ref->{$key}.';';
-		delete($attr_ref->{$key});
-	    }
-	}
-	$attr_ref->{'style'}=$style.'; font-size: x-large;';
-    }
-
-    if ($env{'browser.blackwhite'} eq 'on') {
-	delete($attr_ref->{'font'});
-	delete($attr_ref->{'link'});
-	delete($attr_ref->{'alink'});
-	delete($attr_ref->{'vlink'});
-	delete($attr_ref->{'bgcolor'});
-	delete($attr_ref->{'background'});
+	$attr_ref->{'onload'}  = $on_load;
+	$attr_ref->{'onunload'}= $on_unload;
     }
 
     my $attr_string;
@@ -4140,7 +5305,10 @@ i.e., $env{'internal.head.redirect'} exi
 
 sub endbodytag {
     my ($args) = @_;
-    my $endbodytag='</body>';
+    my $endbodytag;
+    unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
+        $endbodytag='</body>';
+    }
     $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
     if ( exists( $env{'internal.head.redirect'} ) ) {
         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
@@ -4173,6 +5341,8 @@ sub standard_css {
     my $img    = &designparm($function.'.img',   $domain);
     my $tabbg  = &designparm($function.'.tabbg', $domain);
     my $font   = &designparm($function.'.font',  $domain);
+    my $fontmenu = &designparm($function.'.fontmenu', $domain);
+#second colour for later usage
     my $sidebg = &designparm($function.'.sidebg',$domain);
     my $pgbg_or_bgcolor =
 	         $bgcolor ||
@@ -4184,9 +5354,9 @@ sub standard_css {
 
     my $sans                 = 'Verdana,Arial,Helvetica,sans-serif';
     my $mono                 = 'monospace';
-    my $data_table_head      = $tabbg;
-    my $data_table_light     = '#EEEEEE';
-    my $data_table_dark      = '#DDDDDD';
+    my $data_table_head      = $sidebg;
+    my $data_table_light     = '#FAFAFA';
+    my $data_table_dark      = '#E0E0E0';
     my $data_table_darker    = '#CCCCCC';
     my $data_table_highlight = '#FFFF00';
     my $mail_new             = '#FFBB77';
@@ -4199,35 +5369,91 @@ sub standard_css {
     my $mail_other_hover     = '#669999';
     my $table_header         = '#DDDDDD';
     my $feedback_link_bg     = '#BBBBBB';
+    my $lg_border_color      = '#C8C8C8';
+    my $button_hover         = '#BF2317';
 
     my $border = ($env{'browser.type'} eq 'explorer' ||
-		  $env{'browser.type'} eq 'safari'     ) ? '0px 2px 0px 2px'
-	                                                 : '0px 3px 0px 4px';
+      $env{'browser.type'} eq 'safari'     ) ? '0 2px 0 2px'
+                                             : '0 3px 0 4px';
 
 
     return <<END;
-h1, h2, h3, th { font-family: $sans }
-a:focus { color: red; background: yellow } 
-table.thinborder,
 
-table.thinborder tr th {
-  border-style: solid;
-  border-width: 1px;
-  background: $tabbg;
+/* needed for iframe to allow 100% height in FF */
+body, html { 
+    margin: 0;
+    padding: 0 0.5%;
+    height: 99%; /* to avoid scrollbars */
 }
-table.thinborder tr td {
-  border-style: solid;
-  border-width: 1px
+
+body {
+  font-family: $sans;
+  line-height:130%;
+  font-size:0.83em;
+  color:$font;
+}
+
+a:focus,
+a:focus img {
+  color: red;
+}
+
+form, .inline {
+  display: inline;
+}
+
+.LC_right {
+  text-align:right;
+}
+
+.LC_middle {
+  vertical-align:middle;
+}
+
+.LC_400Box {
+  width:400px;
+}
+
+.LC_iframecontainer {
+    width: 98%;
+    margin: 0;
+    position: fixed;
+    top: 8.5em;
+    bottom: 0;
+}
+
+.LC_iframecontainer iframe{
+    border: none;
+    width: 100%;
+    height: 100%;
+}
+
+.LC_filename {
+  font-family: $mono;
+  white-space:pre;
+  font-size: 120%;
+}
+
+.LC_fileicon {
+  border: none;
+  height: 1.3em;
+  vertical-align: text-bottom;
+  margin-right: 0.3em;
+  text-decoration:none;
+}
+
+.LC_setting {
+  text-decoration:underline;
 }
 
-form, .inline { display: inline; }
-.center { text-align: center; }
-.LC_filename {font-family: $mono; white-space:pre;}
 .LC_error {
   color: red;
-  font-size: larger;
 }
-.LC_warning,
+
+.LC_warning {
+  color: darkorange;
+}
+
 .LC_diff_removed {
   color: red;
 }
@@ -4237,25 +5463,65 @@ form, .inline { display: inline; }
 .LC_diff_added {
   color: green;
 }
-.LC_unknown {
-  color: yellow;
+
+div.LC_confirm_box {
+  background-color: #FAFAFA;
+  border: 1px solid $lg_border_color;
+  margin-right: 0;
+  padding: 5px;
 }
 
-.LC_icon {
-  border: 0px;
+div.LC_confirm_box .LC_error img,
+div.LC_confirm_box .LC_success img {
+  vertical-align: middle;
 }
-.LC_indexer_icon {
-  border: 0px;
-  height: 22px;
+
+.LC_icon {
+  border: none;
+  vertical-align: middle;
 }
+
 .LC_docs_spacer {
   width: 25px;
   height: 1px;
-  border: 0px;
+  border: none;
 }
 
 .LC_internal_info {
-  color: #999;
+  color: #999999;
+}
+
+.LC_discussion {
+  background: $data_table_dark;
+  border: 1px solid black;
+  margin: 2px;
+}
+
+.LC_disc_action_left {
+  background: $sidebg;
+  text-align: left;
+  padding: 4px;
+  margin: 2px;
+}
+
+.LC_disc_action_right {
+  background: $sidebg;
+  text-align: right;
+  padding: 4px;
+  margin: 2px;
+}
+
+.LC_disc_new_item {
+  background: white;
+  border: 2px solid red;
+  margin: 4px;
+  padding: 4px;
+}
+
+.LC_disc_old_item {
+  background: white;
+  margin: 4px;
+  padding: 4px;
 }
 
 table.LC_pastsubmission {
@@ -4263,125 +5529,123 @@ table.LC_pastsubmission {
   margin: 2px;
 }
 
-table#LC_top_nav, table#LC_menubuttons,table#LC_nav_location {
+table#LC_menubuttons {
   width: 100%;
   background: $pgbg;
   border: 2px;
   border-collapse: separate;
-  padding: 0px;
+  padding: 0;
+}
+
+table#LC_title_bar a {
+  color: $fontmenu;
+}
+
+table#LC_title_bar {
+  clear: both;
+  display: none;
 }
 
-table#LC_title_bar, table.LC_breadcrumbs, 
+table#LC_title_bar,
+table.LC_breadcrumbs, /* obsolete? */
 table#LC_title_bar.LC_with_remote {
   width: 100%;
   border-color: $pgbg;
   border-style: solid;
   border-width: $border;
-
   background: $pgbg;
-  font-family: $sans;
+  color: $fontmenu;
   border-collapse: collapse;
-  padding: 0px;
+  padding: 0;
+  margin: 0;
 }
 
-table.LC_docs_path {
-  width: 100%;
-  border: 0;
-  background: $pgbg;
-  font-family: $sans;
-  border-collapse: collapse;
-  padding: 0px;
+ul.LC_breadcrumb_tools_outerlist {
+    margin: 0;
+    padding: 0;
+    position: relative;
+    list-style: none;
 }
-
-table#LC_title_bar td {
-  background: $tabbg;
+ul.LC_breadcrumb_tools_outerlist li {
+    display: inline;
 }
-table#LC_title_bar td.LC_title_bar_who {
-  background: $tabbg;
-  color: $font;
-  font: small $sans;
-  text-align: right;
-}
-span.LC_metadata {
-    font-family: $sans;
+
+.LC_breadcrumb_tools_navigation {
+    padding: 0;
+    margin: 0;
+    float: left;
 }
-span.LC_title_bar_title {
-  font: bold x-large $sans;
+.LC_breadcrumb_tools_tools {
+    padding: 0;
+    margin: 0;
+    float: right;
 }
-table#LC_title_bar td.LC_title_bar_domain_logo {
-  background: $sidebg;
-  text-align: right;
-  padding: 0px;
+
+table#LC_title_bar td {
+  background: $tabbg;
 }
-table#LC_title_bar td.LC_title_bar_role_logo {
-  background: $sidebg;
-  padding: 0px;
+
+table#LC_menubuttons img {
+  border: none;
 }
 
-table#LC_menubuttons_mainmenu {
-  width: 100%;
-  border: 0px;
-  border-spacing: 1px;
-  padding: 0px 1px;
-  margin: 0px;
-  border-collapse: separate;
+.LC_breadcrumbs_component {
+  float: right;
+  margin: 0 1em;
 }
-table#LC_menubuttons img, table#LC_menubuttons_mainmenu img {
-  border: 0px;
+.LC_breadcrumbs_component img {
+  vertical-align: middle;
 }
-table#LC_top_nav td {
-  background: $tabbg;
-  border: 0px;
-  font-size: small;
+
+td.LC_table_cell_checkbox {
+  text-align: center;
 }
-table#LC_top_nav td a, div#LC_top_nav a {
-  color: $font;
-  font-family: $sans;
+
+.LC_fontsize_small {
+  font-size: 70%;
 }
-table#LC_top_nav td.LC_top_nav_logo {
-  background: $tabbg;
+
+#LC_breadcrumbs {
+  clear:both;
+  background: $sidebg;
+  border-bottom: 1px solid $lg_border_color;
+  line-height: 2.5em;
+  overflow: hidden;
+  margin: 0;
+  padding: 0;
   text-align: left;
-  white-space: nowrap;
-  width: 31px;
 }
-table#LC_top_nav td.LC_top_nav_logo img {
-  border: 0px;
-  vertical-align: bottom;
-}
-table#LC_top_nav td.LC_top_nav_exit,
-table#LC_top_nav td.LC_top_nav_help {
-  width: 2.0em;
+
+.LC_head_subbox, .LC_actionbox {
+  clear:both;
+  background: #F8F8F8; /* $sidebg; */
+  border: 1px solid $sidebg;
+  margin: 0 0 10px 0;
+  padding: 3px;
+  text-align: left;
 }
-table#LC_top_nav td.LC_top_nav_login {
-  width: 4.0em;
-  text-align: center;
+
+.LC_fontsize_medium {
+  font-size: 85%;
 }
-table.LC_breadcrumbs td, table.LC_docs_path td  {
-  background: $tabbg;
-  color: $font;
-  font-family: $sans;
-  font-size: smaller;
+
+.LC_fontsize_large {
+  font-size: 120%;
 }
-table.LC_breadcrumbs td.LC_breadcrumbs_component,
-table.LC_docs_path td.LC_docs_path_component {
-  background: $tabbg;
+
+.LC_menubuttons_inline_text {
   color: $font;
-  font-family: $sans;
-  font-size: larger;
-  text-align: right;
-}
-td.LC_table_cell_checkbox {
-  text-align: center;
+  font-size: 90%;
+  padding-left:3px;
 }
 
-table#LC_mainmenu td.LC_mainmenu_column {
-    vertical-align: top;
+.LC_menubuttons_inline_text img{
+  vertical-align: middle;
 }
 
-.LC_menubuttons_inline_text {
-  color: $font;
-  font-family: $sans;
-  font-size: smaller;
+li.LC_menubuttons_inline_text img {
+  cursor:pointer;
+  text-decoration: none;
 }
 
 .LC_menubuttons_link {
@@ -4391,130 +5655,128 @@ table#LC_mainmenu td.LC_mainmenu_column
 .LC_menubuttons_category {
   color: $font;
   background: $pgbg;
-  font-family: $sans;
   font-size: larger;
   font-weight: bold;
 }
 
 td.LC_menubuttons_text {
-  width: 90%;
   color: $font;
-  font-family: $sans;
-}
-
-td.LC_menubuttons_img {
 }
 
 .LC_current_location {
-  font-family: $sans;
-  background: $tabbg;
-}
-.LC_new_mail {
-  font-family: $sans;
   background: $tabbg;
-  font-weight: bold;
-}
-
-.LC_rolesmenu_is {
-  font-family: $sans;
-}
-
-.LC_rolesmenu_selected {
-  font-family: $sans;
-}
-
-.LC_rolesmenu_future {
-  font-family: $sans;
-}
-
-
-.LC_rolesmenu_will {
-  font-family: $sans;
-}
-
-.LC_rolesmenu_will_not {
-  font-family: $sans;
-}
-
-.LC_rolesmenu_expired {
-  font-family: $sans;
-}
-
-.LC_rolesinfo {
-  font-family: $sans;
-}
-
-.LC_dropadd_labeltext {
-  font-family: $sans;
-  text-align: right;
 }
 
-.LC_preferences_labeltext {
-  font-family: $sans;
-  text-align: right;
-}
-
-table.LC_aboutme_port {
-  border: 0px;
-  border-collapse: collapse;
-  border-spacing: 0px;
-}
-table.LC_data_table, table.LC_mail_list {
+table.LC_data_table {
   border: 1px solid #000000;
   border-collapse: separate;
   border-spacing: 1px;
   background: $pgbg;
 }
+
 .LC_data_table_dense {
   font-size: small;
 }
+
 table.LC_nested_outer {
   border: 1px solid #000000;
   border-collapse: collapse;
-  border-spacing: 0px;
+  border-spacing: 0;
   width: 100%;
 }
+
+table.LC_innerpickbox,
 table.LC_nested {
-  border: 0px;
+  border: none;
   border-collapse: collapse;
-  border-spacing: 0px;
+  border-spacing: 0;
   width: 100%;
 }
-table.LC_data_table tr th, table.LC_calendar tr th, table.LC_mail_list tr th,
-table.LC_prior_tries tr th {
+
+table.LC_data_table tr th,
+table.LC_calendar tr th,
+table.LC_prior_tries tr th,
+table.LC_innerpickbox tr th {
   font-weight: bold;
   background-color: $data_table_head;
-  font-size: smaller;
+  color:$fontmenu;
+  font-size:90%;
 }
-table.LC_data_table tr.LC_odd_row > td, 
-table.LC_aboutme_port tr td {
+
+table.LC_innerpickbox tr th,
+table.LC_innerpickbox tr td {
+  vertical-align: top;
+}
+
+table.LC_data_table tr.LC_info_row > td {
+  background-color: #CCCCCC;
+  font-weight: bold;
+  text-align: left;
+}
+
+table.LC_data_table tr.LC_odd_row > td {
   background-color: $data_table_light;
   padding: 2px;
+  vertical-align: top;
 }
-table.LC_data_table tr.LC_even_row > td,
-table.LC_aboutme_port tr.LC_even_row td {
+
+table.LC_pick_box tr > td.LC_odd_row {
+  background-color: $data_table_light;
+  vertical-align: top;
+}
+
+table.LC_data_table tr.LC_even_row > td {
+  background-color: $data_table_dark;
+  padding: 2px;
+  vertical-align: top;
+}
+
+table.LC_pick_box tr > td.LC_even_row {
   background-color: $data_table_dark;
+  vertical-align: top;
 }
+
 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;
   font-weight: bold;
   font-style: italic;
   text-align: center;
   padding: 8px;
 }
+
+table.LC_data_table tr.LC_empty_row td,
+table.LC_data_table tr.LC_footer_row td {
+  background-color: $sidebg;
+}
+
+table.LC_nested tr.LC_empty_row td {
+  background-color: #FFFFFF;
+}
+
+table.LC_caption {
+}
+
 table.LC_nested tr.LC_empty_row td {
   padding: 4ex
 }
+
 table.LC_nested_outer tr th {
   font-weight: bold;
+  color:$fontmenu;
   background-color: $data_table_head;
-  font-size: smaller;
+  font-size: small;
   border-bottom: 1px solid #000000;
 }
+
 table.LC_nested_outer tr td.LC_subheader {
   background-color: $data_table_head;
   font-weight: bold;
@@ -4522,20 +5784,24 @@ table.LC_nested_outer tr td.LC_subheader
   border-bottom: 1px solid #000000;
   text-align: right;
 }
+
 table.LC_nested tr.LC_info_row td {
-  background-color: #CCC;
+  background-color: #CCCCCC;
   font-weight: bold;
   font-size: small;
   text-align: center;
 }
+
 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 {
-  background-color: #FFF;
+  background-color: #FFFFFF;
   font-size: small;
 }
+
 table.LC_nested_outer tr th.LC_right_item,
 table.LC_nested tr.LC_info_row td.LC_right_item,
 table.LC_nested tr.LC_odd_row td.LC_right_item,
@@ -4544,18 +5810,18 @@ table.LC_nested tr td.LC_right_item {
 }
 
 table.LC_nested tr.LC_odd_row td {
-  background-color: #EEE;
+  background-color: #EEEEEE;
 }
 
 table.LC_createuser {
 }
 
 table.LC_createuser tr.LC_section_row td {
-  font-size: smaller;
+  font-size: small;
 }
 
 table.LC_createuser tr.LC_info_row td  {
-  background-color: #CCC;
+  background-color: #CCCCCC;
   font-weight: bold;
   text-align: center;
 }
@@ -4563,174 +5829,197 @@ table.LC_createuser tr.LC_info_row td  {
 table.LC_calendar {
   border: 1px solid #000000;
   border-collapse: collapse;
+  width: 98%;
 }
+
 table.LC_calendar_pickdate {
   font-size: xx-small;
 }
+
 table.LC_calendar tr td {
   border: 1px solid #000000;
   vertical-align: top;
+  width: 14%;
 }
+
 table.LC_calendar tr td.LC_calendar_day_empty {
   background-color: $data_table_dark;
 }
+
 table.LC_calendar tr td.LC_calendar_day_current {
   background-color: $data_table_highlight;
 }
 
-table.LC_mail_list tr.LC_mail_new {
+table.LC_data_table tr td.LC_mail_new {
   background-color: $mail_new;
 }
-table.LC_mail_list tr.LC_mail_new:hover {
+
+table.LC_data_table tr.LC_mail_new:hover {
   background-color: $mail_new_hover;
 }
-table.LC_mail_list tr.LC_mail_read {
+
+table.LC_data_table tr td.LC_mail_read {
   background-color: $mail_read;
 }
-table.LC_mail_list tr.LC_mail_read:hover {
+
+/*
+table.LC_data_table tr.LC_mail_read:hover {
   background-color: $mail_read_hover;
 }
-table.LC_mail_list tr.LC_mail_replied {
+*/
+
+table.LC_data_table tr td.LC_mail_replied {
   background-color: $mail_replied;
 }
-table.LC_mail_list tr.LC_mail_replied:hover {
+
+/*
+table.LC_data_table tr.LC_mail_replied:hover {
   background-color: $mail_replied_hover;
 }
-table.LC_mail_list tr.LC_mail_other {
+*/
+
+table.LC_data_table tr td.LC_mail_other {
   background-color: $mail_other;
 }
-table.LC_mail_list tr.LC_mail_other:hover {
+
+/*
+table.LC_data_table tr.LC_mail_other:hover {
   background-color: $mail_other_hover;
 }
-table.LC_mail_list tr.LC_mail_even {
-}
-table.LC_mail_list tr.LC_mail_odd {
-}
+*/
 
-
-table#LC_portfolio_actions {
-  width: auto;
-  background: $pgbg;
-  border: 0px;
-  border-spacing: 2px 2px;
-  padding: 0px;
-  margin: 0px;
-  border-collapse: separate;
-}
-table#LC_portfolio_actions td.LC_label {
-  background: $tabbg;
-  text-align: right;
-}
-table#LC_portfolio_actions td.LC_value {
-  background: $tabbg;
+table.LC_data_table tr > td.LC_browser_file,
+table.LC_data_table tr > td.LC_browser_file_published {
+  background: #AAEE77;
 }
 
-table#LC_cstr_controls {
-  width: 100%;
-  border-collapse: collapse;
-}
-table#LC_cstr_controls tr td {
-  border: 4px solid $pgbg;
-  padding: 4px;
-  text-align: center;
-  background: $tabbg;
+table.LC_data_table tr > td.LC_browser_file_locked,
+table.LC_data_table tr > td.LC_browser_file_unpublished {
+  background: #FFAA99;
 }
-table#LC_cstr_controls tr th {
-  border: 4px solid $pgbg;
-  background: $table_header;
-  text-align: center;
-  font-family: $sans;
-  font-size: smaller;
+
+table.LC_data_table tr > td.LC_browser_file_obsolete {
+  background: #888888;
 }
 
-table#LC_browser {
- 
+table.LC_data_table tr > td.LC_browser_file_modified,
+table.LC_data_table tr > td.LC_browser_file_metamodified {
+  background: #F8F866;
 }
-table#LC_browser tr th {
-  background: $table_header;
+
+table.LC_data_table tr.LC_browser_folder > td {
+  background: #E0E8FF;
 }
-table#LC_browser tr td {
-  padding: 2px;
+
+table.LC_data_table tr > td.LC_roles_is {
+  /* background: #77FF77; */
 }
-table#LC_browser tr.LC_browser_file,
-table#LC_browser tr.LC_browser_file_published {
-  background: #CCFF88;
+
+table.LC_data_table tr > td.LC_roles_future {
+  border-right: 8px solid #FFFF77;
 }
-table#LC_browser tr.LC_browser_file_locked,
-table#LC_browser tr.LC_browser_file_unpublished {
-  background: #FFAA99;
+
+table.LC_data_table tr > td.LC_roles_will {
+  border-right: 8px solid #FFAA77;
 }
-table#LC_browser tr.LC_browser_file_obsolete {
-  background: #AAAAAA;
+
+table.LC_data_table tr > td.LC_roles_expired {
+  border-right: 8px solid #FF7777;
 }
-table#LC_browser tr.LC_browser_file_modified,
-table#LC_browser tr.LC_browser_file_metamodified {
-  background: #FFFF77;
+
+table.LC_data_table tr > td.LC_roles_will_not {
+  border-right: 8px solid #AAFF77;
 }
-table#LC_browser tr.LC_browser_folder {
-  background: #CCCCFF;
+
+table.LC_data_table tr > td.LC_roles_selected {
+  border-right: 8px solid #11CC55;
 }
+
 span.LC_current_location {
-  font-size: x-large;
+  font-size:larger;
   background: $pgbg;
 }
 
+span.LC_current_nav_location {
+  font-weight:bold;
+  background: $sidebg;
+}
+
 span.LC_parm_menu_item {
   font-size: larger;
-  font-family: $sans;
 }
+
 span.LC_parm_scope_all {
   color: red;
 }
+
 span.LC_parm_scope_folder {
   color: green;
 }
+
 span.LC_parm_scope_resource {
   color: orange;
 }
+
 span.LC_parm_part {
   color: blue;
 }
-span.LC_parm_folder, span.LC_parm_symb {
+
+span.LC_parm_folder,
+span.LC_parm_symb {
   font-size: x-small;
   font-family: $mono;
   color: #AAAAAA;
 }
 
-td.LC_parm_overview_level_menu, td.LC_parm_overview_map_menu,
-td.LC_parm_overview_parm_selectors, td.LC_parm_overview_parm_restrictions {
+ul.LC_parm_parmlist li {
+  display: inline-block;
+  padding: 0.3em 0.8em;
+  vertical-align: top;
+  width: 150px;
+  border-top:1px solid $lg_border_color;
+}
+
+td.LC_parm_overview_level_menu,
+td.LC_parm_overview_map_menu,
+td.LC_parm_overview_parm_selectors,
+td.LC_parm_overview_restrictions  {
   border: 1px solid black;
   border-collapse: collapse;
 }
+
 table.LC_parm_overview_restrictions td {
   border-width: 1px 4px 1px 4px;
   border-style: solid;
   border-color: $pgbg;
   text-align: center;
 }
+
 table.LC_parm_overview_restrictions th {
   background: $tabbg;
   border-width: 1px 4px 1px 4px;
   border-style: solid;
   border-color: $pgbg;
 }
+
 table#LC_helpmenu {
-  border: 0px;
+  border: none;
   height: 55px;
-  border-spacing: 0px;
+  border-spacing: 0;
 }
 
 table#LC_helpmenu fieldset legend {
   font-size: larger;
-  font-weight: bold;
 }
+
 table#LC_helpmenu_links {
   width: 100%;
   border: 1px solid black;
   background: $pgbg;
-  padding: 0px;
+  padding: 0;
   border-spacing: 1px;
 }
+
 table#LC_helpmenu_links tr td {
   padding: 1px;
   background: $tabbg;
@@ -4738,11 +6027,13 @@ table#LC_helpmenu_links tr td {
   font-weight: bold;
 }
 
-table#LC_helpmenu_links a:link, table#LC_helpmenu_links a:visited,
+table#LC_helpmenu_links a:link,
+table#LC_helpmenu_links a:visited,
 table#LC_helpmenu_links a:active {
   text-decoration: none;
   color: $font;
 }
+
 table#LC_helpmenu_links a:hover {
   text-decoration: underline;
   color: $vlink;
@@ -4752,163 +6043,173 @@ table#LC_helpmenu_links a:hover {
   border: 1px solid #339933;
   margin: -1px;
 }
+
 .LC_chrt_popup_up {
   border: 1px solid yellow;
   margin: -1px;
 }
+
 .LC_chrt_popup {
   border: 1px solid #8888FF;
   background: #CCCCFF;
 }
+
 table.LC_pick_box {
   border-collapse: separate;
   background: white;
   border: 1px solid black;
   border-spacing: 1px;
 }
+
 table.LC_pick_box td.LC_pick_box_title {
-  background: $tabbg;
+  background: $sidebg;
   font-weight: bold;
-  text-align: right;
+  text-align: left;
+  vertical-align: top;
   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;
+  padding: 0;
   height: 1px;
   background: black;
 }
+
 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;
   border-spacing: 1px;
 }
+
 table.LC_group_priv_box td.LC_pick_box_title {
   background: $tabbg;
   font-weight: bold;
   text-align: right;
   width: 184px;
 }
+
 table.LC_group_priv_box td.LC_groups_fixed {
   background: $data_table_light;
   text-align: center;
 }
+
 table.LC_group_priv_box td.LC_groups_optional {
   background: $data_table_dark;
   text-align: center;
 }
+
 table.LC_group_priv_box td.LC_groups_functionality {
   background: $data_table_darker;
   text-align: center;
   font-weight: bold;
 }
+
 table.LC_group_priv td {
   text-align: left;
-  padding: 0px;
+  padding: 0;
 }
 
-table.LC_notify_front_page {
-  background: white;
-  border: 1px solid black;
-  padding: 8px;
-}
-table.LC_notify_front_page td {
-  padding: 8px;
-}
 .LC_navbuttons {
   margin: 2ex 0ex 2ex 0ex;
 }
+
 .LC_topic_bar {
-  font-family: $sans;
   font-weight: bold;
-  width: 100%;
   background: $tabbg;
-  vertical-align: middle;
-  margin: 2ex 0ex 2ex 0ex;
+  margin: 1em 0em 1em 2em;
+  padding: 3px;
+  font-size: 1.2em;
 }
+
 .LC_topic_bar span {
+  left: 0.5em;
+  position: absolute;
   vertical-align: middle;
+  font-size: 1.2em;
 }
-.LC_topic_bar img {
-  vertical-align: bottom;
-}
+
 table.LC_course_group_status {
   margin: 20px;
 }
+
 table.LC_status_selector td {
   vertical-align: top;
   text-align: center;
   padding: 4px;
 }
-table.LC_descriptive_input td.LC_description {
-  vertical-align: top;
-  text-align: right;
-  font-weight: bold;
-}
+
 div.LC_feedback_link {
   clear: both;
-  background: white;
-  width: 100%;  
+  background: $sidebg;
+  width: 100%;
+  padding-bottom: 10px;
+  border: 1px $tabbg solid;
+  height: 22px;
+  line-height: 22px;
+  padding-top: 5px;
+}
+
+div.LC_feedback_link img {
+  height: 22px;
+  vertical-align:middle;
+}
+
+div.LC_feedback_link a {
+  text-decoration: none;
+}
+
+div.LC_comblock {
+  display:inline;
+  color:$font;
+  font-size:90%;
+}
+
+div.LC_feedback_link div.LC_comblock {
+  padding-left:5px;
 }
+
+div.LC_feedback_link div.LC_comblock a {
+  color:$font;
+}
+
 span.LC_feedback_link {
-  background: $feedback_link_bg;
+  /* background: $feedback_link_bg; */
   font-size: larger;
 }
+
 span.LC_message_link {
-  background: $feedback_link_bg;
+  /* background: $feedback_link_bg; */
   font-size: larger;
   position: absolute;
   right: 1em;
@@ -4925,44 +6226,54 @@ table.LC_prior_tries td {
 }
 
 .LC_answer_correct {
-  background: #AAFFAA;
-  color: black;
+  background: lightgreen;
+  color: darkgreen;
+  padding: 6px;
 }
+
 .LC_answer_charged_try {
-  background: #FFAAAA ! important;
-  color: black;
+  background: #FFAAAA;
+  color: darkred;
+  padding: 6px;
 }
-.LC_answer_not_charged_try, 
+
+.LC_answer_not_charged_try,
 .LC_answer_no_grade,
 .LC_answer_late {
-  background: #FFFFAA;
+  background: lightyellow;
   color: black;
+  padding: 6px;
 }
+
 .LC_answer_previous {
-  background: #AAAAFF;
-  color: black;
+  background: lightblue;
+  color: darkblue;
+  padding: 6px;
 }
+
 .LC_answer_no_message {
   background: #FFFFFF;
   color: black;
+  padding: 6px;
 }
+
 .LC_answer_unknown {
   background: orange;
   color: black;
+  padding: 6px;
 }
 
-
 span.LC_prior_numerical,
 span.LC_prior_string,
 span.LC_prior_custom,
 span.LC_prior_reaction,
 span.LC_prior_math {
-  font-family: monospace;
+  font-family: $mono;
   white-space: pre;
 }
 
 span.LC_prior_string {
-  font-family: monospace;
+  font-family: $mono;
   white-space: pre;
 }
 
@@ -4970,16 +6281,19 @@ table.LC_prior_option {
   width: 100%;
   border-collapse: collapse;
 }
-table.LC_prior_rank, table.LC_prior_match {
+
+table.LC_prior_rank,
+table.LC_prior_match {
   border-collapse: collapse;
 }
+
 table.LC_prior_option tr td,
 table.LC_prior_rank tr td,
 table.LC_prior_match tr td {
   border: 1px solid #000000;
 }
 
-span.LC_nobreak {
+.LC_nobreak {
   white-space: nowrap;
 }
 
@@ -4992,47 +6306,35 @@ span.LC_cusr_subheading {
   font-size: 85%;
 }
 
-table.LC_docs_documents {
-  background: #BBBBBB;
-  border-width: 0px;
-  border-collapse: collapse;
-}
-
-table.LC_docs_documents td.LC_docs_document {
-  border: 2px solid black;
-  padding: 4px;
-}
-
-.LC_docs_course_commands div {
-  float: left;
-  border: 4px solid #AAAAAA;
-  padding: 4px;
-  background: #DDDDCC;
+div.LC_docs_entry_move {
+  border: 1px solid #BBBBBB;
+  background: #DDDDDD;
+  width: 22px;
+  padding: 1px;
+  margin: 0;
 }
 
-.LC_docs_entry_move {
-  border: 0px;
-  border-collapse: collapse;
+table.LC_data_table tr > td.LC_docs_entry_commands,
+table.LC_data_table tr > td.LC_docs_entry_parameter {
+  font-size: x-small;
 }
 
-.LC_docs_entry_move td {
-  border: 2px solid #BBBBBB;
-  background: #DDDDDD;
+.LC_docs_entry_parameter {
+  white-space: nowrap;
 }
 
-.LC_docs_editor td.LC_docs_entry_commands {
-  background: #DDDDDD;
-  font-size: x-small;
-}
 .LC_docs_copy {
   color: #000099;
 }
+
 .LC_docs_cut {
   color: #550044;
 }
+
 .LC_docs_rename {
   color: #009900;
 }
+
 .LC_docs_remove {
   color: #990000;
 }
@@ -5042,16 +6344,6 @@ table.LC_docs_documents td.LC_docs_docum
   font-size: x-small;
 }
 
-.LC_docs_editor td.LC_docs_entry_title,
-.LC_docs_editor td.LC_docs_entry_icon {
-  background: #FFFFBB;
-}
-.LC_docs_editor td.LC_docs_entry_parameter {
-  background: #BBBBFF;
-  font-size: x-small;
-  white-space: nowrap;
-}
-
 table.LC_docs_adddocs td,
 table.LC_docs_adddocs th {
   border: 1px solid #BBBBBB;
@@ -5062,12 +6354,13 @@ table.LC_docs_adddocs th {
 table.LC_sty_begin {
   background: #BBFFBB;
 }
+
 table.LC_sty_end {
   background: #FFBBBB;
 }
 
 table.LC_double_column {
-  border-width: 0px;
+  border-width: 0;
   border-collapse: collapse;
   width: 100%;
   padding: 2px;
@@ -5082,15 +6375,11 @@ table.LC_double_column tr td.LC_left_col
 
 table.LC_double_column tr td.LC_right_col {
   top: 2px;
-  right: 2px; 
+  right: 2px;
   width: 47%;
   vertical-align: top;
 }
 
-span.LC_role_level {
-  font-weight: bold;
-}
-
 div.LC_left_float {
   float: left;
   padding-right: 5%;
@@ -5106,79 +6395,59 @@ div.LC_clear_float_footer {
   clear: both;
 }
 
-
-div.LC_grade_select_mode {
-  font-family: $sans;
-}
-div.LC_grade_select_mode div div {
-  margin: 5px;
-}
-div.LC_grade_select_mode_selector {
-  margin: 5px;
-  float: left;
-}
-div.LC_grade_select_mode_selector_header {
-  font: bold medium $sans;
-}
-div.LC_grade_select_mode_type {
-  clear: left;
-}
-
 div.LC_grade_show_user {
-  margin-top: 20px;
-  border: 1px solid black;
+/*  border-left: 5px solid $sidebg; */
+  border-top: 5px solid #000000;
+  margin: 50px 0 0 0;
+  padding: 15px 0 5px 10px;
 }
-div.LC_grade_user_name {
-  background: #DDDDEE;
-  border-bottom: 1px solid black;
-  font: bold large $sans;
+
+div.LC_grade_show_user_odd_row {
+/*  border-left: 5px solid #000000; */
 }
-div.LC_grade_show_user_odd_row div.LC_grade_user_name {
-  background: #DDEEDD;
+
+div.LC_grade_show_user div.LC_Box {
+  margin-right: 50px;
 }
 
-div.LC_grade_show_problem,
 div.LC_grade_submissions,
 div.LC_grade_message_center,
-div.LC_grade_info_links,
-div.LC_grade_assign {
+div.LC_grade_info_links {
   margin: 5px;
   width: 99%;
   background: #FFFFFF;
 }
-div.LC_grade_show_problem_header,
+
 div.LC_grade_submissions_header,
-div.LC_grade_message_center_header,
-div.LC_grade_assign_header {
-  font: bold large $sans;
+div.LC_grade_message_center_header {
+  font-weight: bold;
+  font-size: large;
 }
-div.LC_grade_show_problem_problem,
+
 div.LC_grade_submissions_body,
-div.LC_grade_message_center_body,
-div.LC_grade_assign_body {
+div.LC_grade_message_center_body {
   border: 1px solid black;
   width: 99%;
   background: #FFFFFF;
 }
-span.LC_grade_check_note {
-  font: normal medium $sans;
-  display: inline;
-  position: absolute;
-  right: 1em;
-}
 
 table.LC_scantron_action {
   width: 100%;
 }
+
 table.LC_scantron_action tr th {
-  font: normal bold $sans;
+  font-weight:bold;
+  font-style:normal;
 }
 
-div.LC_edit_problem_header, 
+.LC_edit_problem_header,
 div.LC_edit_problem_footer {
-  font: normal medium $sans;
+  font-weight: normal;
+  font-size:  medium;
   margin: 2px;
+  background-color: $sidebg;
 }
+
 div.LC_edit_problem_header,
 div.LC_edit_problem_header div,
 div.LC_edit_problem_footer,
@@ -5187,43 +6456,759 @@ div.LC_edit_problem_editxml_header,
 div.LC_edit_problem_editxml_header div {
   margin-top: 5px;
 }
-div.LC_edit_problem_header_edit_row {
-  background: $tabbg;
-  padding: 3px;
-  margin-bottom: 5px;
-}
+
 div.LC_edit_problem_header_title {
-  font: larger bold $sans;
+  font-weight: bold;
+  font-size: larger;
   background: $tabbg;
   padding: 3px;
+  margin: 0 0 5px 0;
 }
+
 table.LC_edit_problem_header_title {
-  font: larger bold $sans;
   width: 100%;
-  border-color: $pgbg;
-  border-style: solid;
-  border-width: $border;
-
   background: $tabbg;
-  border-collapse: collapse;
-  padding: 0px
 }
 
 div.LC_edit_problem_discards {
   float: left;
   padding-bottom: 5px;
 }
+
 div.LC_edit_problem_saves {
   float: right;
   padding-bottom: 5px;
 }
-hr.LC_edit_problem_divide {
+
+img.stift {
+  border-width: 0;
+  vertical-align: middle;
+}
+
+table td.LC_mainmenu_col_fieldset {
+  vertical-align: top;
+}
+
+div.LC_createcourse {
+  margin: 10px 10px 10px 10px;
+}
+
+.LC_dccid {
+  margin: 0.2em 0 0 0;
+  padding: 0;
+  font-size: 90%;
+  display:none;
+}
+
+ol.LC_primary_menu a:hover,
+ol#LC_MenuBreadcrumbs a:hover,
+ol#LC_PathBreadcrumbs a:hover,
+ul#LC_secondary_menu a:hover,
+.LC_FormSectionClearButton input:hover
+ul.LC_TabContent   li:hover a {
+  color:$button_hover;
+  text-decoration:none;
+}
+
+h1 {
+  padding: 0;
+  line-height:130%;
+}
+
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin: 5px 0 5px 0;
+  padding: 0;
+  line-height:130%;
+}
+
+.LC_hcell {
+  padding:3px 15px 3px 15px;
+  margin: 0;
+  background-color:$tabbg;
+  color:$fontmenu;
+  border-bottom:solid 1px $lg_border_color;
+}
+
+.LC_Box > .LC_hcell {
+  margin: 0 -10px 10px -10px;
+}
+
+.LC_noBorder {
+  border: 0;
+}
+
+.LC_FormSectionClearButton input {
+  background-color:transparent;
+  border: none;
+  cursor:pointer;
+  text-decoration:underline;
+}
+
+.LC_help_open_topic {
+  color: #FFFFFF;
+  background-color: #EEEEFF;
+  margin: 1px;
+  padding: 4px;
+  border: 1px solid #000033;
+  white-space: nowrap;
+  /* vertical-align: middle; */
+}
+
+dl,
+ul,
+div,
+fieldset {
+  margin: 10px 10px 10px 0;
+  /* overflow: hidden; */
+}
+
+fieldset > legend {
+  font-weight: bold;
+  padding: 0 5px 0 5px;
+}
+
+#LC_nav_bar {
+  float: left;
+  background-color: $pgbg_or_bgcolor;
+  margin: 0 0 2px 0;
+}
+
+#LC_realm {
+  margin: 0.2em 0 0 0;
+  padding: 0;
+  font-weight: bold;
+  text-align: center;
+  background-color: $pgbg_or_bgcolor;
+}
+
+#LC_nav_bar em {
+  font-weight: bold;
+  font-style: normal;
+}
+
+ol.LC_primary_menu {
+  float: right;
+  margin: 0;
+  padding: 0;
+  background-color: $pgbg_or_bgcolor;
+}
+
+ol#LC_PathBreadcrumbs {
+  margin: 0;
+}
+
+ol.LC_primary_menu li {
+  color: RGB(80, 80, 80);
+  vertical-align: middle;
+  text-align: left;
+  list-style: none;
+  float: left;
+}
+
+ol.LC_primary_menu li a {
+  display: block;
+  margin: 0;
+  padding: 0 5px 0 10px;
+  text-decoration: none;
+}
+
+ol.LC_primary_menu li ul {
+  display: none;
+  width: 10em;
+  background-color: $data_table_light;
+}
+
+ol.LC_primary_menu li:hover ul, ol.LC_primary_menu li.hover ul {
+  display: block;
+  position: absolute;
+  margin: 0;
+  padding: 0;
+  z-index: 2;
+}
+
+ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
+  font-size: 90%;
+  vertical-align: top;
+  float: none;
+  border-left: 1px solid black;
+  border-right: 1px solid black;
+}
+
+ol.LC_primary_menu li:hover li a, ol.LC_primary_menu li.hover li a {
+  background-color:$data_table_light;
+}
+
+ol.LC_primary_menu li li a:hover {
+   color:$button_hover;
+   background-color:$data_table_dark;
+}
+
+ol.LC_primary_menu li img {
+  vertical-align: bottom;
+  height: 1.1em;
+  margin: 0.2em 0 0 0;
+}
+
+ol.LC_primary_menu a {
+  color: RGB(80, 80, 80);
+  text-decoration: none;
+}
+
+ol.LC_primary_menu a.LC_new_message {
+  font-weight:bold;
+  color: darkred;
+}
+
+ol.LC_docs_parameters {
+  margin-left: 0;
+  padding: 0;
+  list-style: none;
+}
+
+ol.LC_docs_parameters li {
+  margin: 0;
+  padding-right: 20px;
+  display: inline;
+}
+
+ol.LC_docs_parameters li:before {
+  content: "\\002022 \\0020";
+}
+
+li.LC_docs_parameters_title {
+  font-weight: bold;
+}
+
+ol.LC_docs_parameters li.LC_docs_parameters_title:before {
+  content: "";
+}
+
+ul#LC_secondary_menu {
+  clear: right;
+  color: $fontmenu;
+  background: $tabbg;
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  width: 100%;
+  text-align: left;
+  float: left;
+}
+
+ul#LC_secondary_menu li {
+  font-weight: bold;
+  line-height: 1.8em;
+  border-right: 1px solid black;
+  float: left;
+}
+
+ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
+  background-color: $data_table_light;
+}
+
+ul#LC_secondary_menu li a {
+  padding: 0 0.8em;
+}
+
+ul#LC_secondary_menu li ul {
+  display: none;
+}
+
+ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
+  display: block;
+  position: absolute;
+  margin: 0;
+  padding: 0;
+  list-style:none;
+  float: none;
+  background-color: $data_table_light;
+  z-index: 2;
+  margin-left: -1px;
+}
+
+ul#LC_secondary_menu li ul li {
+  font-size: 90%;
+  vertical-align: top;
+  border-left: 1px solid black;
+  border-right: 1px solid black;
+  background-color: $data_table_light;
+  list-style:none;
+  float: none;
+}
+
+ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
+  background-color: $data_table_dark;
+}
+
+ul.LC_TabContent {
+  display:block;
+  background: $sidebg;
+  border-bottom: solid 1px $lg_border_color;
+  list-style:none;
+  margin: -1px -10px 0 -10px;
+  padding: 0;
+}
+
+ul.LC_TabContent li,
+ul.LC_TabContentBigger li {
+  float:left;
+}
+
+ul#LC_secondary_menu li a {
+  color: $fontmenu;
+  text-decoration: none;
+}
+
+ul.LC_TabContent {
+  min-height:20px;
+}
+
+ul.LC_TabContent li {
+  vertical-align:middle;
+  padding: 0 16px 0 10px;
+  background-color:$tabbg;
+  border-bottom:solid 1px $lg_border_color;
+  border-left: solid 1px $font;
+}
+
+ul.LC_TabContent .right {
+  float:right;
+}
+
+ul.LC_TabContent li a,
+ul.LC_TabContent li {
+  color:rgb(47,47,47);
+  text-decoration:none;
+  font-size:95%;
+  font-weight:bold;
+  min-height:20px;
+}
+
+ul.LC_TabContent li a:hover,
+ul.LC_TabContent li a:focus {
+  color: $button_hover;
+  background:none;
+  outline:none;
+}
+
+ul.LC_TabContent li:hover {
+  color: $button_hover;
+  cursor:pointer;
+}
+
+ul.LC_TabContent li.active {
+  color: $font;
+  background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
+  border-bottom:solid 1px #FFFFFF;
+  cursor: default;
+}
+
+ul.LC_TabContent li.active a {
+  color:$font;
+  background:#FFFFFF;
+  outline: none;
+}
+
+ul.LC_TabContent li.goback {
+  float: left;
+  border-left: none;
+}
+
+#maincoursedoc {
+  clear:both;
+}
+
+ul.LC_TabContentBigger {
+  display:block;
+  list-style:none;
+  padding: 0;
+}
+
+ul.LC_TabContentBigger li {
+  vertical-align:bottom;
+  height: 30px;
+  font-size:110%;
+  font-weight:bold;
+  color: #737373;
+}
+
+ul.LC_TabContentBigger li.active {
+  position: relative;
+  top: 1px;
+}
+
+ul.LC_TabContentBigger li a {
+  background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
+  height: 30px;
+  line-height: 30px;
+  text-align: center;
+  display: block;
+  text-decoration: none;
+  outline: none;  
+}
+
+ul.LC_TabContentBigger li.active a {
+  background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
+  color:$font;
+}
+
+ul.LC_TabContentBigger li b {
+  background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
+  display: block;
+  float: left;
+  padding: 0 30px;
+  border-bottom: 1px solid $lg_border_color;
+}
+
+ul.LC_TabContentBigger li:hover b {
+  color:$button_hover;
+}
+
+ul.LC_TabContentBigger li.active b {
+  background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
+  color:$font;
+  border: 0;
+}
+
+
+ul.LC_CourseBreadcrumbs {
+  background: $sidebg;
+  height: 2em;
+  padding-left: 10px;
+  margin: 0;
+  list-style-position: inside;
+}
+
+ol#LC_MenuBreadcrumbs,
+ol#LC_PathBreadcrumbs {
+  padding-left: 10px;
+  margin: 0;
+  height: 2.5em;  /* equal to #LC_breadcrumbs line-height */
+}
+
+ol#LC_MenuBreadcrumbs li,
+ol#LC_PathBreadcrumbs li,
+ul.LC_CourseBreadcrumbs li {
+  display: inline;
+  white-space: normal;  
+}
+
+ol#LC_MenuBreadcrumbs li a,
+ul.LC_CourseBreadcrumbs li a {
+  text-decoration: none;
+  font-size:90%;
+}
+
+ol#LC_MenuBreadcrumbs h1 {
+  display: inline;
+  font-size: 90%;
+  line-height: 2.5em;
+  margin: 0;
+  padding: 0;
+}
+
+ol#LC_PathBreadcrumbs li a {
+  text-decoration:none;
+  font-size:100%;
+  font-weight:bold;
+}
+
+.LC_Box {
+  border: solid 1px $lg_border_color;
+  padding: 0 10px 10px 10px;
+}
+
+.LC_DocsBox {
+  border: solid 1px $lg_border_color;
+  padding: 0 0 10px 10px;
+}
+
+.LC_AboutMe_Image {
+  float:left;
+  margin-right:10px;
+}
+
+.LC_Clear_AboutMe_Image {
+  clear:left;
+}
+
+dl.LC_ListStyleClean dt {
+  padding-right: 5px;
+  display: table-header-group;
+}
+
+dl.LC_ListStyleClean dd {
+  display: table-row;
+}
+
+.LC_ListStyleClean,
+.LC_ListStyleSimple,
+.LC_ListStyleNormal,
+.LC_ListStyleSpecial {
+  /* display:block; */
+  list-style-position: inside;
+  list-style-type: none;
+  overflow: hidden;
+  padding: 0;
+}
+
+.LC_ListStyleSimple li,
+.LC_ListStyleSimple dd,
+.LC_ListStyleNormal li,
+.LC_ListStyleNormal dd,
+.LC_ListStyleSpecial li,
+.LC_ListStyleSpecial dd {
+  margin: 0;
+  padding: 5px 5px 5px 10px;
   clear: both;
-  color: $tabbg;
-  background-color: $tabbg;
-  height: 3px;
-  border: 0px;
 }
+
+.LC_ListStyleClean li,
+.LC_ListStyleClean dd {
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+.LC_ListStyleSimple dd,
+.LC_ListStyleSimple li {
+  border-bottom: solid 1px $lg_border_color;
+}
+
+.LC_ListStyleSpecial li,
+.LC_ListStyleSpecial dd {
+  list-style-type: none;
+  background-color: RGB(220, 220, 220);
+  margin-bottom: 4px;
+}
+
+table.LC_SimpleTable {
+  margin:5px;
+  border:solid 1px $lg_border_color;
+}
+
+table.LC_SimpleTable tr {
+  padding: 0;
+  border:solid 1px $lg_border_color;
+}
+
+table.LC_SimpleTable thead {
+  background:rgb(220,220,220);
+}
+
+div.LC_columnSection {
+  display: block;
+  clear: both;
+  overflow: hidden;
+  margin: 0;
+}
+
+div.LC_columnSection>* {
+  float: left;
+  margin: 10px 20px 10px 0;
+  overflow:hidden;
+}
+
+table em {
+  font-weight: bold;
+  font-style: normal;
+}
+
+table.LC_tableBrowseRes,
+table.LC_tableOfContent {
+  border:none;
+  border-spacing: 1px;
+  padding: 3px;
+  background-color: #FFFFFF;
+  font-size: 90%;
+}
+
+table.LC_tableOfContent {
+  border-collapse: collapse;
+}
+
+table.LC_tableBrowseRes a,
+table.LC_tableOfContent a {
+  background-color: transparent;
+  text-decoration: none;
+}
+
+table.LC_tableOfContent img {
+  border: none;
+  height: 1.3em;
+  vertical-align: text-bottom;
+  margin-right: 0.3em;
+}
+
+a#LC_content_toolbar_firsthomework {
+  background-image:url(/res/adm/pages/open-first-problem.gif);
+}
+
+a#LC_content_toolbar_everything {
+  background-image:url(/res/adm/pages/show-all.gif);
+}
+
+a#LC_content_toolbar_uncompleted {
+  background-image:url(/res/adm/pages/show-incomplete-problems.gif);
+}
+
+#LC_content_toolbar_clearbubbles {
+  background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
+}
+
+a#LC_content_toolbar_changefolder {
+  background : url(/res/adm/pages/close-all-folders.gif) top center ;
+}
+
+a#LC_content_toolbar_changefolder_toggled {
+  background-image:url(/res/adm/pages/open-all-folders.gif);
+}
+
+a#LC_content_toolbar_edittoplevel {
+  background-image:url(/res/adm/pages/edittoplevel.gif);
+}
+
+ul#LC_toolbar li a:hover {
+  background-position: bottom center;
+}
+
+ul#LC_toolbar {
+  padding: 0;
+  margin: 2px;
+  list-style:none;
+  position:relative;
+  background-color:white;
+  overflow: auto;
+}
+
+ul#LC_toolbar li {
+  border:1px solid white;
+  padding: 0;
+  margin: 0;
+  float: left;
+  display:inline;
+  vertical-align:middle;
+  white-space: nowrap;
+}
+
+
+a.LC_toolbarItem {
+  display:block;
+  padding: 0;
+  margin: 0;
+  height: 32px;
+  width: 32px;
+  color:white;
+  border: none;
+  background-repeat:no-repeat;
+  background-color:transparent;
+}
+
+ul.LC_funclist {
+    margin: 0;
+    padding: 0.5em 1em 0.5em 0;
+}
+
+ul.LC_funclist > li:first-child {
+    font-weight:bold; 
+    margin-left:0.8em;
+}
+
+ul.LC_funclist + ul.LC_funclist {
+    /* 
+       left border as a seperator if we have more than
+       one list 
+    */
+    border-left: 1px solid $sidebg;
+    /* 
+       this hides the left border behind the border of the 
+       outer box if element is wrapped to the next 'line' 
+    */
+    margin-left: -1px;
+}
+
+ul.LC_funclist li {
+  display: inline;
+  white-space: nowrap;
+  margin: 0 0 0 25px;
+  line-height: 150%;
+}
+
+.LC_hidden {
+  display: none;
+}
+
+.LCmodal-overlay {
+		position:fixed;
+		top:0;
+		right:0;
+		bottom:0;
+		left:0;
+		height:100%;
+		width:100%;
+		margin:0;
+		padding:0;
+		background:#999;
+		opacity:.75;
+		filter: alpha(opacity=75);
+		-moz-opacity: 0.75;
+		z-index:101;
+}
+
+* html .LCmodal-overlay {   
+		position: absolute;
+		height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+}
+
+.LCmodal-window {
+		position:fixed;
+		top:50%;
+		left:50%;
+		margin:0;
+		padding:0;
+		z-index:102;
+	}
+
+* html .LCmodal-window {
+		position:absolute;
+}
+
+.LCclose-window {
+		position:absolute;
+		width:32px;
+		height:32px;
+		right:8px;
+		top:8px;
+		background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
+		text-indent:-99999px;
+		overflow:hidden;
+		cursor:pointer;
+}
+
+/*
+  styles used by TTH when "Default set of options to pass to tth/m
+  when converting TeX" in course settings has been set
+
+  option passed: -t
+
+*/
+
+td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
+td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
+td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
+td div.norm {line-height:normal;}
+
+/*
+  option passed -y3
+*/
+
+span.roman {font-family: serif; font-style: normal; font-weight: normal;}
+span.overacc2 {position: relative;  left: .8em; top: -1.2ex;}
+span.overacc1 {position: relative;  left: .6em; top: -1.2ex;}
+
 END
 }
 
@@ -5272,18 +7257,36 @@ sub headtag {
 	'<head>'.
 	&font_settings();
 
+    my $inhibitprint = &print_suppression();
+
     if (!$args->{'frameset'}) {
 	$result .= &Apache::lonhtmlcommon::htmlareaheaders();
     }
-    if ($args->{'force_register'}) {
-	$result .= &Apache::lonmenu::registerurl(1);
+    if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
+        $result .= Apache::lonxml::display_title();
     }
     if (!$args->{'no_nav_bar'} 
 	&& !$args->{'only_body'}
 	&& !$args->{'frameset'}) {
 	$result .= &help_menu_js();
+        $result.=&modal_window();
+        $result.=&togglebox_script();
+        $result.=&wishlist_window();
+        $result.=&LCprogressbarUpdate_script();
+    } else {
+        if ($args->{'add_modal'}) {
+           $result.=&modal_window();
+        }
+        if ($args->{'add_wishlist'}) {
+           $result.=&wishlist_window();
+        }
+        if ($args->{'add_togglebox'}) {
+           $result.=&togglebox_script();
+        }
+        if ($args->{'add_progressbar'}) {
+           $result.=&LCprogressbarUpdate_script();
+        }
     }
-
     if (ref($args->{'redirect'})) {
 	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
 	$url = &Apache::lonenc::check_encrypt($url);
@@ -5301,8 +7304,9 @@ ADDMETA
     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
     $result .= '<title> LON-CAPA '.$title.'</title>'
 	.'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
+        .$inhibitprint
 	.$head_extra;
-    return $result;
+    return $result.'</head>';
 }
 
 =pod
@@ -5317,10 +7321,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" />';
     }
@@ -5329,6 +7330,82 @@ sub font_settings {
 
 =pod
 
+=item * &print_suppression()
+
+In course context returns css which causes the body to be blank when media="print",
+if printout generation is unavailable for the current resource.
+
+This could be because:
+
+(a) printstartdate is in the future
+
+(b) printenddate is in the past
+
+(c) there is an active exam block with "printout"
+functionality blocked
+
+Users with pav, pfo or evb privileges are exempt.
+
+Inputs: none
+
+=cut
+
+
+sub print_suppression {
+    my $noprint;
+    if ($env{'request.course.id'}) {
+        my $scope = $env{'request.course.id'};
+        if ((&Apache::lonnet::allowed('pav',$scope)) ||
+            (&Apache::lonnet::allowed('pfo',$scope))) {
+            return;
+        }
+        if ($env{'request.course.sec'} ne '') {
+            $scope .= "/$env{'request.course.sec'}";
+            if ((&Apache::lonnet::allowed('pav',$scope)) ||
+                (&Apache::lonnet::allowed('pfo',$scope))) {
+                return;
+            }
+        }
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $blocked = &blocking_status('printout',$cnum,$cdom);
+        if ($blocked) {
+            my $checkrole = "cm./$cdom/$cnum";
+            if ($env{'request.course.sec'} ne '') {
+                $checkrole .= "/$env{'request.course.sec'}";
+            }
+            unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
+                    ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
+                $noprint = 1;
+            }
+        }
+        unless ($noprint) {
+            my $symb = &Apache::lonnet::symbread();
+            if ($symb ne '') {
+                my $navmap = Apache::lonnavmaps::navmap->new();
+                if (ref($navmap)) {
+                    my $res = $navmap->getBySymb($symb);
+                    if (ref($res)) {
+                        if (!$res->resprintable()) {
+                            $noprint = 1;
+                        }
+                    }
+                }
+            }
+        }
+        if ($noprint) {
+            return <<"ENDSTYLE";
+<style type="text/css" media="print">
+    body { display:none }
+</style>
+ENDSTYLE
+        }
+    }
+    return;
+}
+
+=pod
+
 =item * &xml_begin()
 
 Returns the needed doctype and <html>
@@ -5340,10 +7417,6 @@ Inputs: none
 sub xml_begin {
     my $output='';
 
-    if ($env{'internal.start_page'}==1) {
-	&Apache::lonhtmlcommon::init_htmlareafields();
-    }
-
     if ($env{'browser.mathml'}) {
 	$output='<?xml version="1.0"?>'
             #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
@@ -5354,105 +7427,75 @@ sub xml_begin {
             .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" ' 
 	    .'xmlns="http://www.w3.org/1999/xhtml">';
     } else {
-	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>';
+	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
+           .'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
     }
     return $output;
 }
 
 =pod
 
-=item * &endheadtag()
-
-Returns a uniform </head> for LON-CAPA web pages.
-
-Inputs: none
-
-=cut
-
-sub endheadtag {
-    return '</head>';
-}
-
-=pod
-
-=item * &head()
+=item * &start_page()
 
-Returns a uniform complete <head>..</head> section for LON-CAPA web pages.
+Returns a complete <html> .. <body> section for LON-CAPA web pages.
 
-Inputs: $title - optional title for the page
-        $head_extra - optional extra HTML to put inside the <head>
+Inputs:
 
-=cut
+=over 4
 
-sub head {
-    my ($title,$head_extra,$args) = @_;
-    return &headtag($title,$head_extra,$args).&endheadtag();
-}
+$title - optional title for the page
 
-=pod
+$head_extra - optional extra HTML to incude inside the <head>
 
-=item * &start_page()
+$args - additional optional args supported are:
 
-Returns a complete <html> .. <body> section for LON-CAPA web pages.
+=over 8
 
-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
+             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() no_nav_bar 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
-                                    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 
+             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 
-                                    '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
+             bread_crumbs ->             Array containing breadcrumbs
+             bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs
+             group          -> includes the current group, if page is for a 
+                               specific group  
+
+=back
+
+=back
 
 =cut
 
 sub start_page {
     my ($title,$head_extra,$args) = @_;
     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
-    my %head_args;
-    foreach my $arg ('redirect','force_register','domain','function',
-		     'bgcolor','frameset','no_nav_bar','only_body',
-		     'no_auto_mt_title') {
-	if (defined($args->{$arg})) {
-	    $head_args{$arg} = $args->{$arg};
-	}
-    }
 
     $env{'internal.start_page'}++;
-    my $result;
+    my ($result,@advtools);
+
     if (! exists($args->{'skip_phases'}{'head'}) ) {
-	$result.=
-	    &xml_begin().
-	    &headtag($title,$head_extra,\%head_args).&endheadtag();
+        $result .= &xml_begin() . &headtag($title, $head_extra, $args);
     }
     
     if (! exists($args->{'skip_phases'}{'body'}) ) {
@@ -5460,48 +7503,56 @@ sub start_page {
 	    my $attr_string = &make_attr_string($args->{'force_register'},
 						$args->{'add_entries'});
 	    $result .= "\n<frameset $attr_string>\n";
-	} else {
-	    $result .=
-		&bodytag($title, 
-			 $args->{'function'},       $args->{'add_entries'},
-			 $args->{'only_body'},      $args->{'domain'},
-			 $args->{'force_register'}, $args->{'body_title'},
-			 $args->{'no_nav_bar'},     $args->{'bgcolor'},
-			 $args->{'no_title'},       $args->{'no_inline_link'},
-			 $args);
-	}
+        } else {
+            $result .=
+                &bodytag($title, 
+                         $args->{'function'},       $args->{'add_entries'},
+                         $args->{'only_body'},      $args->{'domain'},
+                         $args->{'force_register'}, $args->{'no_nav_bar'},
+                         $args->{'bgcolor'},        $args,
+                         \@advtools);
+        }
     }
 
     if ($args->{'js_ready'}) {
-	$result = &js_ready($result);
+		$result = &js_ready($result);
     }
     if ($args->{'html_encode'}) {
-	$result = &html_encode($result);
+		$result = &html_encode($result);
     }
-    return $result;
-}
 
+    # Preparation for new and consistent functionlist at top of screen
+    # if ($args->{'functionlist'}) {
+    #            $result .= &build_functionlist();
+    #}
+
+    # Don't add anything more if only_body wanted or in const space
+    return $result if    $args->{'only_body'} 
+                      || $env{'request.state'} eq 'construct';
+
+    #Breadcrumbs
+    if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
+		&Apache::lonhtmlcommon::clear_breadcrumbs();
+		#if any br links exists, add them to the breadcrumbs
+		if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {         
+			foreach my $crumb (@{$args->{'bread_crumbs'}}){
+				&Apache::lonhtmlcommon::add_breadcrumb($crumb);
+			}
+		}
+                # if @advtools array contains items add then to the breadcrumbs
+                if (@advtools > 0) {
+                    &Apache::lonmenu::advtools_crumbs(@advtools);
+                }
 
-=pod
-
-=item * &head()
-
-Returns a complete </body></html> section for LON-CAPA web pages.
-
-Inputs:         $args - additional optional args supported are:
-                 js_ready     -> return a string ready for being used in 
-                                 a javascript writeln
-                 html_encode  -> return a string ready for being used in 
-                                 a html attribute
-                 frameset     -> if true will start with a <frameset>
-                                 rather than <body>
-                 dicsussion   -> if true will get discussion from
-                                  lonxml::xmlend
-                                 (you can pass the target and parser arguments
-                                  through optional 'target' and 'parser' args
-                                  to this routine)
-
-=cut
+		#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
+		if(exists($args->{'bread_crumbs_component'})){
+			$result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
+		}else{
+			$result .= &Apache::lonhtmlcommon::breadcrumbs();
+		}
+    }
+    return $result;
+}
 
 sub end_page {
     my ($args) = @_;
@@ -5515,13 +7566,14 @@ sub end_page {
 	}
 	$result .= &Apache::lonxml::xmlend($target,$parser);
     }
-
     if ($args->{'frameset'}) {
 	$result .= '</frameset>';
     } else {
 	$result .= &endbodytag($args);
     }
-    $result .= "\n</html>";
+    unless ($args->{'notbody'}) {
+        $result .= "\n</html>";
+    }
 
     if ($args->{'js_ready'}) {
 	$result = &js_ready($result);
@@ -5534,6 +7586,287 @@ sub end_page {
     return $result;
 }
 
+sub wishlist_window {
+    return(<<'ENDWISHLIST');
+<script type="text/javascript">
+// <![CDATA[
+// <!-- BEGIN LON-CAPA Internal
+function set_wishlistlink(title, path) {
+    if (!title) {
+        title = document.title;
+        title = title.replace(/^LON-CAPA /,'');
+    }
+    if (!path) {
+        path = location.pathname;
+    }
+    Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
+                      'wishlistNewLink','width=560,height=350,scrollbars=0');
+}
+// END LON-CAPA Internal -->
+// ]]>
+</script>
+ENDWISHLIST
+}
+
+sub modal_window {
+    return(<<'ENDMODAL');
+<script type="text/javascript">
+// <![CDATA[
+// <!-- BEGIN LON-CAPA Internal
+var modalWindow = {
+	parent:"body",
+	windowId:null,
+	content:null,
+	width:null,
+	height:null,
+	close:function()
+	{
+	        $(".LCmodal-window").remove();
+	        $(".LCmodal-overlay").remove();
+	},
+	open:function()
+	{
+		var modal = "";
+		modal += "<div class=\"LCmodal-overlay\"></div>";
+		modal += "<div id=\"" + this.windowId + "\" class=\"LCmodal-window\" style=\"width:" + this.width + "px; height:" + this.height + "px; margin-top:-" + (this.height / 2) + "px; margin-left:-" + (this.width / 2) + "px;\">";
+		modal += this.content;
+		modal += "</div>";	
+
+		$(this.parent).append(modal);
+
+		$(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
+		$(".LCclose-window").click(function(){modalWindow.close();});
+		$(".LCmodal-overlay").click(function(){modalWindow.close();});
+	}
+};
+	var openMyModal = function(source,width,height,scrolling)
+	{
+		modalWindow.windowId = "myModal";
+		modalWindow.width = width;
+		modalWindow.height = height;
+		modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='true' src='" + source + "'>&lt/iframe>";
+		modalWindow.open();
+	};	
+// END LON-CAPA Internal -->
+// ]]>
+</script>
+ENDMODAL
+}
+
+sub modal_link {
+    my ($link,$linktext,$width,$height,$target,$scrolling,$title)=@_;
+    unless ($width) { $width=480; }
+    unless ($height) { $height=400; }
+    unless ($scrolling) { $scrolling='yes'; }
+    my $target_attr;
+    if (defined($target)) {
+        $target_attr = 'target="'.$target.'"';
+    }
+    return <<"ENDLINK";
+<a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling'); return false;">
+           $linktext</a>
+ENDLINK
+}
+
+sub modal_adhoc_script {
+    my ($funcname,$width,$height,$content)=@_;
+    return (<<ENDADHOC);
+<script type="text/javascript">
+// <![CDATA[
+        var $funcname = function()
+        {
+                modalWindow.windowId = "myModal";
+                modalWindow.width = $width;
+                modalWindow.height = $height;
+                modalWindow.content = '$content';
+                modalWindow.open();
+        };  
+// ]]>
+</script>
+ENDADHOC
+}
+
+sub modal_adhoc_inner {
+    my ($funcname,$width,$height,$content)=@_;
+    my $innerwidth=$width-20;
+    $content=&js_ready(
+               &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
+                 &start_scrollbox($width.'px',$innerwidth.'px',$height.'px').
+                    $content.
+                 &end_scrollbox().
+               &end_page()
+             );
+    return &modal_adhoc_script($funcname,$width,$height,$content);
+}
+
+sub modal_adhoc_window {
+    my ($funcname,$width,$height,$content,$linktext)=@_;
+    return &modal_adhoc_inner($funcname,$width,$height,$content).
+           "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
+}
+
+sub modal_adhoc_launch {
+    my ($funcname,$width,$height,$content)=@_;
+    return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
+<script type="text/javascript">
+// <![CDATA[
+$funcname();
+// ]]>
+</script>
+ENDLAUNCH
+}
+
+sub modal_adhoc_close {
+    return (<<ENDCLOSE);
+<script type="text/javascript">
+// <![CDATA[
+modalWindow.close();
+// ]]>
+</script>
+ENDCLOSE
+}
+
+sub togglebox_script {
+   return(<<ENDTOGGLE);
+<script type="text/javascript"> 
+// <![CDATA[
+function LCtoggleDisplay(id,hidetext,showtext) {
+   link = document.getElementById(id + "link").childNodes[0];
+   with (document.getElementById(id).style) {
+      if (display == "none" ) {
+          display = "inline";
+          link.nodeValue = hidetext;
+        } else {
+          display = "none";
+          link.nodeValue = showtext;
+       }
+   }
+}
+// ]]>
+</script>
+ENDTOGGLE
+}
+
+sub start_togglebox {
+    my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
+    unless ($heading) { $heading=''; } else { $heading.=' '; }
+    unless ($showtext) { $showtext=&mt('show'); }
+    unless ($hidetext) { $hidetext=&mt('hide'); }
+    unless ($headerbg) { $headerbg='#FFFFFF'; }
+    return &start_data_table().
+           &start_data_table_header_row().
+           '<td bgcolor="'.$headerbg.'">'.$heading.
+           '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
+           $showtext.'\')">'.$showtext.'</a>]</td>'.
+           &end_data_table_header_row().
+           '<tr id="'.$id.'" style="display:none""><td>';
+}
+
+sub end_togglebox {
+    return '</td></tr>'.&end_data_table();
+}
+
+sub LCprogressbar_script {
+   my ($id)=@_;
+   return(<<ENDPROGRESS);
+<script type="text/javascript">
+// <![CDATA[
+\$('#progressbar$id').progressbar({
+  value: 0,
+  change: function(event, ui) {
+    var newVal = \$(this).progressbar('option', 'value');
+    \$('.pblabel', this).text(LCprogressTxt);
+  }
+});
+// ]]>
+</script>
+ENDPROGRESS
+}
+
+sub LCprogressbarUpdate_script {
+   return(<<ENDPROGRESSUPDATE);
+<style type="text/css">
+.ui-progressbar { position:relative; }
+.pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
+</style>
+<script type="text/javascript">
+// <![CDATA[
+var LCprogressTxt='---';
+
+function LCupdateProgress(percent,progresstext,id) {
+   LCprogressTxt=progresstext;
+   \$('#progressbar'+id).progressbar('value',percent);
+}
+// ]]>
+</script>
+ENDPROGRESSUPDATE
+}
+
+my $LClastpercent;
+my $LCidcnt;
+my $LCcurrentid;
+
+sub LCprogressbar {
+    my ($r)=(@_);
+    $LClastpercent=0;
+    $LCidcnt++;
+    $LCcurrentid=$$.'_'.$LCidcnt;
+    my $starting=&mt('Starting');
+    my $content=(<<ENDPROGBAR);
+<p>
+  <div id="progressbar$LCcurrentid">
+    <span class="pblabel">$starting</span>
+  </div>
+</p>
+ENDPROGBAR
+    &r_print($r,$content.&LCprogressbar_script($LCcurrentid));
+}
+
+sub LCprogressbarUpdate {
+    my ($r,$val,$text)=@_;
+    unless ($val) { 
+       if ($LClastpercent) {
+           $val=$LClastpercent;
+       } else {
+           $val=0;
+       }
+    }
+    if ($val<0) { $val=0; }
+    if ($val>100) { $val=0; }
+    $LClastpercent=$val;
+    unless ($text) { $text=$val.'%'; }
+    $text=&js_ready($text);
+    &r_print($r,<<ENDUPDATE);
+<script type="text/javascript">
+// <![CDATA[
+LCupdateProgress($val,'$text','$LCcurrentid');
+// ]]>
+</script>
+ENDUPDATE
+}
+
+sub LCprogressbarClose {
+    my ($r)=@_;
+    $LClastpercent=0;
+    &r_print($r,<<ENDCLOSE);
+<script type="text/javascript">
+// <![CDATA[
+\$("#progressbar$LCcurrentid").hide('slow'); 
+// ]]>
+</script>
+ENDCLOSE
+}
+
+sub r_print {
+    my ($r,$to_print)=@_;
+    if ($r) {
+      $r->print($to_print);
+      $r->rflush();
+    } else {
+      print($to_print);
+    }
+}
+
 sub html_encode {
     my ($result) = @_;
 
@@ -5541,6 +7874,7 @@ sub html_encode {
     
     return $result;
 }
+
 sub js_ready {
     my ($result) = @_;
 
@@ -5577,11 +7911,34 @@ sub validate_page {
     }
 }
 
+
+sub start_scrollbox {
+    my ($outerwidth,$width,$height,$id,$bgcolor)=@_;
+    unless ($outerwidth) { $outerwidth='520px'; }
+    unless ($width) { $width='500px'; }
+    unless ($height) { $height='200px'; }
+    my ($table_id,$div_id,$tdcol);
+    if ($id ne '') {
+        $table_id = " id='table_$id'";
+        $div_id = " id='div_$id'";
+    }
+    if ($bgcolor ne '') {
+        $tdcol = "background-color: $bgcolor;";
+    }
+    return <<"END";
+<table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol"><div style="overflow:auto; width:$width; height: $height;"$div_id>
+END
+}
+
+sub end_scrollbox {
+    return '</div></td></tr></table>';
+}
+
 sub simple_error_page {
     my ($r,$title,$msg) = @_;
     my $page =
 	&Apache::loncommon::start_page($title).
-	&mt($msg).
+	'<p class="LC_error">'.&mt($msg).'</p>'.
 	&Apache::loncommon::end_page();
     if (ref($r)) {
 	$r->print($page);
@@ -5592,31 +7949,48 @@ sub simple_error_page {
 
 {
     my @row_count;
+
+    sub start_data_table_count {
+        unshift(@row_count, 0);
+        return;
+    }
+
+    sub end_data_table_count {
+        shift(@row_count);
+        return;
+    }
+
     sub start_data_table {
-	my ($add_class) = @_;
+	my ($add_class,$id) = @_;
 	my $css_class = (join(' ','LC_data_table',$add_class));
-	unshift(@row_count,0);
-	return '<table class="'.$css_class.'">'."\n";
+        my $table_id;
+        if (defined($id)) {
+            $table_id = ' id="'.$id.'"';
+        }
+	&start_data_table_count();
+	return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
     }
 
     sub end_data_table {
-	shift(@row_count);
+	&end_data_table_count();
 	return '</table>'."\n";;
     }
 
     sub start_data_table_row {
-	my ($add_class) = @_;
+	my ($add_class, $id) = @_;
 	$row_count[0]++;
 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
-	$css_class = (join(' ',$css_class,$add_class));
-	return  '<tr class="'.$css_class.'">'."\n";;
+	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
+        $id = (' id="'.$id.'"') unless ($id eq '');
+        return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
     }
     
     sub continue_data_table_row {
-	my ($add_class) = @_;
+	my ($add_class, $id) = @_;
 	my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
-	$css_class = (join(' ',$css_class,$add_class));
-	return  '<tr class="'.$css_class.'">'."\n";;
+	$css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
+        $id = (' id="'.$id.'"') unless ($id eq '');
+        return  '<tr class="'.$css_class.'"'.$id.'>'."\n";
     }
 
     sub end_data_table_row {
@@ -5624,7 +7998,7 @@ sub simple_error_page {
     }
 
     sub start_data_table_empty_row {
-	$row_count[0]++;
+#	$row_count[0]++;
 	return  '<tr class="LC_empty_row" >'."\n";;
     }
 
@@ -5639,6 +8013,11 @@ sub simple_error_page {
     sub end_data_table_header_row {
 	return '</tr>'."\n";;
     }
+
+    sub data_table_caption {
+        my $caption = shift;
+        return "<caption class=\"LC_caption\">$caption</caption>";
+    }
 }
 
 =pod
@@ -5705,15 +8084,18 @@ Returns either 'student','coordinator','
 
 ###############################################
 sub get_users_function {
-    my $function = 'student';
-    if ($env{'request.role'}=~/^(cc|in|ta|ep)/) {
+    my $function = 'norole';
+    if ($env{'request.role'}=~/^(st)/) {
+        $function='student';
+    }
+    if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
         $function='coordinator';
     }
     if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
         $function='admin';
     }
-    if (($env{'request.role'}=~/^(au|ca)/) ||
-        ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
+    if (($env{'request.role'}=~/^(au|ca|aa)/) ||
+        ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
         $function='author';
     }
     return $function;
@@ -5723,6 +8105,38 @@ sub get_users_function {
 
 =pod
 
+=item * &show_course()
+
+Used by lonmenu.pm and lonroles.pm to determine whether to use the word
+'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
+
+Inputs:
+None
+
+Outputs:
+Scalar: 1 if 'Course' to be used, 0 otherwise.
+
+=cut
+
+###############################################
+sub show_course {
+    my $course = !$env{'user.adv'};
+    if (!$env{'user.adv'}) {
+        foreach my $env (keys(%env)) {
+            next if ($env !~ m/^user\.priv\./);
+            if ($env !~ m/^user\.priv\.(?:st|cm)/) {
+                $course = 0;
+                last;
+            }
+        }
+    }
+    return $course;
+}
+
+###############################################
+
+=pod
+
 =item * &check_user_status()
 
 Determines current status of supplied role for a
@@ -5745,7 +8159,7 @@ sub check_user_status {
     my $active_chk = 'none';
     my $now = time;
     if (@uroles > 0) {
-        if (($role eq 'cc') || ($sec eq '') || (!defined($sec))) {
+        if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
             $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
         } else {
             $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
@@ -5817,7 +8231,19 @@ sub get_sections {
     my %sectioncount;
     my $now = time;
 
-    if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
+    my $check_students = 1;
+    my $only_students = 0;
+    if (ref($possible_roles) eq 'ARRAY') {
+        if (grep(/^st$/,@{$possible_roles})) {
+            if (@{$possible_roles} == 1) {
+                $only_students = 1;
+            }
+        } else {
+            $check_students = 0;
+        }
+    }
+
+    if ($check_students) { 
 	my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
 	my $sec_index = &Apache::loncoursedata::CL_SECTION();
 	my $status_index = &Apache::loncoursedata::CL_STATUS();
@@ -5844,6 +8270,9 @@ sub get_sections {
 	    }
 	}
     }
+    if ($only_students) {
+        return %sectioncount;
+    }
     my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
     foreach my $user (sort(keys(%courseroles))) {
 	if ($user !~ /^(\w{2})/) { next; }
@@ -5991,7 +8420,7 @@ sub get_course_users {
                               active   => 'Active',
                               future   => 'Future',
                             );
-        my %nothide;
+        my (%nothide,@possdoms);
         if ($hidepriv) {
             my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
             foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
@@ -6001,6 +8430,10 @@ sub get_course_users {
                     $nothide{$user} = 1;
                 }
             }
+            my @possdoms = ($cdom);
+            if ($coursehash{'checkforpriv'}) {
+                push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
+            }
         }
         foreach my $person (sort(keys(%coursepersonnel))) {
             my $match = 0;
@@ -6036,7 +8469,7 @@ sub get_course_users {
                 }
                 if ($uname ne '' && $udom ne '') {
                     if ($hidepriv) {
-                        if ((&Apache::lonnet::privileged($uname,$udom)) &&
+                        if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
                             (!$nothide{$uname.':'.$udom})) {
                             next;
                         }
@@ -6226,6 +8659,8 @@ If the user's status includes multiple t
 the largest default quota which applies to the user determines the
 default quota returned.
 
+=back
+
 =cut
 
 ###############################################
@@ -6238,21 +8673,37 @@ sub default_quota {
                                             ['quotas'],$udom);
     if (ref($quotahash{'quotas'}) eq 'HASH') {
         if ($inststatus ne '') {
-            my @statuses = split(/:/,$inststatus);
+            my @statuses = map { &unescape($_); } split(/:/,$inststatus);
             foreach my $item (@statuses) {
-                if ($quotahash{'quotas'}{$item} ne '') {
-                    if ($defquota eq '') {
-                        $defquota = $quotahash{'quotas'}{$item};
-                        $settingstatus = $item;
-                    } elsif ($quotahash{'quotas'}{$item} > $defquota) {
-                        $defquota = $quotahash{'quotas'}{$item};
-                        $settingstatus = $item;
+                if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
+                    if ($quotahash{'quotas'}{'defaultquota'}{$item} ne '') {
+                        if ($defquota eq '') {
+                            $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
+                            $settingstatus = $item;
+                        } elsif ($quotahash{'quotas'}{'defaultquota'}{$item} > $defquota) {
+                            $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
+                            $settingstatus = $item;
+                        }
+                    }
+                } else {
+                    if ($quotahash{'quotas'}{$item} ne '') {
+                        if ($defquota eq '') {
+                            $defquota = $quotahash{'quotas'}{$item};
+                            $settingstatus = $item;
+                        } elsif ($quotahash{'quotas'}{$item} > $defquota) {
+                            $defquota = $quotahash{'quotas'}{$item};
+                            $settingstatus = $item;
+                        }
                     }
                 }
             }
         }
         if ($defquota eq '') {
-            $defquota = $quotahash{'quotas'}{'default'};
+            if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
+                $defquota = $quotahash{'quotas'}{'defaultquota'}{'default'};
+            } else {
+                $defquota = $quotahash{'quotas'}{'default'};
+            }
             $settingstatus = 'default';
         }
     } else {
@@ -6304,7 +8755,7 @@ sub get_secgrprole_info {
 }
 
 sub user_picker {
-    my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype) = @_;
+    my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context) = @_;
     my $currdom = $dom;
     my %curr_selected = (
                         srchin => 'dom',
@@ -6358,6 +8809,7 @@ sub user_picker {
         #       loncreateuser::print_user_query_page()
         #       has been completed.
         next if ($option eq 'alc');
+        next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));  
         next if ($option eq 'crs' && !$env{'request.course.id'});
         if ($curr_selected{'srchin'} eq $option) {
             $srchinsel .= ' 
@@ -6394,19 +8846,29 @@ sub user_picker {
     $srchtypesel .= "\n  </select>\n";
 
     my ($newuserscript,$new_user_create);
-
+    my $context_dom = $env{'request.role.domain'};
+    if ($context eq 'requestcrs') {
+        if ($env{'form.coursedom'} ne '') { 
+            $context_dom = $env{'form.coursedom'};
+        }
+    }
     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'}) {
+            if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
                 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 $helplink = '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 />';
+                    $new_user_create = '<p class="LC_warning">'
+                                      .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
+                                      .' '
+                                      .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
+                                          ,'<a href="'.$helplink.'">','</a>')
+                                      .'</p><br />';
                 }
             }
         }
@@ -6431,7 +8893,7 @@ function setSearch(createnew,callingForm
             }
         }
         for (var i=0; i<callingForm.srchdomain.length; i++) {
-            if (callingForm.srchdomain.options[i].value == '$env{'request.role.domain'}') {
+            if (callingForm.srchdomain.options[i].value == '$context_dom') {
                 callingForm.srchdomain.selectedIndex = i;
             }
         }
@@ -6443,6 +8905,7 @@ ENDSCRIPT
 
     my $output = <<"END_BLOCK";
 <script type="text/javascript">
+// <![CDATA[
 function validateEntry(callingForm) {
 
     var checkok = 1;
@@ -6511,28 +8974,25 @@ function validateEntry(callingForm) {
 
 $newuserscript
 
+// ]]>
 </script>
 
 $new_user_create
 
-<table>
- <tr>
-  <td>$lt{'doma'}:</td>
-  <td>$domform</td>
-  </td>
- </tr>
- <tr>
-  <td>$lt{'usr'}:</td>
-  <td>$srchbysel
-      $srchtypesel 
-      <input type="text" size="15" name="srchterm" value="$srchterm" />
-      $srchinsel 
-  </td>
- </tr>
-</table>
-<br />
 END_BLOCK
 
+    $output .= &Apache::lonhtmlcommon::start_pick_box().
+               &Apache::lonhtmlcommon::row_title($lt{'doma'}).
+               $domform.
+               &Apache::lonhtmlcommon::row_closure().
+               &Apache::lonhtmlcommon::row_title($lt{'usr'}).
+               $srchbysel.
+               $srchtypesel. 
+               '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
+               $srchinsel.
+               &Apache::lonhtmlcommon::row_closure(1). 
+               &Apache::lonhtmlcommon::end_pick_box().
+               '<br />';
     return $output;
 }
 
@@ -6614,7 +9074,10 @@ sub user_rule_formats {
     my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
     if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
         if (@{$ruleorder} > 0) {
-            $output = '<br />'.&mt("$text{$check} with the following format(s) may <span class=\"LC_cusr_emph\">only</span> be used for verified users at [_1]:",$domdesc).' <ul>';
+            $output = '<br />'.
+                      &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
+                          '<span class="LC_cusr_emph">','</span>',$domdesc).
+                      ' <ul>';
             foreach my $rule (@{$ruleorder}) {
                 if (ref($curr_rules) eq 'ARRAY') {
                     if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
@@ -6658,12 +9121,16 @@ sub instrule_disallow_msg {
             $text{'action'} = 'IDs';
         }
     }
-    $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for <span class=\"LC_cusr_emph\">[_1]</span>, but the $text{'item'} $text{'do'} not exist in the institutional directory.",$domdesc).'<br />';
+    $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",'<span class="LC_cusr_emph">'.$domdesc.'</span>').'<br />';
     if ($mode eq 'upload') {
         if ($checkitem eq 'username') {
             $response .= &mt("You will need to modify your upload file so it will include $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
         } elsif ($checkitem eq 'id') {
-            $response .= &mt("Either upload a file which includes $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the ID/Student Number field.");
+            $response .= &mt("Either upload a file which includes $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the Student/Employee ID field.");
+        }
+    } elsif ($mode eq 'selfcreate') {
+        if ($checkitem eq 'id') {
+            $response .= &mt("You must either choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
         }
     } else {
         if ($checkitem eq 'username') {
@@ -6684,10 +9151,259 @@ sub personal_data_fieldtitles {
                         middlename => 'Middle Name',
                         generation => 'Generation',
                         gen => 'Generation',
+                        inststatus => 'Affiliation',
                    );
     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  = &mt('Any users');
+    }
+    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');
+    }
+    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;
+}
+
+sub get_standard_codeitems {
+    return ('Year','Semester','Department','Number','Section');
+}
+
+=pod
+
+=head1 Slot Helpers
+
+=over 4
+
+=item * sorted_slots()
+
+Sorts an array of slot names in order of an optional sort key,
+default sort is by slot start time (earliest first). 
+
+Inputs:
+
+=over 4
+
+slotsarr  - Reference to array of unsorted slot names.
+
+slots     - Reference to hash of hash, where outer hash keys are slot names.
+
+sortkey   - Name of key in inner hash to be sorted on (e.g., starttime).
+
+=back
+
+Returns:
+
+=over 4
+
+sorted   - An array of slot names sorted by a specified sort key 
+           (default sort key is start time of the slot).
+
+=back
+
+=cut
+
+
+sub sorted_slots {
+    my ($slotsarr,$slots,$sortkey) = @_;
+    if ($sortkey eq '') {
+        $sortkey = 'starttime';
+    }
+    my @sorted;
+    if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
+        @sorted =
+            sort {
+                     if (ref($slots->{$a}) && ref($slots->{$b})) {
+                         return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
+                     }
+                     if (ref($slots->{$a})) { return -1;}
+                     if (ref($slots->{$b})) { return 1;}
+                     return 0;
+                 } @{$slotsarr};
+    }
+    return @sorted;
+}
+
+=pod
+
+=item * get_future_slots()
+
+Inputs:
+
+=over 4
+
+cnum - course number
+
+cdom - course domain
+
+now - current UNIX time
+
+symb - optional symb
+
+=back
+
+Returns:
+
+=over 4
+
+sorted_reservable - ref to array of student_schedulable slots currently 
+                    reservable, ordered by end date of reservation period.
+
+reservable_now - ref to hash of student_schedulable slots currently
+                 reservable.
+
+    Keys in inner hash are:
+    (a) symb: either blank or symb to which slot use is restricted.
+    (b) endreserve: end date of reservation period. 
+
+sorted_future - ref to array of student_schedulable slots reservable in
+                the future, ordered by start date of reservation period.
+
+future_reservable - ref to hash of student_schedulable slots reservable
+                    in the future.
+
+    Keys in inner hash are:
+    (a) symb: either blank or symb to which slot use is restricted.
+    (b) startreserve:  start date of reservation period.
+
+=back
+
+=cut
+
+sub get_future_slots {
+    my ($cnum,$cdom,$now,$symb) = @_;
+    my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
+    my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
+    foreach my $slot (keys(%slots)) {
+        next unless($slots{$slot}->{'type'} eq 'schedulable_student');
+        if ($symb) {
+            next if (($slots{$slot}->{'symb'} ne '') && 
+                     ($slots{$slot}->{'symb'} ne $symb));
+        }
+        if (($slots{$slot}->{'starttime'} > $now) &&
+            ($slots{$slot}->{'endtime'} > $now)) {
+            if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
+                my $userallowed = 0;
+                if ($slots{$slot}->{'allowedsections'}) {
+                    my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
+                    if (!defined($env{'request.role.sec'})
+                        && grep(/^No section assigned$/,@allowed_sec)) {
+                        $userallowed=1;
+                    } else {
+                        if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
+                            $userallowed=1;
+                        }
+                    }
+                    unless ($userallowed) {
+                        if (defined($env{'request.course.groups'})) {
+                            my @groups = split(/:/,$env{'request.course.groups'});
+                            foreach my $group (@groups) {
+                                if (grep(/^\Q$group\E$/,@allowed_sec)) {
+                                    $userallowed=1;
+                                    last;
+                                }
+                            }
+                        }
+                    }
+                }
+                if ($slots{$slot}->{'allowedusers'}) {
+                    my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
+                    my $user = $env{'user.name'}.':'.$env{'user.domain'};
+                    if (grep(/^\Q$user\E$/,@allowed_users)) {
+                        $userallowed = 1;
+                    }
+                }
+                next unless($userallowed);
+            }
+            my $startreserve = $slots{$slot}->{'startreserve'};
+            my $endreserve = $slots{$slot}->{'endreserve'};
+            my $symb = $slots{$slot}->{'symb'};
+            if (($startreserve < $now) &&
+                (!$endreserve || $endreserve > $now)) {
+                my $lastres = $endreserve;
+                if (!$lastres) {
+                    $lastres = $slots{$slot}->{'starttime'};
+                }
+                $reservable_now{$slot} = {
+                                           symb       => $symb,
+                                           endreserve => $lastres
+                                         };
+            } elsif (($startreserve > $now) &&
+                     (!$endreserve || $endreserve > $startreserve)) {
+                $future_reservable{$slot} = {
+                                              symb         => $symb,
+                                              startreserve => $startreserve
+                                            };
+            }
+        }
+    }
+    my @unsorted_reservable = keys(%reservable_now);
+    if (@unsorted_reservable > 0) {
+        @sorted_reservable = 
+            &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
+    }
+    my @unsorted_future = keys(%future_reservable);
+    if (@unsorted_future > 0) {
+        @sorted_future =
+            &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
+    }
+    return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
+}
+
 =pod
 
 =back
@@ -6696,7 +9412,7 @@ sub personal_data_fieldtitles {
 
 =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),
@@ -6725,7 +9441,7 @@ sub get_unprocessed_cgi {
 
 =pod
 
-=item * cacheheader() 
+=item * &cacheheader() 
 
 returns cache-controlling header code
 
@@ -6742,7 +9458,7 @@ sub cacheheader {
 
 =pod
 
-=item * no_cache($r) 
+=item * &no_cache($r) 
 
 specifies header code to not have cache
 
@@ -6778,7 +9494,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
@@ -6805,7 +9521,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.
@@ -6828,6 +9544,2319 @@ sub get_env_multiple {
     return(@values);
 }
 
+sub ask_for_embedded_content {
+    my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
+    my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
+        %currsubfile,%unused,$rem);
+    my $counter = 0;
+    my $numnew = 0;
+    my $numremref = 0;
+    my $numinvalid = 0;
+    my $numpathchg = 0;
+    my $numexisting = 0;
+    my $numunused = 0;
+    my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
+        $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path);
+    my $heading = &mt('Upload embedded files');
+    my $buttontext = &mt('Upload');
+
+    my $navmap;
+    if ($env{'request.course.id'}) {
+        $navmap = Apache::lonnavmaps::navmap->new();
+    }
+    if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+        my $current_path='/';
+        if ($env{'form.currentpath'}) {
+            $current_path = $env{'form.currentpath'};
+        }
+        if ($actionurl eq '/adm/coursegrp_portfolio') {
+            $udom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            $uname = $env{'course.'.$env{'request.course.id'}.'.num'};
+            $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
+        } else {
+            $udom = $env{'user.domain'};
+            $uname = $env{'user.name'};
+            $url = '/userfiles/portfolio';
+        }
+        $toplevel = $url.'/';
+        $url .= $current_path;
+        $getpropath = 1;
+    } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
+             ($actionurl eq '/adm/imsimport')) { 
+        my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
+        $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
+        $toplevel = $url;
+        if ($rest ne '') {
+            $url .= $rest;
+        }
+    } elsif ($actionurl eq '/adm/coursedocs') {
+        if (ref($args) eq 'HASH') {
+            $url = $args->{'docs_url'};
+            $toplevel = $url;
+            if ($args->{'context'} eq 'paste') {
+                ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
+                ($path) = 
+                    ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
+                $fileloc = &Apache::lonnet::filelocation('',$toplevel);
+                $fileloc =~ s{^/}{};
+            }
+        }
+    } elsif ($actionurl eq '/adm/dependencies')  {
+        if ($env{'request.course.id'} ne '') {
+            $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            $cnum =  $env{'course.'.$env{'request.course.id'}.'.num'};
+            if (ref($args) eq 'HASH') {
+                $url = $args->{'docs_url'};
+                $title = $args->{'docs_title'};
+                $toplevel = "/$url";
+                ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
+                ($path) =  
+                    ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
+                $fileloc = &Apache::lonnet::filelocation('',$toplevel);
+                $fileloc =~ s{^/}{};
+                ($filename) = ($fileloc =~ m{.+/([^/]+)$});
+                $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
+            }
+        }
+    }
+    my $now = time();
+    foreach my $embed_file (keys(%{$allfiles})) {
+        my $absolutepath;
+        if ($embed_file =~ m{^\w+://}) {
+            $newfiles{$embed_file} = 1;
+            $mapping{$embed_file} = $embed_file;
+        } else {
+            if ($embed_file =~ m{^/}) {
+                $absolutepath = $embed_file;
+                $embed_file =~ s{^(/+)}{};
+            }
+            if ($embed_file =~ m{/}) {
+                my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
+                $path = &check_for_traversal($path,$url,$toplevel);
+                my $item = $fname;
+                if ($path ne '') {
+                    $item = $path.'/'.$fname;
+                    $subdependencies{$path}{$fname} = 1;
+                } else {
+                    $dependencies{$item} = 1;
+                }
+                if ($absolutepath) {
+                    $mapping{$item} = $absolutepath;
+                } else {
+                    $mapping{$item} = $embed_file;
+                }
+            } else {
+                $dependencies{$embed_file} = 1;
+                if ($absolutepath) {
+                    $mapping{$embed_file} = $absolutepath;
+                } else {
+                    $mapping{$embed_file} = $embed_file;
+                }
+            }
+        }
+    }
+    my $dirptr = 16384;
+    foreach my $path (keys(%subdependencies)) {
+        $currsubfile{$path} = {};
+        if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { 
+            my ($sublistref,$listerror) =
+                &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
+            if (ref($sublistref) eq 'ARRAY') {
+                foreach my $line (@{$sublistref}) {
+                    my ($file_name,$rest) = split(/\&/,$line,2);
+                    $currsubfile{$path}{$file_name} = 1;
+                }
+            }
+        } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
+            if (opendir(my $dir,$url.'/'.$path)) {
+                my @subdir_list = grep(!/^\./,readdir($dir));
+                map {$currsubfile{$path}{$_} = 1;} @subdir_list;
+            }
+        } elsif (($actionurl eq '/adm/dependencies') ||
+                 (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
+                  ($args->{'context'} eq 'paste'))) {
+            if ($env{'request.course.id'} ne '') {
+                my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
+                if ($dir ne '') {
+                    my ($sublistref,$listerror) =
+                        &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
+                    if (ref($sublistref) eq 'ARRAY') {
+                        foreach my $line (@{$sublistref}) {
+                            my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
+                                undef,$mtime)=split(/\&/,$line,12);
+                            unless (($testdir&$dirptr) ||
+                                    ($file_name =~ /^\.\.?$/)) {
+                                $currsubfile{$path}{$file_name} = [$size,$mtime];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        foreach my $file (keys(%{$subdependencies{$path}})) {
+            if (exists($currsubfile{$path}{$file})) {
+                my $item = $path.'/'.$file;
+                unless ($mapping{$item} eq $item) {
+                    $pathchanges{$item} = 1;
+                }
+                $existing{$item} = 1;
+                $numexisting ++;
+            } else {
+                $newfiles{$path.'/'.$file} = 1;
+            }
+        }
+        if ($actionurl eq '/adm/dependencies') {
+            foreach my $path (keys(%currsubfile)) {
+                if (ref($currsubfile{$path}) eq 'HASH') {
+                    foreach my $file (keys(%{$currsubfile{$path}})) {
+                         unless ($subdependencies{$path}{$file}) {
+                             next if (($rem ne '') &&
+                                      (($env{"httpref.$rem"."$path/$file"} ne '') ||
+                                       (ref($navmap) &&
+                                       (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
+                                        (($file =~ /^(.*\.s?html?)\.bak$/i) &&
+                                         ($navmap->getResourceByUrl($rem."$path/$1")))))));
+                             $unused{$path.'/'.$file} = 1; 
+                         }
+                    }
+                }
+            }
+        }
+    }
+    my %currfile;
+    if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+        my ($dirlistref,$listerror) =
+            &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
+        if (ref($dirlistref) eq 'ARRAY') {
+            foreach my $line (@{$dirlistref}) {
+                my ($file_name,$rest) = split(/\&/,$line,2);
+                $currfile{$file_name} = 1;
+            }
+        }
+    } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
+        if (opendir(my $dir,$url)) {
+            my @dir_list = grep(!/^\./,readdir($dir));
+            map {$currfile{$_} = 1;} @dir_list;
+        }
+    } elsif (($actionurl eq '/adm/dependencies') ||
+             (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
+              ($args->{'context'} eq 'paste'))) {
+        if ($env{'request.course.id'} ne '') {
+            my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
+            if ($dir ne '') {
+                my ($dirlistref,$listerror) =
+                    &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
+                if (ref($dirlistref) eq 'ARRAY') {
+                    foreach my $line (@{$dirlistref}) {
+                        my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
+                            $size,undef,$mtime)=split(/\&/,$line,12);
+                        unless (($testdir&$dirptr) ||
+                                ($file_name =~ /^\.\.?$/)) {
+                            $currfile{$file_name} = [$size,$mtime];
+                        }
+                    }
+                }
+            }
+        }
+    }
+    foreach my $file (keys(%dependencies)) {
+        if (exists($currfile{$file})) {
+            unless ($mapping{$file} eq $file) {
+                $pathchanges{$file} = 1;
+            }
+            $existing{$file} = 1;
+            $numexisting ++;
+        } else {
+            $newfiles{$file} = 1;
+        }
+    }
+    foreach my $file (keys(%currfile)) {
+        unless (($file eq $filename) ||
+                ($file eq $filename.'.bak') ||
+                ($dependencies{$file})) {
+            if ($actionurl eq '/adm/dependencies') {
+                next if (($rem ne '') &&
+                         (($env{"httpref.$rem".$file} ne '') ||
+                          (ref($navmap) &&
+                          (($navmap->getResourceByUrl($rem.$file) ne '') ||
+                           (($file =~ /^(.*\.s?html?)\.bak$/i) &&
+                            ($navmap->getResourceByUrl($rem.$1)))))));
+            }
+            $unused{$file} = 1;
+        }
+    }
+    if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
+        ($args->{'context'} eq 'paste')) {
+        $counter = scalar(keys(%existing));
+        $numpathchg = scalar(keys(%pathchanges));
+        return ($output,$counter,$numpathchg,\%existing); 
+    }
+    foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
+        if ($actionurl eq '/adm/dependencies') {
+            next if ($embed_file =~ m{^\w+://});
+        }
+        $upload_output .= &start_data_table_row().
+                          '<td><img src="'.&icon($embed_file).'" />&nbsp;'.
+                          '<span class="LC_filename">'.$embed_file.'</span>';
+        unless ($mapping{$embed_file} eq $embed_file) {
+            $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.&mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
+        }
+        $upload_output .= '</td><td>';
+        if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) { 
+            $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>';
+            $numremref++;
+        } elsif ($args->{'error_on_invalid_names'}
+            && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
+            $upload_output.='<span class="LC_warning">'.&mt('Invalid characters').'</span>';
+            $numinvalid++;
+        } else {
+            $upload_output .= &embedded_file_element('upload_embedded',$counter,
+                                                     $embed_file,\%mapping,
+                                                     $allfiles,$codebase,'upload');
+            $counter ++;
+            $numnew ++;
+        }
+        $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
+    }
+    foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
+        if ($actionurl eq '/adm/dependencies') {
+            my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
+            $modify_output .= &start_data_table_row().
+                              '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
+                              '<img src="'.&icon($embed_file).'" border="0" />'.
+                              '&nbsp;<span class="LC_filename">'.$embed_file.'</span></a></td>'.
+                              '<td>'.$size.'</td>'.
+                              '<td>'.$mtime.'</td>'.
+                              '<td><label><input type="checkbox" name="mod_upload_dep" '.
+                              'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
+                              $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
+                              '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
+                              &embedded_file_element('upload_embedded',$counter,
+                                                     $embed_file,\%mapping,
+                                                     $allfiles,$codebase,'modify').
+                              '</div></td>'.
+                              &end_data_table_row()."\n";
+            $counter ++;
+        } else {
+            $upload_output .= &start_data_table_row().
+                              '<td><span class="LC_filename">'.$embed_file.'</span></td>';
+                              '<td><span class="LC_warning">'.&mt('Already exists').'</span></td>'.
+                              &Apache::loncommon::end_data_table_row()."\n";
+        }
+    }
+    my $delidx = $counter;
+    foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
+        my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
+        $delete_output .= &start_data_table_row().
+                          '<td><img src="'.&icon($oldfile).'" />'.
+                          '&nbsp;<span class="LC_filename">'.$oldfile.'</span></td>'.
+                          '<td>'.$size.'</td>'.
+                          '<td>'.$mtime.'</td>'.
+                          '<td><label><input type="checkbox" name="del_upload_dep" '.
+                          ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
+                          &embedded_file_element('upload_embedded',$delidx,
+                                                 $oldfile,\%mapping,$allfiles,
+                                                 $codebase,'delete').'</td>'.
+                          &end_data_table_row()."\n"; 
+        $numunused ++;
+        $delidx ++;
+    }
+    if ($upload_output) {
+        $upload_output = &start_data_table().
+                         $upload_output.
+                         &end_data_table()."\n";
+    }
+    if ($modify_output) {
+        $modify_output = &start_data_table().
+                         &start_data_table_header_row().
+                         '<th>'.&mt('File').'</th>'.
+                         '<th>'.&mt('Size (KB)').'</th>'.
+                         '<th>'.&mt('Modified').'</th>'.
+                         '<th>'.&mt('Upload replacement?').'</th>'.
+                         &end_data_table_header_row().
+                         $modify_output.
+                         &end_data_table()."\n";
+    }
+    if ($delete_output) {
+        $delete_output = &start_data_table().
+                         &start_data_table_header_row().
+                         '<th>'.&mt('File').'</th>'.
+                         '<th>'.&mt('Size (KB)').'</th>'.
+                         '<th>'.&mt('Modified').'</th>'.
+                         '<th>'.&mt('Delete?').'</th>'.
+                         &end_data_table_header_row().
+                         $delete_output.
+                         &end_data_table()."\n";
+    }
+    my $applies = 0;
+    if ($numremref) {
+        $applies ++;
+    }
+    if ($numinvalid) {
+        $applies ++;
+    }
+    if ($numexisting) {
+        $applies ++;
+    }
+    if ($counter || $numunused) {
+        $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
+                  ' method="post" enctype="multipart/form-data">'."\n".
+                  $state.'<h3>'.$heading.'</h3>'; 
+        if ($actionurl eq '/adm/dependencies') {
+            if ($numnew) {
+                $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
+                           '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
+                           $upload_output.'<br />'."\n";
+            }
+            if ($numexisting) {
+                $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
+                           '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
+                           $modify_output.'<br />'."\n";
+                           $buttontext = &mt('Save changes');
+            }
+            if ($numunused) {
+                $output .= '<h4>'.&mt('Unused files').'</h4>'.
+                           '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
+                           $delete_output.'<br />'."\n";
+                           $buttontext = &mt('Save changes');
+            }
+        } else {
+            $output .= $upload_output.'<br />'."\n";
+        }
+        $output .= '<input type ="hidden" name="number_embedded_items" value="'.
+                   $counter.'" />'."\n";
+        if ($actionurl eq '/adm/dependencies') { 
+            $output .= '<input type ="hidden" name="number_newemb_items" value="'.
+                       $numnew.'" />'."\n";
+        } elsif ($actionurl eq '') {
+            $output .=  '<input type="hidden" name="phase" value="three" />';
+        }
+    } elsif ($applies) {
+        $output = '<b>'.&mt('Referenced files').'</b>:<br />';
+        if ($applies > 1) {
+            $output .=  
+                &mt('No files need to be uploaded, as one of the following applies to each reference:').'<ul>';
+            if ($numremref) {
+                $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
+            }
+            if ($numinvalid) {
+                $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
+            }
+            if ($numexisting) {
+                $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
+            }
+            $output .= '</ul><br />';
+        } elsif ($numremref) {
+            $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
+        } elsif ($numinvalid) {
+            $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
+        } elsif ($numexisting) {
+            $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
+        }
+        $output .= $upload_output.'<br />';
+    }
+    my ($pathchange_output,$chgcount);
+    $chgcount = $counter;
+    if (keys(%pathchanges) > 0) {
+        foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
+            if ($counter) {
+                $output .= &embedded_file_element('pathchange',$chgcount,
+                                                  $embed_file,\%mapping,
+                                                  $allfiles,$codebase,'change');
+            } else {
+                $pathchange_output .= 
+                    &start_data_table_row().
+                    '<td><input type ="checkbox" name="namechange" value="'.
+                    $chgcount.'" checked="checked" /></td>'.
+                    '<td>'.$mapping{$embed_file}.'</td>'.
+                    '<td>'.$embed_file.
+                    &embedded_file_element('pathchange',$numpathchg,$embed_file,
+                                           \%mapping,$allfiles,$codebase,'change').
+                    '</td>'.&end_data_table_row();
+            }
+            $numpathchg ++;
+            $chgcount ++;
+        }
+    }
+    if ($counter) {
+        if ($numpathchg) {
+            $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
+                       $numpathchg.'" />'."\n";
+        }
+        if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') || 
+            ($actionurl eq '/adm/imsimport')) {
+            $output .= '<input type="hidden" name="phase" value="three" />'."\n";
+        } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
+            $output .= '<input type="hidden" name="action" value="upload_embedded" />';
+        } elsif ($actionurl eq '/adm/dependencies') {
+            $output .= '<input type="hidden" name="action" value="process_changes" />';
+        }
+        $output .=  '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
+    } elsif ($numpathchg) {
+        my %pathchange = ();
+        $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
+        if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+            $output .= '<p>'.&mt('or').'</p>'; 
+        } 
+    }
+    return ($output,$counter,$numpathchg);
+}
+
+sub embedded_file_element {
+    my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
+    return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
+                   (ref($codebase) eq 'HASH'));
+    my $output;
+    if (($context eq 'upload_embedded') && ($type ne 'delete')) {
+       $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
+    }
+    $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
+               &escape($embed_file).'" />';
+    unless (($context eq 'upload_embedded') && 
+            ($mapping->{$embed_file} eq $embed_file)) {
+        $output .='
+        <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
+    }
+    my $attrib;
+    if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
+        $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
+    }
+    $output .=
+        "\n\t\t".
+        '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
+        $attrib.'" />';
+    if (exists($codebase->{$mapping->{$embed_file}})) {
+        $output .=
+            "\n\t\t".
+            '<input name="codebase_'.$num.'" type="hidden" value="'.
+            &escape($codebase->{$mapping->{$embed_file}}).'" />';
+    }
+    return $output;
+}
+
+sub get_dependency_details {
+    my ($currfile,$currsubfile,$embed_file) = @_;
+    my ($size,$mtime,$showsize,$showmtime);
+    if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
+        if ($embed_file =~ m{/}) {
+            my ($path,$fname) = split(/\//,$embed_file);
+            if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
+                ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
+            }
+        } else {
+            if (ref($currfile->{$embed_file}) eq 'ARRAY') {
+                ($size,$mtime) = @{$currfile->{$embed_file}};
+            }
+        }
+        $showsize = $size/1024.0;
+        $showsize = sprintf("%.1f",$showsize);
+        if ($mtime > 0) {
+            $showmtime = &Apache::lonlocal::locallocaltime($mtime);
+        }
+    }
+    return ($showsize,$showmtime);
+}
+
+sub ask_embedded_js {
+    return <<"END";
+<script type="text/javascript"">
+// <![CDATA[
+function toggleBrowse(counter) {
+    var chkboxid = document.getElementById('mod_upload_dep_'+counter);
+    var fileid = document.getElementById('embedded_item_'+counter);
+    var uploaddivid = document.getElementById('moduploaddep_'+counter);
+    if (chkboxid.checked == true) {
+        uploaddivid.style.display='block';
+    } else {
+        uploaddivid.style.display='none';
+        fileid.value = '';
+    }
+}
+// ]]>
+</script>
+
+END
+}
+
+sub upload_embedded {
+    my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
+        $current_disk_usage,$hiddenstate,$actionurl) = @_;
+    my (%pathchange,$output,$modifyform,$footer,$returnflag);
+    for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
+        next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
+        my $orig_uploaded_filename =
+            $env{'form.embedded_item_'.$i.'.filename'};
+        foreach my $type ('orig','ref','attrib','codebase') {
+            if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
+                $env{'form.embedded_'.$type.'_'.$i} =
+                    &unescape($env{'form.embedded_'.$type.'_'.$i});
+            }
+        }
+        my ($path,$fname) =
+            ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
+        # no path, whole string is fname
+        if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
+        $fname = &Apache::lonnet::clean_filename($fname);
+        # See if there is anything left
+        next if ($fname eq '');
+
+        # Check if file already exists as a file or directory.
+        my ($state,$msg);
+        if ($context eq 'portfolio') {
+            my $port_path = $dirpath;
+            if ($group ne '') {
+                $port_path = "groups/$group/$port_path";
+            }
+            ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
+                                              $fname,$group,'embedded_item_'.$i,
+                                              $dir_root,$port_path,$disk_quota,
+                                              $current_disk_usage,$uname,$udom);
+            if ($state eq 'will_exceed_quota'
+                || $state eq 'file_locked') {
+                $output .= $msg;
+                next;
+            }
+        } elsif (($context eq 'author') || ($context eq 'testbank')) {
+            ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
+            if ($state eq 'exists') {
+                $output .= $msg;
+                next;
+            }
+        }
+        # Check if extension is valid
+        if (($fname =~ /\.(\w+)$/) &&
+            (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
+            $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1).'<br />';
+            next;
+        } elsif (($fname =~ /\.(\w+)$/) &&
+                 (!defined(&Apache::loncommon::fileembstyle($1)))) {
+            $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
+            next;
+        } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
+            $output .= &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).'<br />';
+            next;
+        }
+        $env{'form.embedded_item_'.$i.'.filename'}=$fname;
+        if ($context eq 'portfolio') {
+            my $result;
+            if ($state eq 'existingfile') {
+                $result=
+                    &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
+                                                    $dirpath.$env{'form.currentpath'}.$path);
+            } else {
+                $result=
+                    &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
+                                                    $dirpath.
+                                                    $env{'form.currentpath'}.$path);
+                if ($result !~ m|^/uploaded/|) {
+                    $output .= '<span class="LC_error">'
+                               .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
+                               ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
+                               .'</span><br />';
+                    next;
+                } else {
+                    $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
+                               $path.$fname.'</span>').'<br />';     
+                }
+            }
+        } elsif ($context eq 'coursedoc') {
+            my $result =
+                &Apache::lonnet::userfileupload('embedded_item_'.$i,'coursedoc',
+                                                $dirpath.'/'.$path);
+            if ($result !~ m|^/uploaded/|) {
+                $output .= '<span class="LC_error">'
+                           .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
+                           ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
+                           .'</span><br />';
+                    next;
+            } else {
+                $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
+                           $path.$fname.'</span>').'<br />';
+            }
+        } else {
+# Save the file
+            my $target = $env{'form.embedded_item_'.$i};
+            my $fullpath = $dir_root.$dirpath.'/'.$path;
+            my $dest = $fullpath.$fname;
+            my $url = $url_root.$dirpath.'/'.$path.$fname;
+            my @parts=split(/\//,"$dirpath/$path");
+            my $count;
+            my $filepath = $dir_root;
+            foreach my $subdir (@parts) {
+                $filepath .= "/$subdir";
+                if (!-e $filepath) {
+                    mkdir($filepath,0770);
+                }
+            }
+            my $fh;
+            if (!open($fh,'>'.$dest)) {
+                &Apache::lonnet::logthis('Failed to create '.$dest);
+                $output .= '<span class="LC_error">'.
+                           &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
+                               $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
+                           '</span><br />';
+            } else {
+                if (!print $fh $env{'form.embedded_item_'.$i}) {
+                    &Apache::lonnet::logthis('Failed to write to '.$dest);
+                    $output .= '<span class="LC_error">'.
+                              &mt('An error occurred while writing the file [_1] for embedded element [_2].',
+                                  $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
+                              '</span><br />';
+                } else {
+                    $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
+                               $url.'</span>').'<br />';
+                    unless ($context eq 'testbank') {
+                        $footer .= &mt('View embedded file: [_1]',
+                                       '<a href="'.$url.'">'.$fname.'</a>').'<br />';
+                    }
+                }
+                close($fh);
+            }
+        }
+        if ($env{'form.embedded_ref_'.$i}) {
+            $pathchange{$i} = 1;
+        }
+    }
+    if ($output) {
+        $output = '<p>'.$output.'</p>';
+    }
+    $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
+    $returnflag = 'ok';
+    my $numpathchgs = scalar(keys(%pathchange));
+    if ($numpathchgs > 0) {
+        if ($context eq 'portfolio') {
+            $output .= '<p>'.&mt('or').'</p>';
+        } elsif ($context eq 'testbank') {
+            $output .=  '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
+                                  '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
+            $returnflag = 'modify_orightml';
+        }
+    }
+    return ($output.$footer,$returnflag,$numpathchgs);
+}
+
+sub modify_html_form {
+    my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
+    my $end = 0;
+    my $modifyform;
+    if ($context eq 'upload_embedded') {
+        return unless (ref($pathchange) eq 'HASH');
+        if ($env{'form.number_embedded_items'}) {
+            $end += $env{'form.number_embedded_items'};
+        }
+        if ($env{'form.number_pathchange_items'}) {
+            $end += $env{'form.number_pathchange_items'};
+        }
+        if ($end) {
+            for (my $i=0; $i<$end; $i++) {
+                if ($i < $env{'form.number_embedded_items'}) {
+                    next unless($pathchange->{$i});
+                }
+                $modifyform .=
+                    &start_data_table_row().
+                    '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
+                    'checked="checked" /></td>'.
+                    '<td>'.$env{'form.embedded_ref_'.$i}.
+                    '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
+                    &escape($env{'form.embedded_ref_'.$i}).'" />'.
+                    '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
+                    &escape($env{'form.embedded_codebase_'.$i}).'" />'.
+                    '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
+                    &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
+                    '<td>'.$env{'form.embedded_orig_'.$i}.
+                    '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
+                    &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
+                    &end_data_table_row();
+            }
+        }
+    } else {
+        $modifyform = $pathchgtable;
+        if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
+            $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
+        } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+            $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
+        }
+    }
+    if ($modifyform) {
+        if ($actionurl eq '/adm/dependencies') {
+            $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
+        }
+        return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
+               '<p>'.&mt('Changes need to be made to the reference(s) used for one or more of the dependencies, if your HTML file is to work correctly:').'<ol>'."\n".
+               '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
+               '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
+               '</ol></p>'."\n".'<p>'.
+               &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
+               '<form method="post" name="refchanger" action="'.$actionurl.'">'.
+               &start_data_table()."\n".
+               &start_data_table_header_row().
+               '<th>'.&mt('Change?').'</th>'.
+               '<th>'.&mt('Current reference').'</th>'.
+               '<th>'.&mt('Required reference').'</th>'.
+               &end_data_table_header_row()."\n".
+               $modifyform.
+               &end_data_table().'<br />'."\n".$hiddenstate.
+               '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
+               '</form>'."\n";
+    }
+    return;
+}
+
+sub modify_html_refs {
+    my ($context,$dirpath,$uname,$udom,$dir_root) = @_;
+    my $container;
+    if ($context eq 'portfolio') {
+        $container = $env{'form.container'};
+    } elsif ($context eq 'coursedoc') {
+        $container = $env{'form.primaryurl'};
+    } elsif ($context eq 'manage_dependencies') {
+        (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
+        $container = "/$container";
+    } else {
+        $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
+    }
+    my (%allfiles,%codebase,$output,$content);
+    my @changes = &get_env_multiple('form.namechange');
+    unless (@changes > 0) {
+        if (wantarray) {
+            return ('',0,0); 
+        } else {
+            return;
+        }
+    }
+    if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
+        ($context eq 'manage_dependencies')) {
+        unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
+            if (wantarray) {
+                return ('',0,0);
+            } else {
+                return;
+            }
+        } 
+        $content = &Apache::lonnet::getfile($container);
+        if ($content eq '-1') {
+            if (wantarray) {
+                return ('',0,0);
+            } else {
+                return;
+            }
+        }
+    } else {
+        unless ($container =~ /^\Q$dir_root\E/) {
+            if (wantarray) {
+                return ('',0,0);
+            } else {
+                return;
+            }
+        } 
+        if (open(my $fh,"<$container")) {
+            $content = join('', <$fh>);
+            close($fh);
+        } else {
+            if (wantarray) {
+                return ('',0,0);
+            } else {
+                return;
+            }
+        }
+    }
+    my ($count,$codebasecount) = (0,0);
+    my $mm = new File::MMagic;
+    my $mime_type = $mm->checktype_contents($content);
+    if ($mime_type eq 'text/html') {
+        my $parse_result = 
+            &Apache::lonnet::extract_embedded_items($container,\%allfiles,
+                                                    \%codebase,\$content);
+        if ($parse_result eq 'ok') {
+            foreach my $i (@changes) {
+                my $orig = &unescape($env{'form.embedded_orig_'.$i});
+                my $ref = &unescape($env{'form.embedded_ref_'.$i});
+                if ($allfiles{$ref}) {
+                    my $newname =  $orig;
+                    my ($attrib_regexp,$codebase);
+                    $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
+                    if ($attrib_regexp =~ /:/) {
+                        $attrib_regexp =~ s/\:/|/g;
+                    }
+                    if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
+                        my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
+                        $count += $numchg;
+                    }
+                    if ($env{'form.embedded_codebase_'.$i} ne '') {
+                        $codebase = &unescape($env{'form.embedded_codebase_'.$i});
+                        my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
+                        $codebasecount ++;
+                    }
+                }
+            }
+            if ($count || $codebasecount) {
+                my $saveresult;
+                if (($context eq 'portfolio') || ($context eq 'coursedoc') || 
+                    ($context eq 'manage_dependencies')) {
+                    my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
+                    if ($url eq $container) {
+                        my ($fname) = ($container =~ m{/([^/]+)$});
+                        $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
+                                            $count,'<span class="LC_filename">'.
+                                            $fname.'</span>').'</p>';
+                    } else {
+                         $output = '<p class="LC_error">'.
+                                   &mt('Error: update failed for: [_1].',
+                                   '<span class="LC_filename">'.
+                                   $container.'</span>').'</p>';
+                    }
+                } else {
+                    if (open(my $fh,">$container")) {
+                        print $fh $content;
+                        close($fh);
+                        $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
+                                  $count,'<span class="LC_filename">'.
+                                  $container.'</span>').'</p>';
+                    } else {
+                         $output = '<p class="LC_error">'.
+                                   &mt('Error: could not update [_1].',
+                                   '<span class="LC_filename">'.
+                                   $container.'</span>').'</p>';
+                    }
+                }
+            }
+        } else {
+            &logthis('Failed to parse '.$container.
+                     ' to modify references: '.$parse_result);
+        }
+    }
+    if (wantarray) {
+        return ($output,$count,$codebasecount);
+    } else {
+        return $output;
+    }
+}
+
+sub check_for_existing {
+    my ($path,$fname,$element) = @_;
+    my ($state,$msg);
+    if (-d $path.'/'.$fname) {
+        $state = 'exists';
+        $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
+    } elsif (-e $path.'/'.$fname) {
+        $state = 'exists';
+        $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
+    }
+    if ($state eq 'exists') {
+        $msg = '<span class="LC_error">'.$msg.'</span><br />';
+    }
+    return ($state,$msg);
+}
+
+sub check_for_upload {
+    my ($path,$fname,$group,$element,$portfolio_root,$port_path,
+        $disk_quota,$current_disk_usage,$uname,$udom) = @_;
+    my $filesize = length($env{'form.'.$element});
+    if (!$filesize) {
+        my $msg = '<span class="LC_error">'.
+                  &mt('Unable to upload [_1]. (size = [_2] bytes)', 
+                      '<span class="LC_filename">'.$fname.'</span>',
+                      $filesize).'<br />'.
+                  &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
+                  '</span>';
+        return ('zero_bytes',$msg);
+    }
+    $filesize =  $filesize/1000; #express in k (1024?)
+    my $getpropath = 1;
+    my ($dirlistref,$listerror) =
+         &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
+    my $found_file = 0;
+    my $locked_file = 0;
+    my @lockers;
+    my $navmap;
+    if ($env{'request.course.id'}) {
+        $navmap = Apache::lonnavmaps::navmap->new();
+    }
+    if (ref($dirlistref) eq 'ARRAY') {
+        foreach my $line (@{$dirlistref}) {
+            my ($file_name,$rest)=split(/\&/,$line,2);
+            if ($file_name eq $fname){
+                $file_name = $path.$file_name;
+                if ($group ne '') {
+                    $file_name = $group.$file_name;
+                }
+                $found_file = 1;
+                if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
+                    foreach my $lock (@lockers) {
+                        if (ref($lock) eq 'ARRAY') {
+                            my ($symb,$crsid) = @{$lock};
+                            if ($crsid eq $env{'request.course.id'}) {
+                                if (ref($navmap)) {
+                                    my $res = $navmap->getBySymb($symb);
+                                    foreach my $part (@{$res->parts()}) { 
+                                        my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
+                                        unless (($slot_status == $res->RESERVED) ||
+                                                ($slot_status == $res->RESERVED_LOCATION)) {
+                                            $locked_file = 1;
+                                        }
+                                    }
+                                } else {
+                                    $locked_file = 1;
+                                }
+                            } else {
+                                $locked_file = 1;
+                            }
+                        }
+                   }
+                } else {
+                    my @info = split(/\&/,$rest);
+                    my $currsize = $info[6]/1000;
+                    if ($currsize < $filesize) {
+                        my $extra = $filesize - $currsize;
+                        if (($current_disk_usage + $extra) > $disk_quota) {
+                            my $msg = '<span class="LC_error">'.
+                                      &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded if existing (smaller) file with same name (size = [_3] kilobytes) is replaced.',
+                                          '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</span>'.
+                                      '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
+                                                   $disk_quota,$current_disk_usage);
+                            return ('will_exceed_quota',$msg);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if (($current_disk_usage + $filesize) > $disk_quota){
+        my $msg = '<span class="LC_error">'.
+                &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'.
+                  '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage);
+        return ('will_exceed_quota',$msg);
+    } elsif ($found_file) {
+        if ($locked_file) {
+            my $msg = '<span class="LC_error">';
+            $msg .= &mt('Unable to upload [_1]. A locked file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>','<span class="LC_filename">'.$port_path.$env{'form.currentpath'}.'</span>');
+            $msg .= '</span><br />';
+            $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
+            return ('file_locked',$msg);
+        } else {
+            my $msg = '<span class="LC_error">';
+            $msg .= &mt(' A file by that name: [_1] was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
+            $msg .= '</span>';
+            return ('existingfile',$msg);
+        }
+    }
+}
+
+sub check_for_traversal {
+    my ($path,$url,$toplevel) = @_;
+    my @parts=split(/\//,$path);
+    my $cleanpath;
+    my $fullpath = $url;
+    for (my $i=0;$i<@parts;$i++) {
+        next if ($parts[$i] eq '.');
+        if ($parts[$i] eq '..') {
+            $fullpath =~ s{([^/]+/)$}{};
+        } else {
+            $fullpath .= $parts[$i].'/';
+        }
+    }
+    if ($fullpath =~ /^\Q$url\E(.*)$/) {
+        $cleanpath = $1;
+    } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
+        my $curr_toprel = $1;
+        my @parts = split(/\//,$curr_toprel);
+        my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
+        my @urlparts = split(/\//,$url_toprel);
+        my $doubledots;
+        my $startdiff = -1;
+        for (my $i=0; $i<@urlparts; $i++) {
+            if ($startdiff == -1) {
+                unless ($urlparts[$i] eq $parts[$i]) {
+                    $startdiff = $i;
+                    $doubledots .= '../';
+                }
+            } else {
+                $doubledots .= '../';
+            }
+        }
+        if ($startdiff > -1) {
+            $cleanpath = $doubledots;
+            for (my $i=$startdiff; $i<@parts; $i++) {
+                $cleanpath .= $parts[$i].'/';
+            }
+        }
+    }
+    $cleanpath =~ s{(/)$}{};
+    return $cleanpath;
+}
+
+sub is_archive_file {
+    my ($mimetype) = @_;
+    if (($mimetype eq 'application/octet-stream') ||
+        ($mimetype eq 'application/x-stuffit') ||
+        ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
+        return 1;
+    }
+    return;
+}
+
+sub decompress_form {
+    my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
+    my %lt = &Apache::lonlocal::texthash (
+        this => 'This file is an archive file.',
+        camt => 'This file is a Camtasia archive file.',
+        itsc => 'Its contents are as follows:',
+        youm => 'You may wish to extract its contents.',
+        extr => 'Extract contents',
+        auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
+        proa => 'Process automatically?',
+        yes  => 'Yes',
+        no   => 'No',
+        fold => 'Title for folder containing movie',
+        movi => 'Title for page containing embedded movie', 
+    );
+    my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
+    my ($is_camtasia,$topdir,%toplevel,@paths);
+    my $info = &list_archive_contents($fileloc,\@paths);
+    if (@paths) {
+        foreach my $path (@paths) {
+            $path =~ s{^/}{};
+            if ($path =~ m{^([^/]+)/$}) {
+                $topdir = $1;
+            }
+            if ($path =~ m{^([^/]+)/}) {
+                $toplevel{$1} = $path;
+            } else {
+                $toplevel{$path} = $path;
+            }
+        }
+    }
+    if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
+        my @camtasia = ("$topdir/","$topdir/index.html",
+                        "$topdir/media/",
+                        "$topdir/media/$topdir.mp4",
+                        "$topdir/media/FirstFrame.png",
+                        "$topdir/media/player.swf",
+                        "$topdir/media/swfobject.js",
+                        "$topdir/media/expressInstall.swf");
+        my @diffs = &compare_arrays(\@paths,\@camtasia);
+        if (@diffs == 0) {
+            $is_camtasia = 1;
+        }
+    }
+    my $output;
+    if ($is_camtasia) {
+        $output = <<"ENDCAM";
+<script type="text/javascript" language="Javascript">
+// <![CDATA[
+
+function camtasiaToggle() {
+    for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
+        if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
+            if (document.uploaded_decompress.autoextract_camtasia[i].value == 1) {
+
+                document.getElementById('camtasia_titles').style.display='block';
+            } else {
+                document.getElementById('camtasia_titles').style.display='none';
+            }
+        }
+    }
+    return;
+}
+
+// ]]>
+</script>
+<p>$lt{'camt'}</p>
+ENDCAM
+    } else {
+        $output = '<p>'.$lt{'this'};
+        if ($info eq '') {
+            $output .= ' '.$lt{'youm'}.'</p>'."\n";
+        } else {
+            $output .= ' '.$lt{'itsc'}.'</p>'."\n".
+                       '<div><pre>'.$info.'</pre></div>';
+        }
+    }
+    $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
+    my $duplicates;
+    my $num = 0;
+    if (ref($dirlist) eq 'ARRAY') {
+        foreach my $item (@{$dirlist}) {
+            if (ref($item) eq 'ARRAY') {
+                if (exists($toplevel{$item->[0]})) {
+                    $duplicates .= 
+                        &start_data_table_row().
+                        '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
+                        'value="0" checked="checked" />'.&mt('No').'</label>'.
+                        '&nbsp;<label><input type="radio" name="archive_overwrite_'.$num.'" '.
+                        'value="1" />'.&mt('Yes').'</label>'.
+                        '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
+                        '<td>'.$item->[0].'</td>';
+                    if ($item->[2]) {
+                        $duplicates .= '<td>'.&mt('Directory').'</td>';
+                    } else {
+                        $duplicates .= '<td>'.&mt('File').'</td>';
+                    }
+                    $duplicates .= '<td>'.$item->[3].'</td>'.
+                                   '<td>'.
+                                   &Apache::lonlocal::locallocaltime($item->[4]).
+                                   '</td>'.
+                                   &end_data_table_row();
+                    $num ++;
+                }
+            }
+        }
+    }
+    my $itemcount;
+    if (@paths > 0) {
+        $itemcount = scalar(@paths);
+    } else {
+        $itemcount = 1;
+    }
+    if ($is_camtasia) {
+        $output .= $lt{'auto'}.'<br />'.
+                   '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
+                   '<input type="radio" name="autoextract_camtasia" value="1" onclick="javascript:camtasiaToggle();" checked="checked" />'.
+                   $lt{'yes'}.'</label>&nbsp;<label>'.
+                   '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
+                   $lt{'no'}.'</label></span><br />'.
+                   '<div id="camtasia_titles" style="display:block">'.
+                   &Apache::lonhtmlcommon::start_pick_box().
+                   &Apache::lonhtmlcommon::row_title($lt{'fold'}).
+                   '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
+                   &Apache::lonhtmlcommon::row_closure().
+                   &Apache::lonhtmlcommon::row_title($lt{'movi'}).
+                   '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
+                   &Apache::lonhtmlcommon::row_closure(1).
+                   &Apache::lonhtmlcommon::end_pick_box().
+                   '</div>';
+    }
+    $output .= 
+        '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
+        '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
+        "\n";
+    if ($duplicates ne '') {
+        $output .= '<p><span class="LC_warning">'.
+                   &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.  
+                   &start_data_table().
+                   &start_data_table_header_row().
+                   '<th>'.&mt('Overwrite?').'</th>'.
+                   '<th>'.&mt('Name').'</th>'.
+                   '<th>'.&mt('Type').'</th>'.
+                   '<th>'.&mt('Size').'</th>'.
+                   '<th>'.&mt('Last modified').'</th>'.
+                   &end_data_table_header_row().
+                   $duplicates.
+                   &end_data_table().
+                   '</p>';
+    }
+    $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
+    if (ref($hiddenelements) eq 'HASH') {
+        foreach my $hidden (sort(keys(%{$hiddenelements}))) {
+            $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
+        }
+    }
+    $output .= <<"END";
+<br />
+<input type="submit" name="decompress" value="$lt{'extr'}" />
+</form>
+$noextract
+END
+    return $output;
+}
+
+sub decompression_utility {
+    my ($program) = @_;
+    my @utilities = ('tar','gunzip','bunzip2','unzip'); 
+    my $location;
+    if (grep(/^\Q$program\E$/,@utilities)) { 
+        foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
+                         '/usr/sbin/') {
+            if (-x $dir.$program) {
+                $location = $dir.$program;
+                last;
+            }
+        }
+    }
+    return $location;
+}
+
+sub list_archive_contents {
+    my ($file,$pathsref) = @_;
+    my (@cmd,$output);
+    my $needsregexp;
+    if ($file =~ /\.zip$/) {
+        @cmd = (&decompression_utility('unzip'),"-l");
+        $needsregexp = 1;
+    } elsif (($file =~ m/\.tar\.gz$/) ||
+             ($file =~ /\.tgz$/)) {
+        @cmd = (&decompression_utility('tar'),"-ztf");
+    } elsif ($file =~ /\.tar\.bz2$/) {
+        @cmd = (&decompression_utility('tar'),"-jtf");
+    } elsif ($file =~ m|\.tar$|) {
+        @cmd = (&decompression_utility('tar'),"-tf");
+    }
+    if (@cmd) {
+        undef($!);
+        undef($@);
+        if (open(my $fh,"-|", @cmd, $file)) {
+            while (my $line = <$fh>) {
+                $output .= $line;
+                chomp($line);
+                my $item;
+                if ($needsregexp) {
+                    ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/); 
+                } else {
+                    $item = $line;
+                }
+                if ($item ne '') {
+                    unless (grep(/^\Q$item\E$/,@{$pathsref})) {
+                        push(@{$pathsref},$item);
+                    } 
+                }
+            }
+            close($fh);
+        }
+    }
+    return $output;
+}
+
+sub decompress_uploaded_file {
+    my ($file,$dir) = @_;
+    &Apache::lonnet::appenv({'cgi.file' => $file});
+    &Apache::lonnet::appenv({'cgi.dir' => $dir});
+    my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
+    my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
+    my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
+    &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
+    my $decompressed = $env{'cgi.decompressed'};
+    &Apache::lonnet::delenv('cgi.file');
+    &Apache::lonnet::delenv('cgi.dir');
+    &Apache::lonnet::delenv('cgi.decompressed');
+    return ($decompressed,$result);
+}
+
+sub process_decompression {
+    my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
+    my ($dir,$error,$warning,$output);
+    if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/) {
+        $error = &mt('Filename not a supported archive file type.').
+                 '<br />'.&mt('Filename should end with one of: [_1].',
+                              '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
+    } else {
+        my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
+        if ($docuhome eq 'no_host') {
+            $error = &mt('Could not determine home server for course.');
+        } else {
+            my @ids=&Apache::lonnet::current_machine_ids();
+            my $currdir = "$dir_root/$destination";
+            if (grep(/^\Q$docuhome\E$/,@ids)) {
+                $dir = &LONCAPA::propath($docudom,$docuname).
+                       "$dir_root/$destination";
+            } else {
+                $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
+                       "$dir_root/$docudom/$docuname/$destination";
+                unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
+                    $error = &mt('Archive file not found.');
+                }
+            }
+            my (@to_overwrite,@to_skip);
+            if ($env{'form.archive_overwrite_total'} > 0) {
+                my $total = $env{'form.archive_overwrite_total'};
+                for (my $i=0; $i<$total; $i++) {
+                    if ($env{'form.archive_overwrite_'.$i} == 1) {
+                        push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
+                    } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
+                        push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
+                    }
+                }
+            }
+            my $numskip = scalar(@to_skip);
+            if (($numskip > 0) && 
+                ($numskip == $env{'form.archive_itemcount'})) {
+                $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');         
+            } elsif ($dir eq '') {
+                $error = &mt('Directory containing archive file unavailable.');
+            } elsif (!$error) {
+                my ($decompressed,$display);
+                if ($numskip > 0) {
+                    my $tempdir = time.'_'.$$.int(rand(10000));
+                    mkdir("$dir/$tempdir",0755);
+                    system("mv $dir/$file $dir/$tempdir/$file");
+                    ($decompressed,$display) = 
+                        &decompress_uploaded_file($file,"$dir/$tempdir");
+                    foreach my $item (@to_skip) {
+                        if (($item ne '') && ($item !~ /\.\./)) {
+                            if (-f "$dir/$tempdir/$item") { 
+                                unlink("$dir/$tempdir/$item");
+                            } elsif (-d "$dir/$tempdir/$item") {
+                                system("rm -rf $dir/$tempdir/$item");
+                            }
+                        }
+                    }
+                    system("mv $dir/$tempdir/* $dir");
+                    rmdir("$dir/$tempdir");   
+                } else {
+                    ($decompressed,$display) = 
+                        &decompress_uploaded_file($file,$dir);
+                }
+                if ($decompressed eq 'ok') {
+                    $output = '<p class="LC_info">'.
+                              &mt('Files extracted successfully from archive.').
+                              '</p>'."\n";
+                    my ($warning,$result,@contents);
+                    my ($newdirlistref,$newlisterror) =
+                        &Apache::lonnet::dirlist($currdir,$docudom,
+                                                 $docuname,1);
+                    my (%is_dir,%changes,@newitems);
+                    my $dirptr = 16384;
+                    if (ref($newdirlistref) eq 'ARRAY') {
+                        foreach my $dir_line (@{$newdirlistref}) {
+                            my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
+                            unless (($item =~ /^\.+$/) || ($item eq $file) || 
+                                    ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) {
+                                push(@newitems,$item);
+                                if ($dirptr&$testdir) {
+                                    $is_dir{$item} = 1;
+                                }
+                                $changes{$item} = 1;
+                            }
+                        }
+                    }
+                    if (keys(%changes) > 0) {
+                        foreach my $item (sort(@newitems)) {
+                            if ($changes{$item}) {
+                                push(@contents,$item);
+                            }
+                        }
+                    }
+                    if (@contents > 0) {
+                        my $wantform;
+                        unless ($env{'form.autoextract_camtasia'}) {
+                            $wantform = 1;
+                        }
+                        my (%children,%parent,%dirorder,%titles);
+                        my ($count,$datatable) = &get_extracted($docudom,$docuname,
+                                                                $currdir,\%is_dir,
+                                                                \%children,\%parent,
+                                                                \@contents,\%dirorder,
+                                                                \%titles,$wantform);
+                        if ($datatable ne '') {
+                            $output .= &archive_options_form('decompressed',$datatable,
+                                                             $count,$hiddenelem);
+                            my $startcount = 6;
+                            $output .= &archive_javascript($startcount,$count,
+                                                           \%titles,\%children);
+                        }
+                        if ($env{'form.autoextract_camtasia'}) {
+                            my %displayed;
+                            my $total = 1;
+                            $env{'form.archive_directory'} = [];
+                            foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
+                                my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
+                                $path =~ s{/$}{};
+                                my $item;
+                                if ($path ne '') {
+                                    $item = "$path/$titles{$i}";
+                                } else {
+                                    $item = $titles{$i};
+                                }
+                                $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
+                                if ($item eq $contents[0]) {
+                                    push(@{$env{'form.archive_directory'}},$i);
+                                    $env{'form.archive_'.$i} = 'display';
+                                    $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
+                                    $displayed{'folder'} = $i;
+                                } elsif ($item eq "$contents[0]/index.html") {
+                                    $env{'form.archive_'.$i} = 'display';
+                                    $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
+                                    $displayed{'web'} = $i;
+                                } else {
+                                    if ($item eq "$contents[0]/media") {
+                                        push(@{$env{'form.archive_directory'}},$i);
+                                    }
+                                    $env{'form.archive_'.$i} = 'dependency';
+                                }
+                                $total ++;
+                            }
+                            for (my $i=1; $i<$total; $i++) {
+                                next if ($i == $displayed{'web'});
+                                next if ($i == $displayed{'folder'});
+                                $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
+                            }
+                            $env{'form.phase'} = 'decompress_cleanup';
+                            $env{'form.archivedelete'} = 1;
+                            $env{'form.archive_count'} = $total-1;
+                            $output .=
+                                &process_extracted_files('coursedocs',$docudom,
+                                                         $docuname,$destination,
+                                                         $dir_root,$hiddenelem);
+                        }
+                    } else {
+                        $warning = &mt('No new items extracted from archive file.');
+                    }
+                } else {
+                    $output = $display;
+                    $error = &mt('An error occurred during extraction from the archive file.');
+                }
+            }
+        }
+    }
+    if ($error) {
+        $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
+                   $error.'</p>'."\n";
+    }
+    if ($warning) {
+        $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
+    }
+    return $output;
+}
+
+sub get_extracted {
+    my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
+        $titles,$wantform) = @_;
+    my $count = 0;
+    my $depth = 0;
+    my $datatable;
+    my @hierarchy;
+    return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
+                   (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
+                   (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
+    foreach my $item (@{$contents}) {
+        $count ++;
+        @{$dirorder->{$count}} = @hierarchy;
+        $titles->{$count} = $item;
+        &archive_hierarchy($depth,$count,$parent,$children);
+        if ($wantform) {
+            $datatable .= &archive_row($is_dir->{$item},$item,
+                                       $currdir,$depth,$count);
+        }
+        if ($is_dir->{$item}) {
+            $depth ++;
+            push(@hierarchy,$count);
+            $parent->{$depth} = $count;
+            $datatable .=
+                &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
+                                           \$depth,\$count,\@hierarchy,$dirorder,
+                                           $children,$parent,$titles,$wantform);
+            $depth --;
+            pop(@hierarchy);
+        }
+    }
+    return ($count,$datatable);
+}
+
+sub recurse_extracted_archive {
+    my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
+        $children,$parent,$titles,$wantform) = @_;
+    my $result='';
+    unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
+            (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
+            (ref($dirorder) eq 'HASH')) {
+        return $result;
+    }
+    my $dirptr = 16384;
+    my ($newdirlistref,$newlisterror) =
+        &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
+    if (ref($newdirlistref) eq 'ARRAY') {
+        foreach my $dir_line (@{$newdirlistref}) {
+            my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
+            unless ($item =~ /^\.+$/) {
+                $$count ++;
+                @{$dirorder->{$$count}} = @{$hierarchy};
+                $titles->{$$count} = $item;
+                &archive_hierarchy($$depth,$$count,$parent,$children);
+
+                my $is_dir;
+                if ($dirptr&$testdir) {
+                    $is_dir = 1;
+                }
+                if ($wantform) {
+                    $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
+                }
+                if ($is_dir) {
+                    $$depth ++;
+                    push(@{$hierarchy},$$count);
+                    $parent->{$$depth} = $$count;
+                    $result .=
+                        &recurse_extracted_archive("$currdir/$item",$docudom,
+                                                   $docuname,$depth,$count,
+                                                   $hierarchy,$dirorder,$children,
+                                                   $parent,$titles,$wantform);
+                    $$depth --;
+                    pop(@{$hierarchy});
+                }
+            }
+        }
+    }
+    return $result;
+}
+
+sub archive_hierarchy {
+    my ($depth,$count,$parent,$children) =@_;
+    if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
+        if (exists($parent->{$depth})) {
+             $children->{$parent->{$depth}} .= $count.':';
+        }
+    }
+    return;
+}
+
+sub archive_row {
+    my ($is_dir,$item,$currdir,$depth,$count) = @_;
+    my ($name) = ($item =~ m{([^/]+)$});
+    my %choices = &Apache::lonlocal::texthash (
+                                       'display'    => 'Add as file',
+                                       'dependency' => 'Include as dependency',
+                                       'discard'    => 'Discard',
+                                      );
+    if ($is_dir) {
+        $choices{'display'} = &mt('Add as folder'); 
+    }
+    my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
+    my $offset = 0;
+    foreach my $action ('display','dependency','discard') {
+        $offset ++;
+        if ($action ne 'display') {
+            $offset ++;
+        }  
+        $output .= '<td><span class="LC_nobreak">'.
+                   '<label><input type="radio" name="archive_'.$count.
+                   '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
+        my $text = $choices{$action};
+        if ($is_dir) {
+            $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
+            if ($action eq 'display') {
+                $text = &mt('Add as folder');
+            }
+        } else {
+            $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
+
+        }
+        $output .= ' />&nbsp;'.$choices{$action}.'</label></span>';
+        if ($action eq 'dependency') {
+            $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
+                       &mt('Used by:').'&nbsp;<select name="archive_dependent_on_'.$count.'" '.
+                       'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
+                       '<option value=""></option>'."\n".
+                       '</select>'."\n".
+                       '</div>';
+        } elsif ($action eq 'display') {
+            $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
+                       &mt('Title:').'&nbsp;<input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
+                       '</div>';
+        }
+        $output .= '</td>';
+    }
+    $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
+               &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.('&nbsp;' x 2);
+    for (my $i=0; $i<$depth; $i++) {
+        $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
+    }
+    if ($is_dir) {
+        $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" />&nbsp;'."\n".
+                   '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
+    } else {
+        $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
+    }
+    $output .= '&nbsp;'.$name.'</td>'."\n".
+               &end_data_table_row();
+    return $output;
+}
+
+sub archive_options_form {
+    my ($form,$display,$count,$hiddenelem) = @_;
+    my %lt = &Apache::lonlocal::texthash(
+               perm => 'Permanently remove archive file?',
+               hows => 'How should each extracted item be incorporated in the course?',
+               cont => 'Content actions for all',
+               addf => 'Add as folder/file',
+               incd => 'Include as dependency for a displayed file',
+               disc => 'Discard',
+               no   => 'No',
+               yes  => 'Yes',
+               save => 'Save',
+    );
+    my $output = <<"END";
+<form name="$form" method="post" action="">
+<p><span class="LC_nobreak">$lt{'perm'}&nbsp;
+<label>
+  <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
+</label>
+&nbsp;
+<label>
+  <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
+</span>
+</p>
+<input type="hidden" name="phase" value="decompress_cleanup" />
+<br />$lt{'hows'}
+<div class="LC_columnSection">
+  <fieldset>
+    <legend>$lt{'cont'}</legend>
+    <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" /> 
+    &nbsp;&nbsp;<input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
+    &nbsp;&nbsp;<input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
+  </fieldset>
+</div>
+END
+    return $output.
+           &start_data_table()."\n".
+           $display."\n".
+           &end_data_table()."\n".
+           '<input type="hidden" name="archive_count" value="'.$count.'" />'.
+           $hiddenelem.
+           '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
+           '</form>';
+}
+
+sub archive_javascript {
+    my ($startcount,$numitems,$titles,$children) = @_;
+    return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
+    my $maintitle = $env{'form.comment'};
+    my $scripttag = <<START;
+<script type="text/javascript">
+// <![CDATA[
+
+function checkAll(form,prefix) {
+    var idstr =  new RegExp("^archive_"+prefix+"_\\\\d+\$");
+    for (var i=0; i < form.elements.length; i++) {
+        var id = form.elements[i].id;
+        if ((id != '') && (id != undefined)) {
+            if (idstr.test(id)) {
+                if (form.elements[i].type == 'radio') {
+                    form.elements[i].checked = true;
+                    var nostart = i-$startcount;
+                    var offset = nostart%7;
+                    var count = (nostart-offset)/7;    
+                    dependencyCheck(form,count,offset);
+                }
+            }
+        }
+    }
+}
+
+function propagateCheck(form,count) {
+    if (count > 0) {
+        var startelement = $startcount + ((count-1) * 7);
+        for (var j=1; j<6; j++) {
+            if ((j != 2) && (j != 4)) {
+                var item = startelement + j; 
+                if (form.elements[item].type == 'radio') {
+                    if (form.elements[item].checked) {
+                        containerCheck(form,count,j);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
+
+numitems = $numitems
+var titles = new Array(numitems);
+var parents = new Array(numitems);
+for (var i=0; i<numitems; i++) {
+    parents[i] = new Array;
+}
+var maintitle = '$maintitle';
+
+START
+
+    foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
+        my @contents = split(/:/,$children->{$container});
+        for (my $i=0; $i<@contents; $i ++) {
+            $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
+        }
+    }
+
+    foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
+        $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
+    }
+
+    $scripttag .= <<END;
+
+function containerCheck(form,count,offset) {
+    if (count > 0) {
+        dependencyCheck(form,count,offset);
+        var item = (offset+$startcount)+7*(count-1);
+        form.elements[item].checked = true;
+        if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
+            if (parents[count].length > 0) {
+                for (var j=0; j<parents[count].length; j++) {
+                    containerCheck(form,parents[count][j],offset);
+                }
+            }
+        }
+    }
+}
+
+function dependencyCheck(form,count,offset) {
+    if (count > 0) {
+        var chosen = (offset+$startcount)+7*(count-1);
+        var depitem = $startcount + ((count-1) * 7) + 4;
+        var currtype = form.elements[depitem].type;
+        if (form.elements[chosen].value == 'dependency') {
+            document.getElementById('arc_depon_'+count).style.display='block'; 
+            form.elements[depitem].options.length = 0;
+            form.elements[depitem].options[0] = new Option('Select','',true,true);
+            for (var i=1; i<=numitems; i++) {
+                if (i == count) {
+                    continue;
+                }
+                var startelement = $startcount + (i-1) * 7;
+                for (var j=1; j<6; j++) {
+                    if ((j != 2) && (j!= 4)) {
+                        var item = startelement + j;
+                        if (form.elements[item].type == 'radio') {
+                            if (form.elements[item].checked) {
+                                if (form.elements[item].value == 'display') {
+                                    var n = form.elements[depitem].options.length;
+                                    form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            document.getElementById('arc_depon_'+count).style.display='none';
+            form.elements[depitem].options.length = 0;
+            form.elements[depitem].options[0] = new Option('Select','',true,true);
+        }
+        titleCheck(form,count,offset);
+    }
+}
+
+function propagateSelect(form,count,offset) {
+    if (count > 0) {
+        var item = (1+offset+$startcount)+7*(count-1);
+        var picked = form.elements[item].options[form.elements[item].selectedIndex].value; 
+        if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
+            if (parents[count].length > 0) {
+                for (var j=0; j<parents[count].length; j++) {
+                    containerSelect(form,parents[count][j],offset,picked);
+                }
+            }
+        }
+    }
+}
+
+function containerSelect(form,count,offset,picked) {
+    if (count > 0) {
+        var item = (offset+$startcount)+7*(count-1);
+        if (form.elements[item].type == 'radio') {
+            if (form.elements[item].value == 'dependency') {
+                if (form.elements[item+1].type == 'select-one') {
+                    for (var i=0; i<form.elements[item+1].options.length; i++) {
+                        if (form.elements[item+1].options[i].value == picked) {
+                            form.elements[item+1].selectedIndex = i;
+                            break;
+                        }
+                    }
+                }
+                if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
+                    if (parents[count].length > 0) {
+                        for (var j=0; j<parents[count].length; j++) {
+                            containerSelect(form,parents[count][j],offset,picked);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+function titleCheck(form,count,offset) {
+    if (count > 0) {
+        var chosen = (offset+$startcount)+7*(count-1);
+        var depitem = $startcount + ((count-1) * 7) + 2;
+        var currtype = form.elements[depitem].type;
+        if (form.elements[chosen].value == 'display') {
+            document.getElementById('arc_title_'+count).style.display='block';
+            if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
+                document.getElementById('archive_title_'+count).value=maintitle;
+            }
+        } else {
+            document.getElementById('arc_title_'+count).style.display='none';
+            if (currtype == 'text') { 
+                document.getElementById('archive_title_'+count).value='';
+            }
+        }
+    }
+    return;
+}
+
+// ]]>
+</script>
+END
+    return $scripttag;
+}
+
+sub process_extracted_files {
+    my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
+    my $numitems = $env{'form.archive_count'};
+    return unless ($numitems);
+    my @ids=&Apache::lonnet::current_machine_ids();
+    my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
+        %folders,%containers,%mapinner,%prompttofetch);
+    my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
+    if (grep(/^\Q$docuhome\E$/,@ids)) {
+        $prefix = &LONCAPA::propath($docudom,$docuname);
+        $pathtocheck = "$dir_root/$destination";
+        $dir = $dir_root;
+        $ishome = 1;
+    } else {
+        $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
+        $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
+        $dir = "$dir_root/$docudom/$docuname";    
+    }
+    my $currdir = "$dir_root/$destination";
+    (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
+    if ($env{'form.folderpath'}) {
+        my @items = split('&',$env{'form.folderpath'});
+        $folders{'0'} = $items[-2];
+        if ($env{'form.folderpath'} =~ /\:1$/) {
+            $containers{'0'}='page';
+        } else {  
+            $containers{'0'}='sequence';
+        }
+    }
+    my @archdirs = &get_env_multiple('form.archive_directory');
+    if ($numitems) {
+        for (my $i=1; $i<=$numitems; $i++) {
+            my $path = $env{'form.archive_content_'.$i};
+            if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
+                my $item = $1;
+                $toplevelitems{$item} = $i;
+                if (grep(/^\Q$i\E$/,@archdirs)) {
+                    $is_dir{$item} = 1;
+                }
+            }
+        }
+    }
+    my ($output,%children,%parent,%titles,%dirorder,$result);
+    if (keys(%toplevelitems) > 0) {
+        my @contents = sort(keys(%toplevelitems));
+        (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
+                                           \%parent,\@contents,\%dirorder,\%titles);
+    }
+    my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
+    if ($numitems) {
+        for (my $i=1; $i<=$numitems; $i++) {
+            next if ($env{'form.archive_'.$i} eq 'dependency');
+            my $path = $env{'form.archive_content_'.$i};
+            if ($path =~ /^\Q$pathtocheck\E/) {
+                if ($env{'form.archive_'.$i} eq 'discard') {
+                    if ($prefix ne '' && $path ne '') {
+                        if (-e $prefix.$path) {
+                            if ((@archdirs > 0) && 
+                                (grep(/^\Q$i\E$/,@archdirs))) {
+                                $todeletedir{$prefix.$path} = 1;
+                            } else {
+                                $todelete{$prefix.$path} = 1;
+                            }
+                        }
+                    }
+                } elsif ($env{'form.archive_'.$i} eq 'display') {
+                    my ($docstitle,$title,$url,$outer);
+                    ($title) = ($path =~ m{/([^/]+)$});
+                    $docstitle = $env{'form.archive_title_'.$i};
+                    if ($docstitle eq '') {
+                        $docstitle = $title;
+                    }
+                    $outer = 0;
+                    if (ref($dirorder{$i}) eq 'ARRAY') {
+                        if (@{$dirorder{$i}} > 0) {
+                            foreach my $item (reverse(@{$dirorder{$i}})) {
+                                if ($env{'form.archive_'.$item} eq 'display') {
+                                    $outer = $item;
+                                    last;
+                                }
+                            }
+                        }
+                    }
+                    my ($errtext,$fatal) = 
+                        &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
+                                               '/'.$folders{$outer}.'.'.
+                                               $containers{$outer});
+                    next if ($fatal);
+                    if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
+                        if ($context eq 'coursedocs') {
+                            $mapinner{$i} = time;
+                            $folders{$i} = 'default_'.$mapinner{$i};
+                            $containers{$i} = 'sequence';
+                            my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
+                                      $folders{$i}.'.'.$containers{$i};
+                            my $newidx = &LONCAPA::map::getresidx();
+                            $LONCAPA::map::resources[$newidx]=
+                                $docstitle.':'.$url.':false:normal:res';
+                            push(@LONCAPA::map::order,$newidx);
+                            my ($outtext,$errtext) =
+                                &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
+                                                        $docuname.'/'.$folders{$outer}.
+                                                        '.'.$containers{$outer},1,1);
+                            $newseqid{$i} = $newidx;
+                            unless ($errtext) {
+                                $result .=  '<li>'.&mt('Folder: [_1] added to course',$docstitle).'</li>'."\n";
+                            }
+                        }
+                    } else {
+                        if ($context eq 'coursedocs') {
+                            my $newidx=&LONCAPA::map::getresidx();
+                            my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
+                                      $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
+                                      $title;
+                            if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
+                                mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
+                            }
+                            if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
+                                mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
+                            }
+                            if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
+                                system("mv $prefix$path $prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title");
+                                $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
+                                unless ($ishome) {
+                                    my $fetch = "$newdest{$i}/$title";
+                                    $fetch =~ s/^\Q$prefix$dir\E//;
+                                    $prompttofetch{$fetch} = 1;
+                                }
+                            }
+                            $LONCAPA::map::resources[$newidx]=
+                                $docstitle.':'.$url.':false:normal:res';
+                            push(@LONCAPA::map::order, $newidx);
+                            my ($outtext,$errtext)=
+                                &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
+                                                        $docuname.'/'.$folders{$outer}.
+                                                        '.'.$containers{$outer},1,1);
+                            unless ($errtext) {
+                                if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
+                                    $result .= '<li>'.&mt('File: [_1] added to course',$docstitle).'</li>'."\n";
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />'; 
+            }
+        }
+        for (my $i=1; $i<=$numitems; $i++) {
+            next unless ($env{'form.archive_'.$i} eq 'dependency');
+            my $path = $env{'form.archive_content_'.$i};
+            if ($path =~ /^\Q$pathtocheck\E/) {
+                my ($title) = ($path =~ m{/([^/]+)$});
+                $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
+                if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
+                    if (ref($dirorder{$i}) eq 'ARRAY') {
+                        my ($itemidx,$fullpath,$relpath);
+                        if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
+                            my $container = $dirorder{$referrer{$i}}->[-1];
+                            for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
+                                if ($dirorder{$i}->[$j] eq $container) {
+                                    $itemidx = $j;
+                                }
+                            }
+                        }
+                        if ($itemidx eq '') {
+                            $itemidx =  0;
+                        } 
+                        if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
+                            if ($mapinner{$referrer{$i}}) {
+                                $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
+                                for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
+                                    if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
+                                        unless (defined($newseqid{$dirorder{$i}->[$j]})) {
+                                            $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
+                                            $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
+                                            if (!-e $fullpath) {
+                                                mkdir($fullpath,0755);
+                                            }
+                                        }
+                                    } else {
+                                        last;
+                                    }
+                                }
+                            }
+                        } elsif ($newdest{$referrer{$i}}) {
+                            $fullpath = $newdest{$referrer{$i}};
+                            for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
+                                if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
+                                    $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
+                                    last;
+                                } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
+                                    unless (defined($newseqid{$dirorder{$i}->[$j]})) {
+                                        $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
+                                        $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
+                                        if (!-e $fullpath) {
+                                            mkdir($fullpath,0755);
+                                        }
+                                    }
+                                } else {
+                                    last;
+                                }
+                            }
+                        }
+                        if ($fullpath ne '') {
+                            if (-e "$prefix$path") {
+                                system("mv $prefix$path $fullpath/$title");
+                            }
+                            if (-e "$fullpath/$title") {
+                                my $showpath;
+                                if ($relpath ne '') {
+                                    $showpath = "$relpath/$title";
+                                } else {
+                                    $showpath = "/$title";
+                                } 
+                                $result .= '<li>'.&mt('[_1] included as a dependency',$showpath).'</li>'."\n";
+                            } 
+                            unless ($ishome) {
+                                my $fetch = "$fullpath/$title";
+                                $fetch =~ s/^\Q$prefix$dir\E//; 
+                                $prompttofetch{$fetch} = 1;
+                            }
+                        }
+                    }
+                } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
+                    $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
+                                    $path,$env{'form.archive_content_'.$referrer{$i}}).'<br />';
+                }
+            } else {
+                $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />'; 
+            }
+        }
+        if (keys(%todelete)) {
+            foreach my $key (keys(%todelete)) {
+                unlink($key);
+            }
+        }
+        if (keys(%todeletedir)) {
+            foreach my $key (keys(%todeletedir)) {
+                rmdir($key);
+            }
+        }
+        foreach my $dir (sort(keys(%is_dir))) {
+            if (($pathtocheck ne '') && ($dir ne ''))  {
+                &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
+            }
+        }
+        if ($result ne '') {
+            $output .= '<ul>'."\n".
+                       $result."\n".
+                       '</ul>';
+        }
+        unless ($ishome) {
+            my $replicationfail;
+            foreach my $item (keys(%prompttofetch)) {
+                my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
+                unless ($fetchresult eq 'ok') {
+                    $replicationfail .= '<li>'.$item.'</li>'."\n";
+                }
+            }
+            if ($replicationfail) {
+                $output .= '<p class="LC_error">'.
+                           &mt('Course home server failed to retrieve:').'<ul>'.
+                           $replicationfail.
+                           '</ul></p>';
+            }
+        }
+    } else {
+        $warning = &mt('No items found in archive.');
+    }
+    if ($error) {
+        $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
+                   $error.'</p>'."\n";
+    }
+    if ($warning) {
+        $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
+    }
+    return $output;
+}
+
+sub cleanup_empty_dirs {
+    my ($path) = @_;
+    if (($path ne '') && (-d $path)) {
+        if (opendir(my $dirh,$path)) {
+            my @dircontents = grep(!/^\./,readdir($dirh));
+            my $numitems = 0;
+            foreach my $item (@dircontents) {
+                if (-d "$path/$item") {
+                    &cleanup_empty_dirs("$path/$item");
+                    if (-e "$path/$item") {
+                        $numitems ++;
+                    }
+                } else {
+                    $numitems ++;
+                }
+            }
+            if ($numitems == 0) {
+                rmdir($path);
+            }
+            closedir($dirh);
+        }
+    }
+    return;
+}
+
+=pod
+
+=item &get_folder_hierarchy()
+
+Provides hierarchy of names of folders/sub-folders containing the current
+item,
+
+Inputs: 3
+     - $navmap - navmaps object
+
+     - $map - url for map (either the trigger itself, or map containing
+                           the resource, which is the trigger).
+
+     - $showitem - 1 => show title for map itself; 0 => do not show.
+
+Outputs: 1 @pathitems - array of folder/subfolder names.
+
+=cut
+
+sub get_folder_hierarchy {
+    my ($navmap,$map,$showitem) = @_;
+    my @pathitems;
+    if (ref($navmap)) {
+        my $mapres = $navmap->getResourceByUrl($map);
+        if (ref($mapres)) {
+            my $pcslist = $mapres->map_hierarchy();
+            if ($pcslist ne '') {
+                my @pcs = split(/,/,$pcslist);
+                foreach my $pc (@pcs) {
+                    if ($pc == 1) {
+                        push(@pathitems,&mt('Main Course Documents'));
+                    } else {
+                        my $res = $navmap->getByMapPc($pc);
+                        if (ref($res)) {
+                            my $title = $res->compTitle();
+                            $title =~ s/\W+/_/g;
+                            if ($title ne '') {
+                                push(@pathitems,$title);
+                            }
+                        }
+                    }
+                }
+            }
+            if ($showitem) {
+                if ($mapres->{ID} eq '0.0') {
+                    push(@pathitems,&mt('Main Course Documents'));
+                } else {
+                    my $maptitle = $mapres->compTitle();
+                    $maptitle =~ s/\W+/_/g;
+                    if ($maptitle ne '') {
+                        push(@pathitems,$maptitle);
+                    }
+                }
+            }
+        }
+    }
+    return @pathitems;
+}
+
+=pod
+
+=item * &get_turnedin_filepath()
+
+Determines path in a user's portfolio file for storage of files uploaded
+to a specific essayresponse or dropbox item.
+
+Inputs: 3 required + 1 optional.
+$symb is symb for resource, $uname and $udom are for current user (required).
+$caller is optional (can be "submission", if routine is called when storing
+an upoaded file when "Submit Answer" button was pressed).
+
+Returns array containing $path and $multiresp. 
+$path is path in portfolio.  $multiresp is 1 if this resource contains more
+than one file upload item.  Callers of routine should append partid as a 
+subdirectory to $path in cases where $multiresp is 1.
+
+Called by: homework/essayresponse.pm and homework/structuretags.pm
+
+=cut
+
+sub get_turnedin_filepath {
+    my ($symb,$uname,$udom,$caller) = @_;
+    my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
+    my $turnindir;
+    my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
+    $turnindir = $userhash{'turnindir'};
+    my ($path,$multiresp);
+    if ($turnindir eq '') {
+        if ($caller eq 'submission') {
+            $turnindir = &mt('turned in');
+            $turnindir =~ s/\W+/_/g;
+            my %newhash = (
+                            'turnindir' => $turnindir,
+                          );
+            &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
+        }
+    }
+    if ($turnindir ne '') {
+        $path = '/'.$turnindir.'/';
+        my ($multipart,$turnin,@pathitems);
+        my $navmap = Apache::lonnavmaps::navmap->new();
+        if (defined($navmap)) {
+            my $mapres = $navmap->getResourceByUrl($map);
+            if (ref($mapres)) {
+                my $pcslist = $mapres->map_hierarchy();
+                if ($pcslist ne '') {
+                    foreach my $pc (split(/,/,$pcslist)) {
+                        my $res = $navmap->getByMapPc($pc);
+                        if (ref($res)) {
+                            my $title = $res->compTitle();
+                            $title =~ s/\W+/_/g;
+                            if ($title ne '') {
+                                push(@pathitems,$title);
+                            }
+                        }
+                    }
+                }
+                my $maptitle = $mapres->compTitle();
+                $maptitle =~ s/\W+/_/g;
+                if ($maptitle ne '') {
+                    push(@pathitems,$maptitle);
+                }
+                unless ($env{'request.state'} eq 'construct') {
+                    my $res = $navmap->getBySymb($symb);
+                    if (ref($res)) {
+                        my $partlist = $res->parts();
+                        my $totaluploads = 0;
+                        if (ref($partlist) eq 'ARRAY') {
+                            foreach my $part (@{$partlist}) {
+                                my @types = $res->responseType($part);
+                                my @ids = $res->responseIds($part);
+                                for (my $i=0; $i < scalar(@ids); $i++) {
+                                    if ($types[$i] eq 'essay') {
+                                        my $partid = $part.'_'.$ids[$i];
+                                        if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
+                                            $totaluploads ++;
+                                        }
+                                    }
+                                }
+                            }
+                            if ($totaluploads > 1) {
+                                $multiresp = 1;
+                            }
+                        }
+                    }
+                }
+            } else {
+                return;
+            }
+        } else {
+            return;
+        }
+        my $restitle=&Apache::lonnet::gettitle($symb);
+        $restitle =~ s/\W+/_/g;
+        if ($restitle eq '') {
+            $restitle = ($resurl =~ m{/[^/]+$});
+            if ($restitle eq '') {
+                $restitle = time;
+            }
+        }
+        push(@pathitems,$restitle);
+        $path .= join('/',@pathitems);
+    }
+    return ($path,$multiresp);
+}
 
 =pod
 
@@ -6837,7 +11866,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'}
@@ -6867,7 +11896,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'},
@@ -6891,7 +11920,7 @@ sub load_tmp_file {
 
 =pod
 
-=item * upfile_record_sep()
+=item * &upfile_record_sep()
 
 Separate uploaded file into records
 returns array of records,
@@ -6913,7 +11942,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'}
 
@@ -6998,7 +12027,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.
@@ -7016,7 +12045,7 @@ sub upfile_select_html {
 #                 xml   => &mt('HTML/XML'),
                  );
     my $Str = '<input type="file" name="upfile" size="50" />'.
-        '<br />Type: <select name="upfiletype">';
+        '<br />'.&mt('Type').': <select name="upfiletype">';
     foreach my $type (sort(keys(%Types))) {
         $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
     }
@@ -7045,7 +12074,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
@@ -7057,12 +12086,12 @@ Apache Request ref, $records is an array
 ######################################################
 sub csv_print_samples {
     my ($r,$records) = @_;
-    my $samples = &get_samples($records,3);
+    my $samples = &get_samples($records,5);
 
     $r->print(&mt('Samples').'<br />'.&start_data_table().
               &start_data_table_header_row());
     foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { 
-        $r->print('<th>'.&mt('Column&nbsp;[_1]',($sample+1)).'</th>'); }
+        $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
     $r->print(&end_data_table_header_row());
     foreach my $hash (@$samples) {
 	$r->print(&start_data_table_row());
@@ -7081,7 +12110,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.
 
@@ -7104,15 +12133,15 @@ sub csv_print_select_table {
               &end_data_table_header_row()."\n");
     foreach my $array_ref (@$d) {
 	my ($value,$display,$defaultcol)=@{ $array_ref };
-	$r->print(&start_data_table_row().'<tr><td>'.$display.'</td>');
+	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
 
-	$r->print('<td><select name=f'.$i.
+	$r->print('<td><select name="f'.$i.'"'.
 		  ' onchange="javascript:flip(this.form,'.$i.');">');
 	$r->print('<option value="none"></option>');
 	foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
 	    $r->print('<option value="'.$sample.'"'.
                       ($sample eq $defaultcol ? ' selected="selected" ' : '').
-                      '>Column '.($sample+1).'</option>');
+                      '>'.&mt('Column [_1]',($sample+1)).'</option>');
 	}
 	$r->print('</select></td>'.&end_data_table_row()."\n");
 	$i++;
@@ -7127,7 +12156,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.
 
@@ -7143,7 +12172,8 @@ sub csv_samples_select_table {
     my ($r,$records,$d) = @_;
     my $i=0;
     #
-    my $samples = &get_samples($records,3);
+    my $max_samples = 5;
+    my $samples = &get_samples($records,$max_samples);
     $r->print(&start_data_table().
               &start_data_table_header_row().'<th>'.
               &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
@@ -7159,7 +12189,7 @@ sub csv_samples_select_table {
                       $display.'</option>');
 	}
 	$r->print('</select></td><td>');
-	foreach my $line (0..2) {
+	foreach my $line (0..($max_samples-1)) {
 	    if (defined($samples->[$line]{$key})) { 
 		$r->print($samples->[$line]{$key}."<br />\n"); 
 	    }
@@ -7177,7 +12207,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.
 
@@ -7196,7 +12226,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
 
@@ -7237,7 +12267,7 @@ sub check_if_partid_hidden {
 
 =over 4
 
-=item get_cgi_id
+=item * &get_cgi_id()
 
 Inputs: none
 
@@ -7261,7 +12291,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 
@@ -7396,7 +12426,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" />';
 }
 
@@ -7405,7 +12435,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 
@@ -7486,7 +12516,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" />';
 }
 
@@ -7495,7 +12525,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 
@@ -7588,7 +12618,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" />';
 }
 
@@ -7605,7 +12635,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.  
 
@@ -7644,9 +12674,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.
@@ -7720,7 +12750,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;
 }
 
@@ -7757,11 +12787,13 @@ sub restore_settings {
 
 =over 4
 
-=item &build_recipient_list
+=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.
+Build recipient lists for five types of e-mail:
+(a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
+(d) Help requests, (e) Course requests needing approval,  generated by
+lonerrorhandler.pm, CHECKRPMS, loncron, lonsupportreq.pm and
+loncoursequeueadmin.pm respectively.
 
 Inputs:
 defmail (scalar - email address of default recipient), 
@@ -7770,7 +12802,9 @@ defdom (domain for which to retrieve con
 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.   
+Returns: comma separated list of addresses to which to send e-mail.
+
+=back
 
 =cut
 
@@ -7783,23 +12817,29 @@ sub build_recipient_list {
     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);
+        if (exists($domconfig{'contacts'}{$mailing})) {
+            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'};
                 }
-                $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
             }
+        } elsif ($origmail ne '') {
+            push(@recipients,$origmail);
         }
     } elsif ($origmail ne '') {
         push(@recipients,$origmail);
     }
-    if ($defmail ne '') {
-        push(@recipients,$defmail);
+    if (defined($defmail)) {
+        if ($defmail ne '') {
+            push(@recipients,$defmail);
+        }
     }
     if ($otheremails) {
         my @others;
@@ -7821,19 +12861,370 @@ sub build_recipient_list {
 ############################################################
 ############################################################
 
+=pod
+
+=head1 Course Catalog Routines
+
+=over 4
+
+=item * &gather_categories()
+
+Converts category definitions - keys of categories hash stored in  
+coursecategories in configuration.db on the primary library server in a 
+domain - to an array.  Also generates javascript and idx hash used to 
+generate Domain Coordinator interface for editing Course Categories.
+
+Inputs:
+
+categories (reference to hash of category definitions).
+
+cats (reference to array of arrays/hashes which encapsulates hierarchy of
+      categories and subcategories).
+
+idx (reference to hash of counters used in Domain Coordinator interface for 
+      editing Course Categories).
+
+jsarray (reference to array of categories used to create Javascript arrays for
+         Domain Coordinator interface for editing Course Categories).
+
+Returns: nothing
+
+Side effects: populates cats, idx and jsarray. 
+
+=cut
+
+sub gather_categories {
+    my ($categories,$cats,$idx,$jsarray) = @_;
+    my %counters;
+    my $num = 0;
+    foreach my $item (keys(%{$categories})) {
+        my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
+        if ($container eq '' && $depth == 0) {
+            $cats->[$depth][$categories->{$item}] = $cat;
+        } else {
+            $cats->[$depth]{$container}[$categories->{$item}] = $cat;
+        }
+        my ($escitem,$tail) = split(/:/,$item,2);
+        if ($counters{$tail} eq '') {
+            $counters{$tail} = $num;
+            $num ++;
+        }
+        if (ref($idx) eq 'HASH') {
+            $idx->{$item} = $counters{$tail};
+        }
+        if (ref($jsarray) eq 'ARRAY') {
+            push(@{$jsarray->[$counters{$tail}]},$item);
+        }
+    }
+    return;
+}
+
+=pod
+
+=item * &extract_categories()
+
+Used to generate breadcrumb trails for course categories.
+
+Inputs:
+
+categories (reference to hash of category definitions).
+
+cats (reference to array of arrays/hashes which encapsulates hierarchy of
+      categories and subcategories).
+
+trails (reference to array of breacrumb trails for each category).
+
+allitems (reference to hash - key is category key 
+         (format: escaped(name):escaped(parent category):depth in hierarchy).
+
+idx (reference to hash of counters used in Domain Coordinator interface for
+      editing Course Categories).
+
+jsarray (reference to array of categories used to create Javascript arrays for
+         Domain Coordinator interface for editing Course Categories).
+
+subcats (reference to hash of arrays containing all subcategories within each 
+         category, -recursive)
+
+Returns: nothing
+
+Side effects: populates trails and allitems hash references.
+
+=cut
+
+sub extract_categories {
+    my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
+    if (ref($categories) eq 'HASH') {
+        &gather_categories($categories,$cats,$idx,$jsarray);
+        if (ref($cats->[0]) eq 'ARRAY') {
+            for (my $i=0; $i<@{$cats->[0]}; $i++) {
+                my $name = $cats->[0][$i];
+                my $item = &escape($name).'::0';
+                my $trailstr;
+                if ($name eq 'instcode') {
+                    $trailstr = &mt('Official courses (with institutional codes)');
+                } elsif ($name eq 'communities') {
+                    $trailstr = &mt('Communities');
+                } else {
+                    $trailstr = $name;
+                }
+                if ($allitems->{$item} eq '') {
+                    push(@{$trails},$trailstr);
+                    $allitems->{$item} = scalar(@{$trails})-1;
+                }
+                my @parents = ($name);
+                if (ref($cats->[1]{$name}) eq 'ARRAY') {
+                    for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
+                        my $category = $cats->[1]{$name}[$j];
+                        if (ref($subcats) eq 'HASH') {
+                            push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
+                        }
+                        &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
+                    }
+                } else {
+                    if (ref($subcats) eq 'HASH') {
+                        $subcats->{$item} = [];
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
+=pod
+
+=item *&recurse_categories()
+
+Recursively used to generate breadcrumb trails for course categories.
+
+Inputs:
+
+cats (reference to array of arrays/hashes which encapsulates hierarchy of
+      categories and subcategories).
+
+depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
+
+category (current course category, for which breadcrumb trail is being generated).
+
+trails (reference to array of breadcrumb trails for each category).
+
+allitems (reference to hash - key is category key
+         (format: escaped(name):escaped(parent category):depth in hierarchy).
+
+parents (array containing containers directories for current category, 
+         back to top level). 
+
+Returns: nothing
+
+Side effects: populates trails and allitems hash references
+
+=cut
+
+sub recurse_categories {
+    my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
+    my $shallower = $depth - 1;
+    if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
+        for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
+            my $name = $cats->[$depth]{$category}[$k];
+            my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
+            my $trailstr = join(' -&gt; ',(@{$parents},$category));
+            if ($allitems->{$item} eq '') {
+                push(@{$trails},$trailstr);
+                $allitems->{$item} = scalar(@{$trails})-1;
+            }
+            my $deeper = $depth+1;
+            push(@{$parents},$category);
+            if (ref($subcats) eq 'HASH') {
+                my $subcat = &escape($name).':'.$category.':'.$depth;
+                for (my $j=@{$parents}; $j>=0; $j--) {
+                    my $higher;
+                    if ($j > 0) {
+                        $higher = &escape($parents->[$j]).':'.
+                                  &escape($parents->[$j-1]).':'.$j;
+                    } else {
+                        $higher = &escape($parents->[$j]).'::'.$j;
+                    }
+                    push(@{$subcats->{$higher}},$subcat);
+                }
+            }
+            &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
+                                $subcats);
+            pop(@{$parents});
+        }
+    } else {
+        my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
+        my $trailstr = join(' -&gt; ',(@{$parents},$category));
+        if ($allitems->{$item} eq '') {
+            push(@{$trails},$trailstr);
+            $allitems->{$item} = scalar(@{$trails})-1;
+        }
+    }
+    return;
+}
+
+=pod
+
+=item *&assign_categories_table()
+
+Create a datatable for display of hierarchical categories in a domain,
+with checkboxes to allow a course to be categorized. 
+
+Inputs:
+
+cathash - reference to hash of categories defined for the domain (from
+          configuration.db)
+
+currcat - scalar with an & separated list of categories assigned to a course. 
+
+type    - scalar contains course type (Course or Community).
+
+Returns: $output (markup to be displayed) 
+
+=cut
+
+sub assign_categories_table {
+    my ($cathash,$currcat,$type) = @_;
+    my $output;
+    if (ref($cathash) eq 'HASH') {
+        my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
+        &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
+        $maxdepth = scalar(@cats);
+        if (@cats > 0) {
+            my $itemcount = 0;
+            if (ref($cats[0]) eq 'ARRAY') {
+                my @currcategories;
+                if ($currcat ne '') {
+                    @currcategories = split('&',$currcat);
+                }
+                my $table;
+                for (my $i=0; $i<@{$cats[0]}; $i++) {
+                    my $parent = $cats[0][$i];
+                    next if ($parent eq 'instcode');
+                    if ($type eq 'Community') {
+                        next unless ($parent eq 'communities');
+                    } else {
+                        next if ($parent eq 'communities');
+                    }
+                    my $css_class = $itemcount%2?' class="LC_odd_row"':'';
+                    my $item = &escape($parent).'::0';
+                    my $checked = '';
+                    if (@currcategories > 0) {
+                        if (grep(/^\Q$item\E$/,@currcategories)) {
+                            $checked = ' checked="checked"';
+                        }
+                    }
+                    my $parent_title = $parent;
+                    if ($parent eq 'communities') {
+                        $parent_title = &mt('Communities');
+                    }
+                    $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
+                              '<input type="checkbox" name="usecategory" value="'.
+                              $item.'"'.$checked.' />'.$parent_title.'</span>'.
+                              '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
+                    my $depth = 1;
+                    push(@path,$parent);
+                    $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
+                    pop(@path);
+                    $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
+                    $itemcount ++;
+                }
+                if ($itemcount) {
+                    $output = &Apache::loncommon::start_data_table().
+                              $table.
+                              &Apache::loncommon::end_data_table();
+                }
+            }
+        }
+    }
+    return $output;
+}
+
+=pod
+
+=item *&assign_category_rows()
+
+Create a datatable row for display of nested categories in a domain,
+with checkboxes to allow a course to be categorized,called recursively.
+
+Inputs:
+
+itemcount - track row number for alternating colors
+
+cats - reference to array of arrays/hashes which encapsulates hierarchy of
+      categories and subcategories.
+
+depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
+
+parent - parent of current category item
+
+path - Array containing all categories back up through the hierarchy from the
+       current category to the top level.
+
+currcategories - reference to array of current categories assigned to the course
+
+Returns: $output (markup to be displayed).
+
+=cut
+
+sub assign_category_rows {
+    my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
+    my ($text,$name,$item,$chgstr);
+    if (ref($cats) eq 'ARRAY') {
+        my $maxdepth = scalar(@{$cats});
+        if (ref($cats->[$depth]) eq 'HASH') {
+            if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
+                my $numchildren = @{$cats->[$depth]{$parent}};
+                my $css_class = $itemcount%2?' class="LC_odd_row"':'';
+                $text .= '<td><table class="LC_datatable">';
+                for (my $j=0; $j<$numchildren; $j++) {
+                    $name = $cats->[$depth]{$parent}[$j];
+                    $item = &escape($name).':'.&escape($parent).':'.$depth;
+                    my $deeper = $depth+1;
+                    my $checked = '';
+                    if (ref($currcategories) eq 'ARRAY') {
+                        if (@{$currcategories} > 0) {
+                            if (grep(/^\Q$item\E$/,@{$currcategories})) {
+                                $checked = ' checked="checked"';
+                            }
+                        }
+                    }
+                    $text .= '<tr><td><span class="LC_nobreak"><label>'.
+                             '<input type="checkbox" name="usecategory" value="'.
+                             $item.'"'.$checked.' />'.$name.'</label></span>'.
+                             '<input type="hidden" name="catname" value="'.$name.'" />'.
+                             '</td><td>';
+                    if (ref($path) eq 'ARRAY') {
+                        push(@{$path},$name);
+                        $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
+                        pop(@{$path});
+                    }
+                    $text .= '</td></tr>';
+                }
+                $text .= '</table></td>';
+            }
+        }
+    }
+    return $text;
+}
+
+############################################################
+############################################################
+
+
 sub commit_customrole {
-    my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_;
+    my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
     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(
-                 $udom,$uname,$url,$three,$four,$five,$end,$start).
+                 $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
                  '</b><br />';
     return $output;
 }
 
 sub commit_standardrole {
-    my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
+    my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
     my ($output,$logmsg,$linefeed);
     if ($context eq 'auto') {
         $linefeed = "\n";
@@ -7842,7 +13233,7 @@ sub commit_standardrole {
     }  
     if ($three eq 'st') {
         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
-                                         $one,$two,$sec,$context);
+                                         $one,$two,$sec,$context,$credits);
         if (($result =~ /^error/) || ($result eq 'not_in_class') || 
             ($result eq 'unknown_course') || ($result eq 'refused')) {
             $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
@@ -7862,7 +13253,7 @@ sub commit_standardrole {
         $output = &mt('Assigning').' '.$three.' in '.$url.
                ($start?', '.&mt('starting').' '.localtime($start):'').
                ($end?', '.&mt('ending').' '.localtime($end):'').': ';
-        my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start);
+        my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
         if ($context eq 'auto') {
             $output .= $result.$linefeed;
         } else {
@@ -7873,7 +13264,8 @@ sub commit_standardrole {
 }
 
 sub commit_studentrole {
-    my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
+    my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
+        $credits) = @_;
     my ($result,$linefeed,$oldsecurl,$newsecurl);
     if ($context eq 'auto') {
         $linefeed = "\n";
@@ -7897,7 +13289,7 @@ sub commit_studentrole {
                 }
                 $oldsecurl = $uurl;
                 $expire_role_result = 
-                    &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now);
+                    &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
                 if ($env{'request.course.sec'} ne '') { 
                     if ($expire_role_result eq 'refused') {
                         my @roles = ('st');
@@ -7920,7 +13312,11 @@ sub commit_studentrole {
             }
         }
         if (($expire_role_result eq 'ok') || ($secchange == 0)) {
-            $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid);
+            $modify_section_result = 
+                &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
+                                                           undef,undef,undef,$sec,
+                                                           $end,$start,'','',$cid,
+                                                           '',$context,$credits);
             if ($modify_section_result =~ /^ok/) {
                 if ($secchange == 1) {
                     if ($sec eq '') {
@@ -7942,7 +13338,7 @@ sub commit_studentrole {
                     }
                 }
             } else {
-                if ($secchange) {       
+                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;
@@ -7951,7 +13347,7 @@ sub commit_studentrole {
             $result = $modify_section_result;
         } elsif ($secchange == 1) {
             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;
+                $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_2] -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;
             }
@@ -7977,6 +13373,26 @@ sub commit_studentrole {
     return $result;
 }
 
+sub show_role_extent {
+    my ($scope,$context,$role) = @_;
+    $scope =~ s{^/}{};
+    my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
+    push(@courseroles,'co');
+    my @authorroles = &Apache::lonuserutils::roles_by_context('author');
+    if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
+        $scope =~ s{/}{_};
+        return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
+    } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
+        my ($audom,$auname) = split(/\//,$scope);
+        return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
+                   &Apache::loncommon::plainname($auname,$audom).'</span>');
+    } else {
+        $scope =~ s{/$}{};
+        return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
+                   &Apache::lonnet::domain($scope,'description').'</span>');
+    }
+}
+
 ############################################################
 ############################################################
 
@@ -7987,12 +13403,26 @@ sub check_clone {
     my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
     my $clonemsg;
     my $can_clone = 0;
-
+    my $lctype = lc($args->{'crstype'});
+    if ($lctype ne 'community') {
+        $lctype = 'course';
+    }
     if ($clonehome eq 'no_host') {
-        $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'});     
+        if ($args->{'crstype'} eq 'Community') {
+            $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
+        } else {
+            $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'}) {
+        if ($args->{'crstype'} eq 'Community') {
+            if ($clonedesc{'type'} ne 'Community') {
+                 $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
+                return ($can_clone, $clonemsg, $cloneid, $clonehome);
+            }
+        }
+	if (($env{'request.role.domain'} eq $args->{'clonedomain'}) && 
+            (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
 	    $can_clone = 1;
 	} else {
 	    my %clonehash = &Apache::lonnet::get('environment',['cloners'],
@@ -8003,15 +13433,25 @@ sub check_clone {
             } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
                 $can_clone = 1;
             } else {
+                my $ccrole = 'cc';
+                if ($args->{'crstype'} eq 'Community') {
+                    $ccrole = 'co';
+                }
 	        my %roleshash =
 		    &Apache::lonnet::get_my_roles($args->{'ccuname'},
 					 $args->{'ccdomain'},
-                                         'userroles',['active'],['cc'],
+                                         'userroles',['active'],[$ccrole],
 					 [$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'});
+	        if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
+                    $can_clone = 1;
+                } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},$args->{'ccuname'},$args->{'ccdomain'})) {
+                    $can_clone = 1;
+                } else {
+                    if ($args->{'crstype'} eq 'Community') {
+                        $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
+                    } 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'});
+                    }
 	        }
 	    }
         }
@@ -8020,7 +13460,7 @@ sub check_clone {
 }
 
 sub construct_course {
-    my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context) = @_;
+    my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category) = @_;
     my $outcome;
     my $linefeed =  '<br />'."\n";
     if ($context eq 'auto') {
@@ -8058,18 +13498,27 @@ sub construct_course {
                                              $args->{'crscode'},
                                              $args->{'ccuname'}.':'.
                                              $args->{'ccdomain'},
-                                             $args->{'crstype'});
+                                             $args->{'crstype'},
+                                             $cnum,$context,$category);
 
     # Note: The testing routines depend on this being output; see 
     # Utils::Course. This needs to at least be output as a comment
     # if anyone ever decides to not show this, and Utils::Course::new
     # will need to be suitably modified.
     $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
+    if ($$courseid =~ /^error:/) {
+        return (0,$outcome);
+    }
+
 #
 # Check if created correctly
 #
     ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
+    if ($crsuhome eq 'no_host') {
+        $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
+        return (0,$outcome);
+    }
     $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
 
 #
@@ -8083,19 +13532,33 @@ 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'};
-	}
+# Restore creation date, creator and creation context.
+        $cenv{'internal.created'}=$oldcenv{'internal.created'};
+        $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
+        $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
 # 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',
+                   'hidefromcat',
+                   'checkforpriv',
+                   'categories'],
+                   $$crsudom,$$crsunum);
     }
 
 #
@@ -8123,7 +13586,9 @@ sub construct_course {
     } else {
         $cenv{'internal.courseowner'} = $args->{'curruser'};
     }
-
+    if ($args->{'defaultcredits'}) {
+        $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
+    }
     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
     if ($args->{'crssections'}) {
         $cenv{'internal.sectionnums'} = '';
@@ -8148,6 +13613,11 @@ sub construct_course {
 # do not hide course coordinator from staff listing, 
 # even if privileged
     $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
+# add course coordinator's domain to domains to check for privileged users
+# if different to course domain
+    if ($$crsudom ne $args->{'ccdomain'}) {
+        $cenv{'checkforpriv'} = $args->{'ccdomain'};
+    }
 # add crosslistings
     if ($args->{'crsxlist'}) {
         $cenv{'internal.crosslistings'}='';
@@ -8323,10 +13793,10 @@ sub construct_course {
         $outcome .= ($fatal?$errtext:'read ok').' - ';
         my $title; my $url;
         if ($args->{'firstres'} eq 'syl') {
-	    $title='Syllabus';
+	    $title=&mt('Syllabus');
             $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
         } else {
-            $title='Navigate Contents';
+            $title=&mt('Table of Contents');
             $url='/adm/navmaps';
         }
 
@@ -8343,6 +13813,8 @@ sub construct_course {
 ############################################################
 ############################################################
 
+#SD
+# only Community and Course, or anything else?
 sub course_type {
     my ($cid) = @_;
     if (!defined($cid)) {
@@ -8359,11 +13831,21 @@ sub group_term {
     my $crstype = &course_type();
     my %names = (
                   'Course' => 'group',
-                  'Group' => 'team',
+                  'Community' => 'group',
                 );
     return $names{$crstype};
 }
 
+sub course_types {
+    my @types = ('official','unofficial','community');
+    my %typename = (
+                         official   => 'Official course',
+                         unofficial => 'Unofficial course',
+                         community  => 'Community',
+                   );
+    return (\@types,\%typename);
+}
+
 sub icon {
     my ($file)=@_;
     my $curfext = lc((split(/\./,$file))[-1]);
@@ -8380,28 +13862,14 @@ sub icon {
     return &lonhttpdurl($iconname);
 } 
 
-sub lonhttpd_port {
-    my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'};
-    if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; }
-    # IE doesn't like a secure page getting images from a non-secure
-    # port (when logging we haven't parsed the browser type so default
-    # back to secure
-    if ((!exists($env{'browser.type'}) || $env{'browser.type'} eq 'explorer')
-	&& $ENV{'SERVER_PORT'} == 443) {
-	return 443;
-    }
-    return $lonhttpd_port;
-
-}
-
 sub lonhttpdurl {
+#
+# Had been used for "small fry" static images on separate port 8080.
+# Modify here if lightweight http functionality desired again.
+# Currently eliminated due to increasing firewall issues.
+#
     my ($url)=@_;
-
-    my $lonhttpd_port = &lonhttpd_port();
-    if ($lonhttpd_port == 443) {
-	return 'https://'.$ENV{'SERVER_NAME'}.$url;
-    }
-    return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url;
+    return $url;
 }
 
 sub connection_aborted {
@@ -8437,7 +13905,23 @@ sub escape_url {
     return join('/',@urlslices).'/'.$lastitem;
 }
 
-# -------------------------------------------------------- Initliaze user login
+sub compare_arrays {
+    my ($arrayref1,$arrayref2) = @_;
+    my (@difference,%count);
+    @difference = ();
+    %count = ();
+    if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
+        foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
+        foreach my $element (keys(%count)) {
+            if ($count{$element} == 1) {
+                push(@difference,$element);
+            }
+        }
+    }
+    return @difference;
+}
+
+# -------------------------------------------------------- Initialize user login
 sub init_user_environment {
     my ($r, $username, $domain, $authhost, $form, $args) = @_;
     my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
@@ -8446,7 +13930,7 @@ sub init_user_environment {
 
 # See if old ID present, if so, remove
 
-    my ($filename,$cookie,$userroles);
+    my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
     my $now=time;
 
     if ($public) {
@@ -8479,52 +13963,35 @@ sub init_user_environment {
 	}
 # Give them a new cookie
 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
-		                   : $now);
+		                   : $now.$$.int(rand(10000)));
 	$cookie="$username\_$id\_$domain\_$authhost";
     
 # Initialize roles
 
-	$userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
+	($userroles,$firstaccenv,$timerintenv) = 
+            &Apache::lonnet::rolesinit($domain,$username,$authhost);
     }
 # ------------------------------------ Check browser type and MathML capability
 
     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
         $clientunicode,$clientos) = &decode_user_agent($r);
 
-# -------------------------------------- Any accessibility options to remember?
-    if (($form->{'interface'}) && ($form->{'remember'} eq 'true')) {
-	foreach my $option ('imagesuppress','appletsuppress',
-			    'embedsuppress','fontenhance','blackwhite') {
-	    if ($form->{$option} eq 'true') {
-		&Apache::lonnet::put('environment',{$option => 'on'},
-				     $domain,$username);
-	    } else {
-		&Apache::lonnet::del('environment',[$option],
-				     $domain,$username);
-	    }
-	}
-    }
 # ------------------------------------------------------------- Get environment
 
     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
     my ($tmp) = keys(%userenv);
     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
-	# default remote control to off
-	if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
     } else {
 	undef(%userenv);
     }
     if (($userenv{'interface'}) && (!$form->{'interface'})) {
 	$form->{'interface'}=$userenv{'interface'};
     }
-    $env{'environment.remote'}=$userenv{'remote'};
     if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
 
 # --------------- Do not trust query string to be put directly into environment
-    foreach my $option ('imagesuppress','appletsuppress',
-			'embedsuppress','fontenhance','blackwhite',
-			'interface','localpath','localres') {
-	$form->{$option}=~s/[\n\r\=]//gs;
+    foreach my $option ('interface','localpath','localres') {
+        $form->{$option}=~s/[\n\r\=]//gs;
     }
 # --------------------------------------------------------- Write first profile
 
@@ -8551,36 +14018,65 @@ sub init_user_environment {
 	    $initial_env{"browser.localres"}   = $form->{'localres'};
         }
 	
-	if ($public) {
-	    $initial_env{"environment.remote"} = "off";
-	}
 	if ($form->{'interface'}) {
 	    $form->{'interface'}=~s/\W//gs;
 	    $initial_env{"browser.interface"} = $form->{'interface'};
 	    $env{'browser.interface'}=$form->{'interface'};
-	    foreach my $option ('imagesuppress','appletsuppress',
-				'embedsuppress','fontenhance','blackwhite') {
-		if (($form->{$option} eq 'true') ||
-		    ($userenv{$option} eq 'on')) {
-		    $initial_env{"browser.$option"} = "on";
-		}
-	    }
 	}
 
+        my %is_adv = ( is_adv => $env{'user.adv'} );
+        my %domdef;
+        unless ($domain eq 'public') {
+            %domdef = &Apache::lonnet::get_domain_defaults($domain);
+        }
+
+        foreach my $tool ('aboutme','blog','webdav','portfolio') {
+            $userenv{'availabletools.'.$tool} = 
+                &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
+                                                  undef,\%userenv,\%domdef,\%is_adv);
+        }
+
+        foreach my $crstype ('official','unofficial','community') {
+            $userenv{'canrequest.'.$crstype} =
+                &Apache::lonnet::usertools_access($username,$domain,$crstype,
+                                                  'reload','requestcourses',
+                                                  \%userenv,\%domdef,\%is_adv);
+        }
+
+        $userenv{'canrequest.author'} =
+            &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
+                                        'reload','requestauthor',
+                                        \%userenv,\%domdef,\%is_adv);
+        my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
+                                             $domain,$username);
+        my $reqstatus = $reqauthor{'author_status'};
+        if ($reqstatus eq 'approval' || $reqstatus eq 'approved') { 
+            if (ref($reqauthor{'author'}) eq 'HASH') {
+                $userenv{'requestauthorqueued'} = $reqstatus.':'.
+                                                  $reqauthor{'author'}{'timestamp'};
+            }
+        }
+
 	$env{'user.environment'} = "$lonids/$cookie.id";
-	
+
 	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
 		 &GDBM_WRCREAT(),0640)) {
 	    &_add_to_env(\%disk_env,\%initial_env);
 	    &_add_to_env(\%disk_env,\%userenv,'environment.');
 	    &_add_to_env(\%disk_env,$userroles);
+            if (ref($firstaccenv) eq 'HASH') {
+                &_add_to_env(\%disk_env,$firstaccenv);
+            }
+            if (ref($timerintenv) eq 'HASH') {
+                &_add_to_env(\%disk_env,$timerintenv);
+            }
 	    if (ref($args->{'extra_env'})) {
 		&_add_to_env(\%disk_env,$args->{'extra_env'});
 	    }
 	    untie(%disk_env);
 	} else {
-	    &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ".
-			   'Could not create environment storage in lonauth: '.$!.'</font>');
+	    &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
+			   'Could not create environment storage in lonauth: '.$!.'</span>');
 	    return 'error: '.$!;
 	}
     }
@@ -8594,12 +14090,398 @@ sub init_user_environment {
 
 sub _add_to_env {
     my ($idf,$env_data,$prefix) = @_;
-    while (my ($key,$value) = each(%$env_data)) {
-	$idf->{$prefix.$key} = $value;
-	$env{$prefix.$key}   = $value;
+    if (ref($env_data) eq 'HASH') {
+        while (my ($key,$value) = each(%$env_data)) {
+	    $idf->{$prefix.$key} = $value;
+	    $env{$prefix.$key}   = $value;
+        }
+    }
+}
+
+# --- Get the symbolic name of a problem and the url
+sub get_symb {
+    my ($request,$silent) = @_;
+    (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
+    my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
+    if ($symb eq '') {
+        if (!$silent) {
+            if (ref($request)) { 
+                $request->print("Unable to handle ambiguous references:$url:.");
+            }
+            return ();
+        }
+    }
+    &Apache::lonenc::check_decrypt(\$symb);
+    return ($symb);
+}
+
+# --------------------------------------------------------------Get annotation
+
+sub get_annotation {
+    my ($symb,$enc) = @_;
+
+    my $key = $symb;
+    if (!$enc) {
+        $key =
+            &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
     }
+    my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
+    return $annotation{$key};
 }
 
+sub clean_symb {
+    my ($symb,$delete_enc) = @_;
+
+    &Apache::lonenc::check_decrypt(\$symb);
+    my $enc = $env{'request.enc'};
+    if ($delete_enc) {
+        delete($env{'request.enc'});
+    }
+
+    return ($symb,$enc);
+}
+
+sub build_release_hashes {
+    my ($checkparms,$checkresponsetypes,$checkcrstypes,$anonsurvey,$randomizetry) = @_;
+    return unless((ref($checkparms) eq 'HASH') && (ref($checkresponsetypes) eq 'HASH') &&
+                  (ref($checkcrstypes) eq 'HASH') && (ref($anonsurvey) eq 'HASH') &&
+                  (ref($randomizetry) eq 'HASH'));
+    foreach my $key (keys(%Apache::lonnet::needsrelease)) {
+        my ($item,$name,$value) = split(/:/,$key);
+        if ($item eq 'parameter') {
+            if (ref($checkparms->{$name}) eq 'ARRAY') {
+                unless(grep(/^\Q$name\E$/,@{$checkparms->{$name}})) {
+                    push(@{$checkparms->{$name}},$value);
+                }
+            } else {
+                push(@{$checkparms->{$name}},$value);
+            }
+        } elsif ($item eq 'resourcetag') {
+            if ($name eq 'responsetype') {
+                $checkresponsetypes->{$value} = $Apache::lonnet::needsrelease{$key}
+            }
+        } elsif ($item eq 'course') {
+            if ($name eq 'crstype') {
+                $checkcrstypes->{$value} = $Apache::lonnet::needsrelease{$key};
+            }
+        }
+    }
+    ($anonsurvey->{major},$anonsurvey->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:anonsurvey'});
+    ($randomizetry->{major},$randomizetry->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:randomizetry'});
+    return;
+}
+
+sub update_content_constraints {
+    my ($cdom,$cnum,$chome,$cid) = @_;
+    my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
+    my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
+    my %checkresponsetypes;
+    foreach my $key (keys(%Apache::lonnet::needsrelease)) {
+        my ($item,$name,$value) = split(/:/,$key);
+        if ($item eq 'resourcetag') {
+            if ($name eq 'responsetype') {
+                $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
+            }
+        }
+    }
+    my $navmap = Apache::lonnavmaps::navmap->new();
+    if (defined($navmap)) {
+        my %allresponses;
+        foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
+            my %responses = $res->responseTypes();
+            foreach my $key (keys(%responses)) {
+                next unless(exists($checkresponsetypes{$key}));
+                $allresponses{$key} += $responses{$key};
+            }
+        }
+        foreach my $key (keys(%allresponses)) {
+            my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
+            if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
+                ($reqdmajor,$reqdminor) = ($major,$minor);
+            }
+        }
+        undef($navmap);
+    }
+    unless (($reqdmajor eq '') && ($reqdminor eq '')) {
+        &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
+    }
+    return;
+}
+
+sub allmaps_incourse {
+    my ($cdom,$cnum,$chome,$cid) = @_;
+    if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
+        $cid = $env{'request.course.id'};
+        $cdom = $env{'course.'.$cid.'.domain'};
+        $cnum = $env{'course.'.$cid.'.num'};
+        $chome = $env{'course.'.$cid.'.home'};
+    }
+    my %allmaps = ();
+    my $lastchange =
+        &Apache::lonnet::get_coursechange($cdom,$cnum);
+    if ($lastchange > $env{'request.course.tied'}) {
+        my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
+        unless ($ferr) {
+            &update_content_constraints($cdom,$cnum,$chome,$cid);
+        }
+    }
+    my $navmap = Apache::lonnavmaps::navmap->new();
+    if (defined($navmap)) {
+        foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
+            $allmaps{$res->src()} = 1;
+        }
+    }
+    return \%allmaps;
+}
+
+sub parse_supplemental_title {
+    my ($title) = @_;
+
+    my ($foldertitle,$renametitle);
+    if ($title =~ /&amp;&amp;&amp;/) {
+        $title = &HTML::Entites::decode($title);
+    }
+    if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
+        $renametitle=$4;
+        my ($time,$uname,$udom) = ($1,$2,$3);
+        $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
+        my $name =  &plainname($uname,$udom);
+        $name = &HTML::Entities::encode($name,'"<>&\'');
+        $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
+        $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
+            $name.': <br />'.$foldertitle;
+    }
+    if (wantarray) {
+        return ($title,$foldertitle,$renametitle);
+    }
+    return $title;
+}
+
+sub symb_to_docspath {
+    my ($symb) = @_;
+    return unless ($symb);
+    my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
+    if ($resurl=~/\.(sequence|page)$/) {
+        $mapurl=$resurl;
+    } elsif ($resurl eq 'adm/navmaps') {
+        $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
+    }
+    my $mapresobj;
+    my $navmap = Apache::lonnavmaps::navmap->new();
+    if (ref($navmap)) {
+        $mapresobj = $navmap->getResourceByUrl($mapurl);
+    }
+    $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
+    my $type=$2;
+    my $path;
+    if (ref($mapresobj)) {
+        my $pcslist = $mapresobj->map_hierarchy();
+        if ($pcslist ne '') {
+            foreach my $pc (split(/,/,$pcslist)) {
+                next if ($pc <= 1);
+                my $res = $navmap->getByMapPc($pc);
+                if (ref($res)) {
+                    my $thisurl = $res->src();
+                    $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
+                    my $thistitle = $res->title();
+                    $path .= '&'.
+                             &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
+                             &Apache::lonhtmlcommon::entity_encode($thistitle).
+                             ':'.$res->randompick().
+                             ':'.$res->randomout().
+                             ':'.$res->encrypted().
+                             ':'.$res->randomorder().
+                             ':'.$res->is_page();
+                }
+            }
+        }
+        $path =~ s/^\&//;
+        my $maptitle = $mapresobj->title();
+        if ($mapurl eq 'default') {
+            $maptitle = 'Main Course Documents';
+        }
+        $path .= (($path ne '')? '&' : '').
+                 &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
+                 &Apache::lonhtmlcommon::entity_encode($maptitle).
+                 ':'.$mapresobj->randompick().
+                 ':'.$mapresobj->randomout().
+                 ':'.$mapresobj->encrypted().
+                 ':'.$mapresobj->randomorder().
+                 ':'.$mapresobj->is_page();
+    } else {
+        my $maptitle = &Apache::lonnet::gettitle($mapurl);
+        my $ispage = (($type eq 'page')? 1 : '');
+        if ($mapurl eq 'default') {
+            $maptitle = 'Main Course Documents';
+        }
+        $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
+                &Apache::lonhtmlcommon::entity_encode($maptitle).':::::'.$ispage;
+    }
+    unless ($mapurl eq 'default') {
+        $path = 'default&'.
+                &Apache::lonhtmlcommon::entity_encode('Main Course Documents').
+                ':::::&'.$path;
+    }
+    return $path;
+}
+
+sub captcha_display {
+    my ($context,$lonhost) = @_;
+    my ($output,$error);
+    my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
+    if ($captcha eq 'original') {
+        $output = &create_captcha();
+        unless ($output) {
+            $error = 'captcha'; 
+        }
+    } elsif ($captcha eq 'recaptcha') {
+        $output = &create_recaptcha($pubkey);
+        unless ($output) {
+            $error = 'recaptcha'; 
+        }
+    }
+    return ($output,$error);
+}
+
+sub captcha_response {
+    my ($context,$lonhost) = @_;
+    my ($captcha_chk,$captcha_error);
+    my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
+    if ($captcha eq 'original') {
+        ($captcha_chk,$captcha_error) = &check_captcha();
+    } elsif ($captcha eq 'recaptcha') {
+        $captcha_chk = &check_recaptcha($privkey);
+    } else {
+        $captcha_chk = 1;
+    }
+    return ($captcha_chk,$captcha_error);
+}
+
+sub get_captcha_config {
+    my ($context,$lonhost) = @_;
+    my ($captcha,$pubkey,$privkey,$hashtocheck);
+    my $hostname = &Apache::lonnet::hostname($lonhost);
+    my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
+    my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
+    if ($context eq 'usercreation') {
+        my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
+        if (ref($domconfig{$context}) eq 'HASH') {
+            $hashtocheck = $domconfig{$context}{'cancreate'};
+            if (ref($hashtocheck) eq 'HASH') {
+                if ($hashtocheck->{'captcha'} eq 'recaptcha') {
+                    if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
+                        $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
+                        $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
+                    }
+                    if ($privkey && $pubkey) {
+                        $captcha = 'recaptcha';
+                    } else {
+                        $captcha = 'original';
+                    }
+                } elsif ($hashtocheck->{'captcha'} ne 'notused') {
+                    $captcha = 'original';
+                }
+            }
+        } else {
+            $captcha = 'captcha';
+        }
+    } elsif ($context eq 'login') {
+        my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
+        if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
+            $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
+            $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
+            if ($privkey && $pubkey) {
+                $captcha = 'recaptcha';
+            } else {
+                $captcha = 'original';
+            }
+        } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
+            $captcha = 'original';
+        }
+    }
+    return ($captcha,$pubkey,$privkey);
+}
+
+sub create_captcha {
+    my %captcha_params = &captcha_settings();
+    my ($output,$maxtries,$tries) = ('',10,0);
+    while ($tries < $maxtries) {
+        $tries ++;
+        my $captcha = Authen::Captcha->new (
+                                           output_folder => $captcha_params{'output_dir'},
+                                           data_folder   => $captcha_params{'db_dir'},
+                                          );
+        my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
+
+        if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
+            $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
+                      &mt('Type in the letters/numbers shown below').'&nbsp;'.
+                     '<input type="text" size="5" name="code" value="" /><br />'.
+                     '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" />';
+            last;
+        }
+    }
+    return $output;
+}
+
+sub captcha_settings {
+    my %captcha_params = (
+                           output_dir     => $Apache::lonnet::perlvar{'lonCaptchaDir'},
+                           www_output_dir => "/captchaspool",
+                           db_dir         => $Apache::lonnet::perlvar{'lonCaptchaDb'},
+                           numchars       => '5',
+                         );
+    return %captcha_params;
+}
+
+sub check_captcha {
+    my ($captcha_chk,$captcha_error);
+    my $code = $env{'form.code'};
+    my $md5sum = $env{'form.crypt'};
+    my %captcha_params = &captcha_settings();
+    my $captcha = Authen::Captcha->new(
+                      output_folder => $captcha_params{'output_dir'},
+                      data_folder   => $captcha_params{'db_dir'},
+                  );
+    $captcha_chk = $captcha->check_code($code,$md5sum);
+    my %captcha_hash = (
+                        0       => 'Code not checked (file error)',
+                       -1      => 'Failed: code expired',
+                       -2      => 'Failed: invalid code (not in database)',
+                       -3      => 'Failed: invalid code (code does not match crypt)',
+    );
+    if ($captcha_chk != 1) {
+        $captcha_error = $captcha_hash{$captcha_chk}
+    }
+    return ($captcha_chk,$captcha_error);
+}
+
+sub create_recaptcha {
+    my ($pubkey) = @_;
+    my $captcha = Captcha::reCAPTCHA->new;
+    return $captcha->get_options_setter({theme => 'white'})."\n".
+           $captcha->get_html($pubkey).
+           &mt('If either word is hard to read, [_1] will replace them.',
+               '<image src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
+           '<br /><br />';
+}
+
+sub check_recaptcha {
+    my ($privkey) = @_;
+    my $captcha_chk;
+    my $captcha = Captcha::reCAPTCHA->new;
+    my $captcha_result =
+        $captcha->check_answer(
+                                $privkey,
+                                $ENV{'REMOTE_ADDR'},
+                                $env{'form.recaptcha_challenge_field'},
+                                $env{'form.recaptcha_response_field'},
+                              );
+    if ($captcha_result->{is_valid}) {
+        $captcha_chk = 1;
+    }
+    return $captcha_chk;
+}
 
 =pod