--- loncom/interface/loncommon.pm	2013/05/10 16:09:58	1.1127
+++ loncom/interface/loncommon.pm	2013/12/18 01:32:16	1.1163
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1127 2013/05/10 16:09:58 raeburn Exp $
+# $Id: loncommon.pm,v 1.1163 2013/12/18 01:32:16 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1388,22 +1388,23 @@ sub top_nav_help {
 
     return <<"END";
 $banner_link
- <a href="$link" title="$title">$text</a>
+<a href="$link" title="$title">$text</a>
 END
 }
 
 sub help_menu_js {
-    my ($text) = @_;
+    my ($httphost) = @_;
     my $stayOnPage = 1;
     my $width = 620;
     my $height = 600;
     my $helptopic=&general_help();
-    my $details_link = '/adm/help/'.$helptopic.'.hlp';
+    my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
     my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
     my $start_page =
         &Apache::loncommon::start_page('Help Menu', undef,
 				       {'frameset'    => 1,
 					'js_ready'    => 1,
+                                        'use_absolute' => $httphost,
 					'add_entries' => {
 					    'border' => '0',
 					    'rows'   => "110,*",},});
@@ -1749,8 +1750,6 @@ RESIZE
 
 =head1 Excel and CSV file utility routines
 
-=over 4
-
 =cut
 
 ###############################################################
@@ -1758,6 +1757,8 @@ RESIZE
 
 =pod
 
+=over 4
+
 =item * &csv_translate($text) 
 
 Translate $text to allow it to be output as a 'comma separated values' 
@@ -2349,6 +2350,10 @@ Outputs:
 
 =item * $clientos
 
+=item * $clientmobile
+
+=item * $clientinfo
+
 =back
 
 =back 
@@ -2367,6 +2372,7 @@ sub decode_user_agent {
     my $clientversion='0';
     my $clientmathml='';
     my $clientunicode='0';
+    my $clientmobile=0;
     for (my $i=0;$i<=$#browsertype;$i++) {
         my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
 	if (($httpbrowser=~/$match/i)  && ($httpbrowser!~/$notmatch/i)) {
@@ -2378,6 +2384,7 @@ sub decode_user_agent {
 	}
     }
     my $clientos='unknown';
+    my $clientinfo;
     if (($httpbrowser=~/linux/i) ||
         ($httpbrowser=~/unix/i) ||
         ($httpbrowser=~/ux/i) ||
@@ -2389,8 +2396,16 @@ sub decode_user_agent {
         ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
     if ($httpbrowser=~/win/i) { $clientos='win'; }
     if ($httpbrowser=~/embed/i) { $clientos='pda'; }
+    if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
+        $clientmobile=lc($1);
+    }
+    if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
+        $clientinfo = 'firefox-'.$1;
+    } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
+        $clientinfo = 'chromeframe-'.$1;
+    }
     return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
-            $clientunicode,$clientos,);
+            $clientunicode,$clientos,$clientmobile,$clientinfo);
 }
 
 ###############################################################
