--- loncom/interface/loncommon.pm 2013/04/11 15:30:37 1.1121 +++ loncom/interface/loncommon.pm 2013/07/22 11:46:23 1.1141 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.1121 2013/04/11 15:30:37 raeburn Exp $ +# $Id: loncommon.pm,v 1.1141 2013/07/22 11:46:23 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -2349,6 +2349,10 @@ Outputs: =item * $clientos +=item * $clientmobile + +=item * $clientinfo + =back =back @@ -2367,6 +2371,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 +2383,7 @@ sub decode_user_agent { } } my $clientos='unknown'; + my $clientinfo; if (($httpbrowser=~/linux/i) || ($httpbrowser=~/unix/i) || ($httpbrowser=~/ux/i) || @@ -2389,8 +2395,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); } ############################################################### @@ -4932,7 +4946,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 +5009,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 +5040,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); @@ -5155,16 +5169,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 +5192,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 +5199,25 @@ sub bodytag { # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls # } + $bodytag .= Apache::lonhtmlcommon::scripttag( + Apache::lonmenu::utilityfunctions(), '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); @@ -5410,6 +5417,14 @@ form, .inline { vertical-align:middle; } +.LC_floatleft { + float: left; +} + +.LC_floatright { + float: right; +} + .LC_400Box { width:400px; } @@ -6480,6 +6495,11 @@ div.LC_edit_problem_saves { padding-bottom: 5px; } +.LC_edit_opt { + padding-left: 1em; + white-space: nowrap; +} + img.stift { border-width: 0; vertical-align: middle; @@ -6494,6 +6514,7 @@ div.LC_createcourse { } .LC_dccid { + float: right; margin: 0.2em 0 0 0; padding: 0; font-size: 90%; @@ -6591,7 +6612,6 @@ fieldset > legend { } ol.LC_primary_menu { - float: right; margin: 0; padding: 0; background-color: $pgbg_or_bgcolor; @@ -7306,6 +7326,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>'; } @@ -7639,12 +7664,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 + "'></iframe>"; + modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>"; modalWindow.open(); }; // END LON-CAPA Internal --> @@ -7654,16 +7679,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 } @@ -7690,11 +7717,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); } @@ -7813,11 +7840,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)); } @@ -7913,20 +7938,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 } @@ -7934,6 +7966,87 @@ 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 $page = @@ -8557,11 +8670,16 @@ 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. @@ -8575,7 +8693,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 @@ -8583,7 +8701,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'}; @@ -8598,27 +8716,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,7 +8791,9 @@ Incoming parameters: 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. @@ -8667,25 +8817,29 @@ default quota returned. 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}; @@ -8699,16 +8853,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); @@ -8717,6 +8880,61 @@ 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. + +=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); @@ -9560,18 +9778,23 @@ sub ask_for_embedded_content { my $heading = &mt('Upload embedded files'); my $buttontext = &mt('Upload'); - my $navmap; + my ($navmap,$cdom,$cnum); if ($env{'request.course.id'}) { - $navmap = Apache::lonnavmaps::navmap->new(); + if ($actionurl eq '/adm/dependencies') { + $navmap = Apache::lonnavmaps::navmap->new(); + } + $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; } - if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { + 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'}; + $udom = $cdom; + $uname = $cnum; $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio'; } else { $udom = $env{'user.domain'}; @@ -9603,24 +9826,42 @@ sub ask_for_embedded_content { } } 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"; + $toplevel = $url; + unless ($toplevel =~ m{^/}) { + $toplevel = "/$url"; + } ($rem) = ($toplevel =~ m{^(.+/)[^/]+$}); - ($path) = - ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/}); + if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) { + $path = $1; + } else { + ($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})) { + } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") { + $udom = $cdom; + $uname = $cnum; + $url = "/uploaded/$cdom/$cnum/portfolio/syllabus"; + $toplevel = $url; + $path = $url; + $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/'; + $fileloc =~ s{^/}{}; + } + foreach my $file (keys(%{$allfiles})) { + my $embed_file; + if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) { + $embed_file = $1; + } else { + $embed_file = $file; + } my $absolutepath; if ($embed_file =~ m{^\w+://}) { $newfiles{$embed_file} = 1; @@ -9658,7 +9899,8 @@ sub ask_for_embedded_content { my $dirptr = 16384; foreach my $path (keys(%subdependencies)) { $currsubfile{$path} = {}; - if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { + 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') { @@ -9674,9 +9916,15 @@ sub ask_for_embedded_content { } } elsif (($actionurl eq '/adm/dependencies') || (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') && - ($args->{'context'} eq 'paste'))) { + ($args->{'context'} eq 'paste')) || + ($actionurl eq "/public/$cdom/$cnum/syllabus")) { if ($env{'request.course.id'} ne '') { - my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$}); + my $dir; + if ($actionurl eq "/public/$cdom/$cnum/syllabus") { + $dir = $fileloc; + } else { + ($dir) = ($fileloc =~ m{^(.+/)[^/]+$}); + } if ($dir ne '') { my ($sublistref,$listerror) = &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/'); @@ -9724,7 +9972,8 @@ sub ask_for_embedded_content { } } my %currfile; - if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { + 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') { @@ -9740,7 +9989,8 @@ sub ask_for_embedded_content { } } elsif (($actionurl eq '/adm/dependencies') || (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') && - ($args->{'context'} eq 'paste'))) { + ($args->{'context'} eq 'paste')) || + ($actionurl eq "/public/$cdom/$cnum/syllabus")) { if ($env{'request.course.id'} ne '') { my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$}); if ($dir ne '') { @@ -9775,12 +10025,14 @@ sub ask_for_embedded_content { ($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))))))); + unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) { + 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; } @@ -9789,28 +10041,38 @@ sub ask_for_embedded_content { ($args->{'context'} eq 'paste')) { $counter = scalar(keys(%existing)); $numpathchg = scalar(keys(%pathchanges)); - return ($output,$counter,$numpathchg,\%existing); + return ($output,$counter,$numpathchg,\%existing); + } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") && + (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) { + $counter = scalar(keys(%existing)); + $numpathchg = scalar(keys(%pathchanges)); + return ($output,$counter,$numpathchg,\%existing,\%mapping); } 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).'" /> '. + '<td valign="top"><img src="'.&icon($embed_file).'" /> '. '<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 .= '<br /><span class="LC_info" style="font-size:smaller;">'. + &mt('changed from: [_1]',$mapping{$embed_file}).'</span>'; } - $upload_output .= '</td><td>'; + $upload_output .= '</td>'; if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) { - $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>'; + $upload_output.='<td align="right">'. + '<span class="LC_info LC_fontsize_medium">'. + &mt("URL points to web address").'</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>'; + $upload_output.='<td align="right"><span class="LC_warning">'. + &mt('Invalid characters').'</span>'; $numinvalid++; } else { - $upload_output .= &embedded_file_element('upload_embedded',$counter, + $upload_output .= '<td>'. + &embedded_file_element('upload_embedded',$counter, $embed_file,\%mapping, $allfiles,$codebase,'upload'); $counter ++; @@ -9839,8 +10101,9 @@ sub ask_for_embedded_content { $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>'. + '<td valign="top"><img src="'.&icon($embed_file).'" /> '. + '<span class="LC_filename">'.$embed_file.'</span></td>'. + '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'. &Apache::loncommon::end_data_table_row()."\n"; } } @@ -9935,7 +10198,7 @@ sub ask_for_embedded_content { $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>'; + &mt('No dependencies 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"; } @@ -9978,7 +10241,7 @@ sub ask_for_embedded_content { $chgcount ++; } } - if ($counter) { + if (($counter) || ($numunused)) { if ($numpathchg) { $output .= '<input type ="hidden" name="number_pathchange_items" value="'. $numpathchg.'" />'."\n"; @@ -9991,13 +10254,13 @@ sub ask_for_embedded_content { } elsif ($actionurl eq '/adm/dependencies') { $output .= '<input type="hidden" name="action" value="process_changes" />'; } - $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n"; + $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); } @@ -10137,17 +10400,19 @@ sub upload_embedded { next; } $env{'form.embedded_item_'.$i.'.filename'}=$fname; + my $subdir = $path; + $subdir =~ s{/+$}{}; if ($context eq 'portfolio') { my $result; if ($state eq 'existingfile') { $result= &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile', - $dirpath.$env{'form.currentpath'}.$path); + $dirpath.$env{'form.currentpath'}.$subdir); } else { $result= &Apache::lonnet::userfileupload('embedded_item_'.$i,'', $dirpath. - $env{'form.currentpath'}.$path); + $env{'form.currentpath'}.$subdir); if ($result !~ m|^/uploaded/|) { $output .= '<span class="LC_error">' .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].' @@ -10159,10 +10424,11 @@ sub upload_embedded { $path.$fname.'</span>').'<br />'; } } - } elsif ($context eq 'coursedoc') { + } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) { + my $extendedsubdir = $dirpath.'/'.$subdir; + $extendedsubdir =~ s{/+$}{}; my $result = - &Apache::lonnet::userfileupload('embedded_item_'.$i,'coursedoc', - $dirpath.'/'.$path); + &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir); if ($result !~ m|^/uploaded/|) { $output .= '<span class="LC_error">' .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].' @@ -10172,6 +10438,9 @@ sub upload_embedded { } else { $output .= &mt('Uploaded [_1]','<span class="LC_filename">'. $path.$fname.'</span>').'<br />'; + if ($context eq 'syllabus') { + &Apache::lonnet::make_public_indefinitely($result); + } } } else { # Save the file @@ -10303,7 +10572,7 @@ sub modify_html_form { } sub modify_html_refs { - my ($context,$dirpath,$uname,$udom,$dir_root) = @_; + my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_; my $container; if ($context eq 'portfolio') { $container = $env{'form.container'}; @@ -10312,12 +10581,14 @@ sub modify_html_refs { } elsif ($context eq 'manage_dependencies') { (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'}); $container = "/$container"; + } elsif ($context eq 'syllabus') { + $container = $url; } else { $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'}; } my (%allfiles,%codebase,$output,$content); my @changes = &get_env_multiple('form.namechange'); - unless (@changes > 0) { + unless ((@changes > 0) || ($context eq 'syllabus')) { if (wantarray) { return ('',0,0); } else { @@ -10325,7 +10596,7 @@ sub modify_html_refs { } } if (($context eq 'portfolio') || ($context eq 'coursedoc') || - ($context eq 'manage_dependencies')) { + ($context eq 'manage_dependencies') || ($context eq 'syllabus')) { unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) { if (wantarray) { return ('',0,0); @@ -10381,6 +10652,7 @@ sub modify_html_refs { 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; + $allfiles{$newname} = $allfiles{$ref}; } if ($env{'form.embedded_codebase_'.$i} ne '') { $codebase = &unescape($env{'form.embedded_codebase_'.$i}); @@ -10389,10 +10661,11 @@ sub modify_html_refs { } } } + my $skiprewrites; if ($count || $codebasecount) { my $saveresult; if (($context eq 'portfolio') || ($context eq 'coursedoc') || - ($context eq 'manage_dependencies')) { + ($context eq 'manage_dependencies') || ($context eq 'syllabus')) { my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult); if ($url eq $container) { my ($fname) = ($container =~ m{/([^/]+)$}); @@ -10405,6 +10678,11 @@ sub modify_html_refs { '<span class="LC_filename">'. $container.'</span>').'</p>'; } + if ($context eq 'syllabus') { + unless ($saveresult eq 'ok') { + $skiprewrites = 1; + } + } } else { if (open(my $fh,">$container")) { print $fh $content; @@ -10420,6 +10698,47 @@ sub modify_html_refs { } } } + if (($context eq 'syllabus') && (!$skiprewrites)) { + my ($actionurl,$state); + $actionurl = "/public/$udom/$uname/syllabus"; + my ($ignore,$num,$numpathchanges,$existing,$mapping) = + &ask_for_embedded_content($actionurl,$state,\%allfiles, + \%codebase, + {'context' => 'rewrites', + 'ignore_remote_references' => 1,}); + if (ref($mapping) eq 'HASH') { + my $rewrites = 0; + foreach my $key (keys(%{$mapping})) { + next if ($key =~ m{^https?://}); + my $ref = $mapping->{$key}; + my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key"; + my $attrib; + if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') { + $attrib = join('|',@{$allfiles{$mapping->{$key}}}); + } + if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) { + my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi); + $rewrites += $numchg; + } + } + if ($rewrites) { + my $saveresult; + my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult); + if ($url eq $container) { + my ($fname) = ($container =~ m{/([^/]+)$}); + $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].', + $count,'<span class="LC_filename">'. + $fname.'</span>').'</p>'; + } else { + $output .= '<p class="LC_error">'. + &mt('Error: could not update links in [_1].', + '<span class="LC_filename">'. + $container.'</span>').'</p>'; + + } + } + } + } } else { &logthis('Failed to parse '.$container. ' to modify references: '.$parse_result); @@ -11722,7 +12041,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)) { @@ -11737,7 +12056,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; @@ -13974,7 +14293,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 @@ -14005,6 +14324,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" => '', @@ -14298,7 +14619,7 @@ 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).'&'. @@ -14312,14 +14633,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; } unless ($mapurl eq 'default') { $path = 'default&'. - &Apache::lonhtmlcommon::entity_encode('Main Course Documents'). + &Apache::lonhtmlcommon::entity_encode('Main Content'). ':::::&'.$path; } return $path; @@ -14462,7 +14783,7 @@ sub create_recaptcha { 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" />'). + '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />'). '<br /><br />'; }