@@ -3060,6 +3075,8 @@ sub get_related_words {
 
 =pod
 
+=back
+
 =head1 Spell checking
 
 =over 4
@@ -3093,12 +3110,6 @@ Note: This sub assumes that aspell is in
 =cut
 
 
-=pod
-
-=back
-
-=cut
-
 sub check_spelling {
     my ($wordlist, $language) = @_;
     my @misspellings;
@@ -3314,7 +3325,7 @@ sub screenname {
 # ------------------------------------------------------------- Confirm Wrapper
 =pod
 
-=item confirmwrapper
+=item * &confirmwrapper($message)
 
 Wrap messages about completion of operation in box
 
@@ -4932,7 +4943,7 @@ sub designparm {
 
 Inputs: $url (usually will be undef).
 
-Returns: Path to Construction Space containing the resource or 
+Returns: Path to Authoring 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.
@@ -4995,7 +5006,7 @@ Input: (optional) filename from which br
        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
+         To be included on Authoring Space pages
 
 =cut
 
@@ -5026,7 +5037,7 @@ sub CSTR_pageheader {
     my $output =
          '<div>'
         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
-        .'<b>'.&mt('Construction Space:').'</b> '
+        .'<b>'.&mt('Authoring 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);
@@ -5114,6 +5125,7 @@ sub bodytag {
         $public = 1;
     }
     if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
+    my $httphost = $args->{'use_absolute'};
 
     $function = &get_users_function() if (!$function);
     my $img =    &designparm($function.'.img',$domain);
@@ -5155,16 +5167,14 @@ sub bodytag {
     my $bodytag = "<body $extra_body_attr>".
 	&Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
 
-    if ($bodyonly) {
+    &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
+
+    if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
         return $bodytag;
-    } 
+    }
 
-    my $name = &plainname($env{'user.name'},$env{'user.domain'});
     if ($public) {
 	undef($role);
-    } else {
-	$name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'},
-                                undef,'LC_menubuttons_link');
     }
     
     my $titleinfo = '<h1>'.$title.'</h1>';
@@ -5180,11 +5190,6 @@ sub bodytag {
     }
 
     $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; }
 
@@ -5192,25 +5197,25 @@ sub bodytag {
         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
         #    }
 
+        $bodytag .= Apache::lonhtmlcommon::scripttag(
+            Apache::lonmenu::utilityfunctions($httphost), 'start');
 
+        my ($left,$right) = Apache::lonmenu::primary_menu();
 
         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 />
+             $bodytag .= qq|<div id="LC_nav_bar">$left $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 .= qq|<div id="LC_nav_bar">$left $role</div>|;
         }
 
-        $bodytag .= Apache::lonhtmlcommon::scripttag(
-            Apache::lonmenu::utilityfunctions(), 'start');
-
-        $bodytag .= Apache::lonmenu::primary_menu();
+        $bodytag .= $right;
 
         if ($dc_info) {
             $dc_info = &dc_courseid_toggle($dc_info);
@@ -5219,7 +5224,7 @@ sub bodytag {
 
         #don't show menus for public users
         if (!$public){
-            $bodytag .= Apache::lonmenu::secondary_menu();
+            $bodytag .= Apache::lonmenu::secondary_menu($httphost);
             $bodytag .= Apache::lonmenu::serverform();
             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
             if ($env{'request.state'} eq 'construct') {
@@ -5279,7 +5284,7 @@ sub make_attr_string {
     }
 
     my $attr_string;
-    foreach my $attr (keys(%$attr_ref)) {
+    foreach my $attr (sort(keys(%$attr_ref))) {
 	$attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
     }
     return $attr_string;
@@ -5410,6 +5415,14 @@ form, .inline {
   vertical-align:middle;
 }
 
+.LC_floatleft {
+  float: left;
+}
+
+.LC_floatright {
+  float: right;
+}
+
 .LC_400Box {
   width:400px;
 }
@@ -6485,6 +6498,14 @@ div.LC_edit_problem_saves {
   white-space: nowrap;
 }
 
+.LC_edit_problem_latexhelper{
+    text-align: right;
+}
+
+#LC_edit_problem_colorful div{
+    margin-left: 40px;
+}
+
 img.stift {
   border-width: 0;
   vertical-align: middle;
@@ -6499,6 +6520,7 @@ div.LC_createcourse {
 }
 
 .LC_dccid {
+  float: right;
   margin: 0.2em 0 0 0;
   padding: 0;
   font-size: 90%;
@@ -6596,7 +6618,6 @@ fieldset > legend {
 }
 
 ol.LC_primary_menu {
-  float: right;
   margin: 0;
   padding: 0;
   background-color: $pgbg_or_bgcolor;
@@ -7250,6 +7271,7 @@ sub headtag {
     my $function = $args->{'function'} || &get_users_function();
     my $domain   = $args->{'domain'}   || &determinedomain();
     my $bgcolor  = $args->{'bgcolor'}  || &designparm($function.'.pgbg',$domain);
+    my $httphost = $args->{'use_absolute'};
     my $url = join(':',$env{'user.name'},$env{'user.domain'},
 		   $Apache::lonnet::perlvar{'lonVersion'},
 		   #time(),
@@ -7260,7 +7282,7 @@ sub headtag {
 
     my $result =
 	'<head>'.
-	&font_settings();
+	&font_settings($args);
 
     my $inhibitprint = &print_suppression();
 
@@ -7273,7 +7295,7 @@ sub headtag {
     if (!$args->{'no_nav_bar'} 
 	&& !$args->{'only_body'}
 	&& !$args->{'frameset'}) {
-	$result .= &help_menu_js();
+	$result .= &help_menu_js($httphost);
         $result.=&modal_window();
         $result.=&togglebox_script();
         $result.=&wishlist_window();
@@ -7311,6 +7333,11 @@ ADDMETA
 	.'<link rel="stylesheet" type="text/css" href="'.$url.'" />'
         .$inhibitprint
 	.$head_extra;
+    if ($env{'browser.mobile'}) {
+        $result .= '
+<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
+<meta name="apple-mobile-web-app-capable" content="yes" />';
+    }
     return $result.'</head>';
 }
 
@@ -7320,15 +7347,17 @@ ADDMETA
 
 Returns neccessary <meta> to set the proper encoding
 
-Inputs: none
+Inputs: optional reference to HASH -- $args passed to &headtag()
 
 =cut
 
 sub font_settings {
+    my ($args) = @_;
     my $headerstring='';
-    if (!$env{'browser.mathml'} && $env{'browser.unicode'}) {
+    if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
+        ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
 	$headerstring.=
-	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
+	    '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'."\n";
     }
     return $headerstring;
 }
@@ -7432,8 +7461,8 @@ 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 xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">';
+	$output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n"
+           .'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
     }
     return $output;
 }
@@ -7644,12 +7673,12 @@ var modalWindow = {
 		$(".LCmodal-overlay").click(function(){modalWindow.close();});
 	}
 };
-	var openMyModal = function(source,width,height,scrolling)
+	var openMyModal = function(source,width,height,scrolling,transparency,style)
 	{
 		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.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'>&lt/iframe>";
 		modalWindow.open();
 	};	
 // END LON-CAPA Internal -->
@@ -7659,16 +7688,18 @@ ENDMODAL
 }
 
 sub modal_link {
-    my ($link,$linktext,$width,$height,$target,$scrolling,$title)=@_;
+    my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
     unless ($width) { $width=480; }
     unless ($height) { $height=400; }
     unless ($scrolling) { $scrolling='yes'; }
+    unless ($transparency) { $transparency='true'; }
+
     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;">
+<a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">
            $linktext</a>
 ENDLINK
 }
@@ -7695,11 +7726,11 @@ 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.
+                 &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
+                 &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
+                 $content.
                  &end_scrollbox().
-               &end_page()
+                 &end_page()
              );
     return &modal_adhoc_script($funcname,$width,$height,$content);
 }
@@ -7818,11 +7849,9 @@ sub LCprogressbar {
     $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));
 }
@@ -7918,20 +7947,27 @@ sub validate_page {
 
 
 sub start_scrollbox {
-    my ($outerwidth,$width,$height,$id,$bgcolor)=@_;
+    my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
     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'";
+        $table_id = ' id="table_'.$id.'"';
+        $div_id = ' id="div_'.$id.'"';
     }
     if ($bgcolor ne '') {
         $tdcol = "background-color: $bgcolor;";
     }
+    my $nicescroll_js;
+    if ($env{'browser.mobile'}) {
+        $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
+    }
     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>
+$nicescroll_js
+
+<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
 }
 
@@ -7939,11 +7975,98 @@ sub end_scrollbox {
     return '</div></td></tr></table>';
 }
 
+sub nicescroll_javascript {
+    my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
+    my %options;
+    if (ref($cursor) eq 'HASH') {
+        %options = %{$cursor};
+    }
+    unless ($options{'railalign'} =~ /^left|right$/) {
+        $options{'railalign'} = 'left';
+    }
+    unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
+        my $function  = &get_users_function();
+        $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
+        unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
+            $options{'cursorcolor'} = '#00F';
+        }
+    }
+    if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
+        unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
+            $options{'cursoropacity'}='1.0';
+        }
+    } else {
+        $options{'cursoropacity'}='1.0';
+    }
+    if ($options{'cursorfixedheight'} eq 'none') {
+        delete($options{'cursorfixedheight'});
+    } else {
+        unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
+    }
+    unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
+        delete($options{'railoffset'});
+    }
+    my @niceoptions;
+    while (my($key,$value) = each(%options)) {
+        if ($value =~ /^\{.+\}$/) {
+            push(@niceoptions,$key.':'.$value);
+        } else {
+            push(@niceoptions,$key.':"'.$value.'"');
+        }
+    }
+    my $nicescroll_js = '
+$(document).ready(
+      function() {
+          $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
+      }
+);
+';
+    if ($framecheck) {
+        $nicescroll_js .= '
+function expand_div(caller) {
+    if (top === self) {
+        document.getElementById("'.$id.'").style.width = "auto";
+        document.getElementById("'.$id.'").style.height = "auto";
+    } else {
+        try {
+            if (parent.frames) {
+                if (parent.frames.length > 1) {
+                    var framesrc = parent.frames[1].location.href;
+                    var currsrc = framesrc.replace(/\#.*$/,"");
+                    if ((caller == "search") || (currsrc == "'.$location.'")) {
+                        document.getElementById("'.$id.'").style.width = "auto";
+                        document.getElementById("'.$id.'").style.height = "auto";
+                    }
+                }
+            }
+        } catch (e) {
+            return;
+        }
+    }
+    return;
+}
+';
+    }
+    if ($needjsready) {
+        $nicescroll_js = '
+<script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
+    } else {
+        $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
+    }
+    return $nicescroll_js;
+}
+
 sub simple_error_page {
-    my ($r,$title,$msg) = @_;
+    my ($r,$title,$msg,$args) = @_;
+    if (ref($args) eq 'HASH') {
+        if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
+    } else {
+        $msg = &mt($msg);
+    }
+
     my $page =
 	&Apache::loncommon::start_page($title).
-	'<p class="LC_error">'.&mt($msg).'</p>'.
+	'<p class="LC_error">'.$msg.'</p>'.
 	&Apache::loncommon::end_page();
     if (ref($r)) {
 	$r->print($page);
@@ -8562,14 +8685,19 @@ sub get_user_info {
 
 =item * &get_user_quota()
 
-Retrieves quota assigned for storage of portfolio files for a user  
+Retrieves quota assigned for storage of user files.
+Default is to report quota for portfolio files.
 
 Incoming parameters:
 1. user's username
 2. user's domain
+3. quota name - portfolio, author, or course
+   (if no quota name provided, defaults to portfolio).
+4. crstype - official, unofficial or community, if quota name is
+   course
 
 Returns:
-1. Disk quota (in Mb) assigned to student.
+1. Disk quota (in MB) assigned to student.
 2. (Optional) Type of setting: custom or default
    (individually assigned or default for user's 
    institutional status).
@@ -8580,7 +8708,7 @@ Returns:
 
 If a value has been stored in the user's environment, 
 it will return that, otherwise it returns the maximal default
-defined for the user's instituional status(es) in the domain.
+defined for the user's institutional status(es) in the domain.
 
 =cut
 
@@ -8588,7 +8716,7 @@ defined for the user's instituional stat
 
 
 sub get_user_quota {
-    my ($uname,$udom) = @_;
+    my ($uname,$udom,$quotaname,$crstype) = @_;
     my ($quota,$quotatype,$settingstatus,$defquota);
     if (!defined($udom)) {
         $udom = $env{'user.domain'};
@@ -8603,27 +8731,57 @@ sub get_user_quota {
         $defquota = 0; 
     } else {
         my $inststatus;
-        if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
-            $quota = $env{'environment.portfolioquota'};
-            $inststatus = $env{'environment.inststatus'};
-        } else {
-            my %userenv = 
-                &Apache::lonnet::get('environment',['portfolioquota',
-                                     'inststatus'],$udom,$uname);
-            my ($tmp) = keys(%userenv);
-            if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
-                $quota = $userenv{'portfolioquota'};
-                $inststatus = $userenv{'inststatus'};
-            } else {
-                undef(%userenv);
-            }
-        }
-        ($defquota,$settingstatus) = &default_quota($udom,$inststatus);
-        if ($quota eq '') {
-            $quota = $defquota;
-            $quotatype = 'default';
+        if ($quotaname eq 'course') {
+            if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
+                ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
+                $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
+            } else {
+                my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
+                $quota = $cenv{'internal.uploadquota'};
+            }
         } else {
-            $quotatype = 'custom';
+            if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
+                if ($quotaname eq 'author') {
+                    $quota = $env{'environment.authorquota'};
+                } else {
+                    $quota = $env{'environment.portfolioquota'};
+                }
+                $inststatus = $env{'environment.inststatus'};
+            } else {
+                my %userenv = 
+                    &Apache::lonnet::get('environment',['portfolioquota',
+                                         'authorquota','inststatus'],$udom,$uname);
+                my ($tmp) = keys(%userenv);
+                if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
+                    if ($quotaname eq 'author') {
+                        $quota = $userenv{'authorquota'};
+                    } else {
+                        $quota = $userenv{'portfolioquota'};
+                    }
+                    $inststatus = $userenv{'inststatus'};
+                } else {
+                    undef(%userenv);
+                }
+            }
+        }
+        if ($quota eq '' || wantarray) {
+            if ($quotaname eq 'course') {
+                my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
+                if (($crstype eq 'official') || ($crstype eq 'unofficial') || ($crstype eq 'community')) { 
+                    $defquota = $domdefs{$crstype.'quota'};
+                }
+                if ($defquota eq '') {
+                    $defquota = 500;
+                }
+            } else {
+                ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
+            }
+            if ($quota eq '') {
+                $quota = $defquota;
+                $quotatype = 'default';
+            } else {
+                $quotatype = 'custom';
+            }
         }
     }
     if (wantarray) {
@@ -8643,54 +8801,60 @@ Retrieves default quota assigned for sto
 given an (optional) user's institutional status.
 
 Incoming parameters:
+
 1. domain
 2. (Optional) institutional status(es).  This is a : separated list of 
    status types (e.g., faculty, staff, student etc.)
    which apply to the user for whom the default is being retrieved.
    If the institutional status string in undefined, the domain
-   default quota will be returned. 
+   default quota will be returned.
+3.  quota name - portfolio, author, or course
+   (if no quota name provided, defaults to portfolio).
 
 Returns:
-1. Default disk quota (in Mb) for user portfolios in the domain.
+
+1. Default disk quota (in MB) for user portfolios in the domain.
 2. (Optional) institutional type which determined the value of the
    default quota.
 
 If a value has been stored in the domain's configuration db,
 it will return that, otherwise it returns 20 (for backwards 
 compatibility with domains which have not set up a configuration
-db file; the original statically defined portfolio quota was 20 Mb). 
+db file; the original statically defined portfolio quota was 20 MB). 
 
 If the user's status includes multiple types (e.g., staff and student),
 the largest default quota which applies to the user determines the
 default quota returned.
 
-=back
-
 =cut
 
 ###############################################
 
 
 sub default_quota {
-    my ($udom,$inststatus) = @_;
+    my ($udom,$inststatus,$quotaname) = @_;
     my ($defquota,$settingstatus);
     my %quotahash = &Apache::lonnet::get_dom('configuration',
                                             ['quotas'],$udom);
+    my $key = 'defaultquota';
+    if ($quotaname eq 'author') {
+        $key = 'authorquota';
+    }
     if (ref($quotahash{'quotas'}) eq 'HASH') {
         if ($inststatus ne '') {
             my @statuses = map { &unescape($_); } split(/:/,$inststatus);
             foreach my $item (@statuses) {
-                if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
-                    if ($quotahash{'quotas'}{'defaultquota'}{$item} ne '') {
+                if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
+                    if ($quotahash{'quotas'}{$key}{$item} ne '') {
                         if ($defquota eq '') {
-                            $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
+                            $defquota = $quotahash{'quotas'}{$key}{$item};
                             $settingstatus = $item;
-                        } elsif ($quotahash{'quotas'}{'defaultquota'}{$item} > $defquota) {
-                            $defquota = $quotahash{'quotas'}{'defaultquota'}{$item};
+                        } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
+                            $defquota = $quotahash{'quotas'}{$key}{$item};
                             $settingstatus = $item;
                         }
                     }
-                } else {
+                } elsif ($key eq 'defaultquota') {
                     if ($quotahash{'quotas'}{$item} ne '') {
                         if ($defquota eq '') {
                             $defquota = $quotahash{'quotas'}{$item};
@@ -8704,16 +8868,25 @@ sub default_quota {
             }
         }
         if ($defquota eq '') {
-            if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') {
-                $defquota = $quotahash{'quotas'}{'defaultquota'}{'default'};
-            } else {
+            if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
+                $defquota = $quotahash{'quotas'}{$key}{'default'};
+            } elsif ($key eq 'defaultquota') {
                 $defquota = $quotahash{'quotas'}{'default'};
             }
             $settingstatus = 'default';
+            if ($defquota eq '') {
+                if ($quotaname eq 'author') {
+                    $defquota = 500;
+                }
+            }
         }
     } else {
         $settingstatus = 'default';
-        $defquota = 20;
+        if ($quotaname eq 'author') {
+            $defquota = 500;
+        } else {
+            $defquota = 20;
+        }
     }
     if (wantarray) {
         return ($defquota,$settingstatus);
@@ -8722,6 +8895,63 @@ sub default_quota {
     }
 }
 
+###############################################
+
+=pod
+
+=item * &excess_filesize_warning()
+
+Returns warning message if upload of file to authoring space, or copying
+of existing file within authoring space will cause quota for the authoring
+space to be exceeded.
+
+Same, if upload of a file directly to a course/community via Course Editor
+will cause quota for uploaded content for the course to be exceeded.
+
+Inputs: 6
+1. username or coursenum
+2. domain
+3. context ('author' or 'course')
+4. filename of file for which action is being requested
+5. filesize (kB) of file
+6. action being taken: copy or upload.
+
+Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
+         otherwise return null.
+
+=back
+
+=cut
+
+sub excess_filesize_warning {
+    my ($uname,$udom,$context,$filename,$filesize,$action) = @_;
+    my $current_disk_usage = 0;
+    my $disk_quota = &get_user_quota($uname,$udom,$context); #expressed in MB
+    if ($context eq 'author') {
+        my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
+        $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
+    } else {
+        foreach my $subdir ('docs','supplemental') {
+            $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
+        }
+    }
+    $disk_quota = int($disk_quota * 1000);
+    if (($current_disk_usage + $filesize) > $disk_quota) {
+        return '<p><span class="LC_warning">'.
+                &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
+                    '<span class="LC_filename">'.$filename.'</span>',$filesize).'</span>'.
+               '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
+                            $disk_quota,$current_disk_usage).
+               '</p>';
+    }
+    return;
+}
+
+###############################################
+
+
+
+
 sub get_secgrprole_info {
     my ($cdom,$cnum,$needroles,$type)  = @_;
     my %sections_count = &get_sections($cdom,$cnum);
@@ -9561,11 +9791,10 @@ sub ask_for_embedded_content {
     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);
+        $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
     my $heading = &mt('Upload embedded files');
     my $buttontext = &mt('Upload');
 
-    my ($navmap,$cdom,$cnum);
     if ($env{'request.course.id'}) {
         if ($actionurl eq '/adm/dependencies') {
             $navmap = Apache::lonnavmaps::navmap->new();
@@ -9649,17 +9878,18 @@ sub ask_for_embedded_content {
         } else {
             $embed_file = $file;
         }
-        my $absolutepath;
+        my ($absolutepath,$cleaned_file);
         if ($embed_file =~ m{^\w+://}) {
-            $newfiles{$embed_file} = 1;
-            $mapping{$embed_file} = $embed_file;
+            $cleaned_file = $embed_file;
+            $newfiles{$cleaned_file} = 1;
+            $mapping{$cleaned_file} = $embed_file;
         } else {
+            $cleaned_file = &clean_path($embed_file);
             if ($embed_file =~ m{^/}) {
                 $absolutepath = $embed_file;
-                $embed_file =~ s{^(/+)}{};
             }
-            if ($embed_file =~ m{/}) {
-                my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
+            if ($cleaned_file =~ m{/}) {
+                my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
                 $path = &check_for_traversal($path,$url,$toplevel);
                 my $item = $fname;
                 if ($path ne '') {
@@ -9676,9 +9906,9 @@ sub ask_for_embedded_content {
             } else {
                 $dependencies{$embed_file} = 1;
                 if ($absolutepath) {
-                    $mapping{$embed_file} = $absolutepath;
+                    $mapping{$cleaned_file} = $absolutepath;
                 } else {
-                    $mapping{$embed_file} = $embed_file;
+                    $mapping{$cleaned_file} = $embed_file;
                 }
             }
         }
@@ -10052,6 +10282,46 @@ sub ask_for_embedded_content {
     return ($output,$counter,$numpathchg);
 }
 
+=pod
+
+=item * clean_path($name)
+
+Performs clean-up of directories, subdirectories and filename in an
+embedded object, referenced in an HTML file which is being uploaded
+to a course or portfolio, where 
+"Upload embedded images/multimedia files if HTML file" checkbox was
+checked.
+
+Clean-up is similar to replacements in lonnet::clean_filename()
+except each / between sub-directory and next level is preserved.
+
+=cut
+
+sub clean_path {
+    my ($embed_file) = @_;
+    $embed_file =~s{^/+}{};
+    my @contents;
+    if ($embed_file =~ m{/}) {
+        @contents = split(/\//,$embed_file);
+    } else {
+        @contents = ($embed_file);
+    }
+    my $lastidx = scalar(@contents)-1;
+    for (my $i=0; $i<=$lastidx; $i++) { 
+        $contents[$i]=~s{\\}{/}g;
+        $contents[$i]=~s/\s+/\_/g;
+        $contents[$i]=~s{[^/\w\.\-]}{}g;
+        if ($i == $lastidx) {
+            $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
+        }
+    }
+    if ($lastidx > 0) {
+        return join('/',@contents);
+    } else {
+        return $contents[0];
+    }
+}
+
 sub embedded_file_element {
     my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
     return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
@@ -10176,7 +10446,8 @@ sub upload_embedded {
         # 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 />';
+            $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
+                      .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
             next;
         } elsif (($fname =~ /\.(\w+)$/) &&
                  (!defined(&Apache::loncommon::fileembstyle($1)))) {
@@ -10440,6 +10711,7 @@ sub modify_html_refs {
                         my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
                         $count += $numchg;
                         $allfiles{$newname} = $allfiles{$ref};
+                        delete($allfiles{$ref});
                     }
                     if ($env{'form.embedded_codebase_'.$i} ne '') {
                         $codebase = &unescape($env{'form.embedded_codebase_'.$i});
@@ -11800,7 +12072,7 @@ sub cleanup_empty_dirs {
 
 =pod
 
-=item &get_folder_hierarchy()
+=item * &get_folder_hierarchy()
 
 Provides hierarchy of names of folders/sub-folders containing the current
 item,
@@ -11828,7 +12100,7 @@ sub get_folder_hierarchy {
                 my @pcs = split(/,/,$pcslist);
                 foreach my $pc (@pcs) {
                     if ($pc == 1) {
-                        push(@pathitems,&mt('Main Course Documents'));
+                        push(@pathitems,&mt('Main Content'));
                     } else {
                         my $res = $navmap->getByMapPc($pc);
                         if (ref($res)) {
@@ -11843,7 +12115,7 @@ sub get_folder_hierarchy {
             }
             if ($showitem) {
                 if ($mapres->{ID} eq '0.0') {
-                    push(@pathitems,&mt('Main Course Documents'));
+                    push(@pathitems,&mt('Main Content'));
                 } else {
                     my $maptitle = $mapres->compTitle();
                     $maptitle =~ s/\W+/_/g;
@@ -11910,6 +12182,9 @@ sub get_turnedin_filepath {
                             my $title = $res->compTitle();
                             $title =~ s/\W+/_/g;
                             if ($title ne '') {
+                                if (($pc > 1) && (length($title) > 12)) {
+                                    $title = substr($title,0,12);
+                                }
                                 push(@pathitems,$title);
                             }
                         }
@@ -11918,6 +12193,9 @@ sub get_turnedin_filepath {
                 my $maptitle = $mapres->compTitle();
                 $maptitle =~ s/\W+/_/g;
                 if ($maptitle ne '') {
+                    if (length($maptitle) > 12) {
+                        $maptitle = substr($maptitle,0,12);
+                    }
                     push(@pathitems,$maptitle);
                 }
                 unless ($env{'request.state'} eq 'construct') {
@@ -11958,6 +12236,9 @@ sub get_turnedin_filepath {
                 $restitle = time;
             }
         }
+        if (length($restitle) > 12) {
+            $restitle = substr($restitle,0,12);
+        }
         push(@pathitems,$restitle);
         $path .= join('/',@pathitems);
     }
@@ -12895,16 +13176,20 @@ sub restore_settings {
 
 =item * &build_recipient_list()
 
-Build recipient lists for five types of e-mail:
+Build recipient lists for following 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.
+(d) Help requests, (e) Course requests needing approval, (f) loncapa
+module change checking, student/employee ID conflict checks, as
+generated by lonerrorhandler.pm, CHECKRPMS, loncron,
+lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
 
 Inputs:
 defmail (scalar - email address of default recipient), 
-mailing type (scalar - errormail, packagesmail, or helpdeskmail), 
+mailing type (scalar: errormail, packagesmail, helpdeskmail,
+requestsmail, updatesmail, or idconflictsmail).
+
 defdom (domain for which to retrieve configuration settings),
+
 origmail (scalar - email address of recipient from loncapa.conf, 
 i.e., predates configuration by DC via domainprefs.pm 
 
@@ -13100,7 +13385,7 @@ sub extract_categories {
 
 =pod
 
-=item *&recurse_categories()
+=item * &recurse_categories()
 
 Recursively used to generate breadcrumb trails for course categories.
 
@@ -13171,7 +13456,7 @@ sub recurse_categories {
 
 =pod
 
-=item *&assign_categories_table()
+=item * &assign_categories_table()
 
 Create a datatable for display of hierarchical categories in a domain,
 with checkboxes to allow a course to be categorized. 
@@ -13248,7 +13533,7 @@ sub assign_categories_table {
 
 =pod
 
-=item *&assign_category_rows()
+=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.
@@ -13282,7 +13567,7 @@ sub assign_category_rows {
             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">';
+                $text .= '<td><table class="LC_data_table">';
                 for (my $j=0; $j<$numchildren; $j++) {
                     $name = $cats->[$depth]{$parent}[$j];
                     $item = &escape($name).':'.&escape($parent).':'.$depth;
@@ -14080,7 +14365,7 @@ sub init_user_environment {
 # ------------------------------------ Check browser type and MathML capability
 
     my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
-        $clientunicode,$clientos) = &decode_user_agent($r);
+        $clientunicode,$clientos,$clientmobile,$clientinfo) = &decode_user_agent($r);
 
 # ------------------------------------------------------------- Get environment
 
@@ -14111,6 +14396,8 @@ sub init_user_environment {
 	     "browser.mathml"     => $clientmathml,
 	     "browser.unicode"    => $clientunicode,
 	     "browser.os"         => $clientos,
+             "browser.mobile"     => $clientmobile,
+             "browser.info"       => $clientinfo,
 	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
 	     "request.course.fn"  => '',
 	     "request.course.uri" => '',
@@ -14130,6 +14417,12 @@ sub init_user_environment {
 	    $env{'browser.interface'}=$form->{'interface'};
 	}
 
+        if ($form->{'iptoken'}) {
+            my $lonhost = $r->dir_config('lonHostID');
+            $initial_env{"user.noloadbalance"} = $lonhost;
+            $env{'user.noloadbalance'} = $lonhost;
+        }
+
         my %is_adv = ( is_adv => $env{'user.adv'} );
         my %domdef;
         unless ($domain eq 'public') {
@@ -14363,6 +14656,30 @@ sub parse_supplemental_title {
     return $title;
 }
 
+sub recurse_supplemental {
+    my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_;
+    if ($suppmap) {
+        my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
+        if ($fatal) {
+            $errors ++;
+        } else {
+            if ($#LONCAPA::map::resources > 0) {
+                foreach my $res (@LONCAPA::map::resources) {
+                    my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
+                    if (($src ne '') && ($status eq 'res')) {
+                        if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
+                            ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors);
+                        } else {
+                            $numfiles ++;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return ($numfiles,$errors);
+}
+
 sub symb_to_docspath {
     my ($symb) = @_;
     return unless ($symb);
@@ -14392,7 +14709,7 @@ sub symb_to_docspath {
                     my $thistitle = $res->title();
                     $path .= '&'.
                              &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
-                             &Apache::lonhtmlcommon::entity_encode($thistitle).
+                             &escape($thistitle).
                              ':'.$res->randompick().
                              ':'.$res->randomout().
                              ':'.$res->encrypted().
@@ -14404,11 +14721,11 @@ sub symb_to_docspath {
         $path =~ s/^\&//;
         my $maptitle = $mapresobj->title();
         if ($mapurl eq 'default') {
-            $maptitle = 'Main Course Documents';
+            $maptitle = 'Main Content';
         }
         $path .= (($path ne '')? '&' : '').
                  &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
-                 &Apache::lonhtmlcommon::entity_encode($maptitle).
+                 &escape($maptitle).
                  ':'.$mapresobj->randompick().
                  ':'.$mapresobj->randomout().
                  ':'.$mapresobj->encrypted().
@@ -14418,14 +14735,14 @@ sub symb_to_docspath {
         my $maptitle = &Apache::lonnet::gettitle($mapurl);
         my $ispage = (($type eq 'page')? 1 : '');
         if ($mapurl eq 'default') {
-            $maptitle = 'Main Course Documents';
+            $maptitle = 'Main Content';
         }
         $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
-                &Apache::lonhtmlcommon::entity_encode($maptitle).':::::'.$ispage;
+                &escape($maptitle).':::::'.$ispage;
     }
     unless ($mapurl eq 'default') {
         $path = 'default&'.
-                &Apache::lonhtmlcommon::entity_encode('Main Course Documents').
+                &escape('Main Content').
                 ':::::&'.$path;
     }
     return $path;
@@ -14564,11 +14881,15 @@ sub check_captcha {
 
 sub create_recaptcha {
     my ($pubkey) = @_;
+    my $use_ssl;
+    if ($ENV{'SERVER_PORT'} == 443) {
+        $use_ssl = 1;
+    }
     my $captcha = Captcha::reCAPTCHA->new;
     return $captcha->get_options_setter({theme => 'white'})."\n".
-           $captcha->get_html($pubkey).
+           $captcha->get_html($pubkey,undef,$use_ssl).
            &mt('If either word is hard to read, [_1] will replace them.',
-               '<image src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
+               '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
            '<br /><br />';
 }
 
@@ -14589,6 +14910,28 @@ sub check_recaptcha {
     return $captcha_chk;
 }
 
+sub cleanup_html {
+    my ($incoming) = @_;
+    my $outgoing;
+    if ($incoming ne '') {
+        $outgoing = $incoming;
+        $outgoing =~ s/;/&#059;/g;
+        $outgoing =~ s/\#/&#035;/g;
+        $outgoing =~ s/\&/&#038;/g;
+        $outgoing =~ s/</&#060;/g;
+        $outgoing =~ s/>/&#062;/g;
+        $outgoing =~ s/\(/&#040/g;
+        $outgoing =~ s/\)/&#041;/g;
+        $outgoing =~ s/"/&#034;/g;
+        $outgoing =~ s/'/&#039;/g;
+        $outgoing =~ s/\$/&#036;/g;
+        $outgoing =~ s{/}{&#047;}g;
+        $outgoing =~ s/=/&#061;/g;
+        $outgoing =~ s/\\/&#092;/g
+    }
+    return $outgoing;
+}
+
 =pod
 
 =back