--- loncom/interface/loncommon.pm 2021/01/04 05:10:35 1.1075.2.141.2.11 +++ loncom/interface/loncommon.pm 2023/09/11 13:54:11 1.1075.2.165 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.1075.2.141.2.11 2021/01/04 05:10:35 raeburn Exp $ +# $Id: loncommon.pm,v 1.1075.2.165 2023/09/11 13:54:11 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -83,8 +83,6 @@ use Crypt::DES; use DynaLoader; # for Crypt::DES version use File::Copy(); use File::Path(); -use String::CRC32(); -use Short::URL(); # ---------------------------------------------- Designs use vars qw(%defaultdesign); @@ -1380,7 +1378,7 @@ sub help_open_menu { } sub top_nav_help { - my ($text) = @_; + my ($text,$linkattr) = @_; $text = &mt($text); my $stay_on_page; unless ($env{'environment.remote'} eq 'on') { @@ -1396,7 +1394,7 @@ sub top_nav_help { if ($link) { return <<"END"; $banner_link -$text +$text END } else { return ' '.$text.' '; @@ -3129,14 +3127,11 @@ sub authform_filesystem { $fsyscheck.' onchange="'.$jscall.'" onclick="'. $jscall.'"'.$disabled.' />'; } - $autharg = ''; $result = &mt ('[_1] Filesystem Authenticated (with initial password [_2])', - ''); + ''.$autharg); return $result; } @@ -4733,8 +4728,91 @@ sub findallcourses { ############################################### sub blockcheck { - my ($setters,$activity,$uname,$udom,$url,$is_course,$symb,$caller) = @_; + my ($setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_; + unless ($activity eq 'docs') { + my ($has_evb,$check_ipaccess); + my $dom = $env{'user.domain'}; + if ($env{'request.course.id'}) { + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $checkrole = "cm./$cdom/$cnum"; + my $sec = $env{'request.course.sec'}; + if ($sec ne '') { + $checkrole .= "/$sec"; + } + if ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) && + ($env{'request.role'} !~ /^st/)) { + $has_evb = 1; + } + unless ($has_evb) { + if (($activity eq 'printout') || ($activity eq 'grades') || ($activity eq 'search') || + ($activity eq 'boards') || ($activity eq 'groups') || ($activity eq 'chat')) { + if ($udom eq $cdom) { + $check_ipaccess = 1; + } + } + } + } elsif (($activity eq 'com') || ($activity eq 'port') || ($activity eq 'blogs') || + ($activity eq 'about') || ($activity eq 'wishlist') || ($activity eq 'passwd')) { + my $checkrole; + if ($env{'request.role.domain'} eq '') { + $checkrole = "cm./$env{'user.domain'}/"; + } else { + $checkrole = "cm./$env{'request.role.domain'}/"; + } + if (($checkrole) && (&Apache::lonnet::allowed('evb',undef,undef,$checkrole))) { + $has_evb = 1; + } + } + unless ($has_evb || $check_ipaccess) { + my @machinedoms = &Apache::lonnet::current_machine_domains(); + if (($dom eq 'public') && ($activity eq 'port')) { + $dom = $udom; + } + if (($dom ne '') && (grep(/^\Q$dom\E$/,@machinedoms))) { + $check_ipaccess = 1; + } else { + my $lonhost = $Apache::lonnet::perlvar{'lonHostID'}; + my $internet_names = &Apache::lonnet::get_internet_names($lonhost); + my $prim = &Apache::lonnet::domain($dom,'primary'); + my $intdom = &Apache::lonnet::internet_dom($prim); + if (($intdom ne '') && (ref($internet_names) eq 'ARRAY')) { + if (grep(/^\Q$intdom\E$/,@{$internet_names})) { + $check_ipaccess = 1; + } + } + } + } + if ($check_ipaccess) { + my ($ipaccessref,$cached)=&Apache::lonnet::is_cached_new('ipaccess',$dom); + unless (defined($cached)) { + my %domconfig = + &Apache::lonnet::get_dom('configuration',['ipaccess'],$dom); + $ipaccessref = &Apache::lonnet::do_cache_new('ipaccess',$dom,$domconfig{'ipaccess'},1800); + } + if ((ref($ipaccessref) eq 'HASH') && ($clientip)) { + foreach my $id (keys(%{$ipaccessref})) { + if (ref($ipaccessref->{$id}) eq 'HASH') { + my $range = $ipaccessref->{$id}->{'ip'}; + if ($range) { + if (&Apache::lonnet::ip_match($clientip,$range)) { + if (ref($ipaccessref->{$id}->{'commblocks'}) eq 'HASH') { + if ($ipaccessref->{$id}->{'commblocks'}->{$activity} eq 'on') { + return ('','','',$id,$dom); + last; + } + } + } + } + } + } + } + } + if (($activity eq 'wishlist') || ($activity eq 'annotate')) { + return (); + } + } if (defined($udom) && defined($uname)) { # If uname and udom are for a course, check for blocks in the course. if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) { @@ -4750,15 +4828,16 @@ sub blockcheck { my $startblock = 0; my $endblock = 0; my $triggerblock = ''; - my %live_courses = &findallcourses(undef,$uname,$udom); + my %live_courses; + unless (($activity eq 'wishlist') || ($activity eq 'annotate')) { + %live_courses = &findallcourses(undef,$uname,$udom); + } # If uname is for a user, and activity is course-specific, i.e., # boards, chat or groups, check for blocking in current course only. if (($activity eq 'boards' || $activity eq 'chat' || - $activity eq 'groups' || $activity eq 'printout' || - $activity eq 'search' || $activity eq 'reinit' || - $activity eq 'alert') && + $activity eq 'groups' || $activity eq 'printout') && ($env{'request.course.id'})) { foreach my $key (keys(%live_courses)) { if ($key ne $env{'request.course.id'}) { @@ -4959,13 +5038,19 @@ sub get_blocks { 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, - }; + 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); + $triggered{$block} = { + start => $start, + end => $end, + type => $type, + }; + } + } + } } } } @@ -5029,14 +5114,17 @@ sub parse_block_record { } sub blocking_status { - my ($activity,$uname,$udom,$url,$is_course,$symb,$caller) = @_; + my ($activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_; my %setters; # check for active blocking - my ($startblock,$endblock,$triggerblock) = - &blockcheck(\%setters,$activity,$uname,$udom,$url,$is_course,$symb,$caller); + if ($clientip eq '') { + $clientip = &Apache::lonnet::get_requestor_ip(); + } + my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = + &blockcheck(\%setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller); my $blocked = 0; - if ($startblock && $endblock) { + if (($startblock && $endblock) || ($by_ip)) { $blocked = 1; } @@ -5045,8 +5133,8 @@ sub blocking_status { # 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') || ($activity eq 'passwd')) { +# $uname and $udom decide whose portfolio (or information page) the user is trying to look at + if (($activity eq 'port') || ($activity eq 'about') || ($activity eq 'passwd')) { $querystring .= "&udom=$udom" if ($udom =~ /^$match_domain$/); $querystring .= "&uname=$uname" if ($uname =~ /^$match_username$/); } elsif ($activity eq 'docs') { @@ -5084,10 +5172,12 @@ END_MYBLOCK $text = &mt('Gradebook Blocked'); } elsif ($activity eq 'search') { $text = &mt('Search Blocked'); - } elsif ($activity eq 'alert') { - $text = &mt('Checking Critical Messages Blocked'); - } elsif ($activity eq 'reinit') { - $text = &mt('Checking Course Update Blocked'); + } elsif ($activity eq 'about') { + $text = &mt('Access to User Information Pages Blocked'); + } elsif ($activity eq 'wishlist') { + $text = &mt('Access to Stored Links Blocked'); + } elsif ($activity eq 'annotate') { + $text = &mt('Access to Annotations Blocked'); } $output .= <<"END_BLOCK";
@@ -5111,44 +5201,24 @@ sub check_ip_acc { if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) { return 1; } - my ($ip,$allowed); + my $allowed=0; + my $ip; if (($ENV{'REMOTE_ADDR'} eq '127.0.0.1') || ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) { $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip; } else { - my $remote_ip = &Apache::lonnet::get_requestor_ip(); + my $remote_ip = &Apache::lonnet::get_requestor_ip(); $ip = $remote_ip || $env{'request.host'} || $clientip; } my $name; - my %access = ( - allowfrom => 1, - denyfrom => 0, - ); - my @allows; - my @denies; - foreach my $item (split(',',$acc)) { - $item =~ s/^\s*//; - $item =~ s/\s*$//; - if ($item =~ /^\!(.+)$/) { - push(@denies,$1); - } else { - push(@allows,$item); - } - } - my $numdenies = scalar(@denies); - my $numallows = scalar(@allows); - my $count = 0; - foreach my $pattern (@denies,@allows) { - $count ++; - my $acctype = 'allowfrom'; - if ($count <= $numdenies) { - $acctype = 'denyfrom'; - } + foreach my $pattern (split(',',$acc)) { + $pattern =~ s/^\s*//; + $pattern =~ s/\s*$//; if ($pattern =~ /\*$/) { #35.8.* $pattern=~s/\*//; - if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; } + if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) { #35.8.3.[34-56] my $low=$2; @@ -5156,7 +5226,7 @@ sub check_ip_acc { $pattern=$1; if ($ip =~ /^\Q$pattern\E/) { my $last=(split(/\./,$ip))[3]; - if ($last <=$high && $last >=$low) { $allowed=$access{$acctype}; } + if ($last <=$high && $last >=$low) { $allowed=1; } } } elsif ($pattern =~ /^\*/) { #*.msu.edu @@ -5166,10 +5236,10 @@ sub check_ip_acc { my $netaddr=inet_aton($ip); ($name)=gethostbyaddr($netaddr,AF_INET); } - if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; } + if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) { #127.0.0.1 - if ($ip =~ /^\Q$pattern\E/) { $allowed=$access{$acctype}; } + if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } } else { #some.name.com if (!defined($name)) { @@ -5177,16 +5247,9 @@ sub check_ip_acc { my $netaddr=inet_aton($ip); ($name)=gethostbyaddr($netaddr,AF_INET); } - if ($name =~ /\Q$pattern\E$/i) { $allowed=$access{$acctype}; } - } - if ($allowed =~ /^(0|1)$/) { last; } - } - if ($allowed eq '') { - if ($numdenies && !$numallows) { - $allowed = 1; - } else { - $allowed = 0; + if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } } + if ($allowed) { last; } } return $allowed; } @@ -5267,6 +5330,17 @@ sub get_domainconf { } } } + } elsif ($key eq 'saml') { + if (ref($domconfig{'login'}{$key}) eq 'HASH') { + foreach my $host (keys(%{$domconfig{'login'}{$key}})) { + if (ref($domconfig{'login'}{$key}{$host}) eq 'HASH') { + $designhash{$udom.'.login.'.$key.'_'.$host} = 1; + foreach my $item ('text','img','alt','url','title','notsso') { + $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item}; + } + } + } + } } else { foreach my $img (keys(%{$domconfig{'login'}{$key}})) { $designhash{$udom.'.login.'.$key.'_'.$img} = @@ -5371,8 +5445,12 @@ sub domainlogo { &Apache::lonnet::repcopy($local_name); } $imgsrc = &lonhttpdurl($imgsrc); - } - return ''.$domain.''; + } + my $alttext = $domain; + if ($designhash{$domain.'.login.alttext_domlogo'} ne '') { + $alttext = $designhash{$domain.'.login.alttext_domlogo'}; + } + return ''; } elsif (defined(&Apache::lonnet::domain($domain,'description'))) { return &Apache::lonnet::domain($domain,'description'); } else { @@ -5636,12 +5714,24 @@ sub bodytag { if ($realm) { $realm = '/'.$realm; } - if ($role eq 'ca') { + if ($role eq 'ca') { my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$}); $realm = &plainname($rname,$rdom); } # realm + my ($cid,$sec); if ($env{'request.course.id'}) { + $cid = $env{'request.course.id'}; + if ($env{'request.course.sec'}) { + $sec = $env{'request.course.sec'}; + } + } elsif ($realm =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$}) { + if (&Apache::lonnet::is_course($1,$2)) { + $cid = $1.'_'.$2; + $sec = $3; + } + } + if ($cid) { if ($env{'request.role'} !~ /^cr/) { $role = &Apache::lonnet::plaintext($role,&course_type()); } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) { @@ -5653,10 +5743,10 @@ sub bodytag { } else { $role = (split(/\//,$role,4))[-1]; } - if ($env{'request.course.sec'}) { - $role .= (' 'x2).'- '.&mt('section:').' '.$env{'request.course.sec'}; + if ($sec) { + $role .= (' 'x2).'- '.&mt('section:').' '.$sec; } - $realm = $env{'course.'.$env{'request.course.id'}.'.description'}; + $realm = $env{'course.'.$cid.'.description'}; } else { $role = &Apache::lonnet::plaintext($role); } @@ -5678,15 +5768,13 @@ sub bodytag { if ($public) { undef($role); } - + my $titleinfo = '

'.$title.'

'; # # Extra info if you are the DC my $dc_info = ''; - if ($env{'user.adv'} && exists($env{'user.role.dc./'. - $env{'course.'.$env{'request.course.id'}. - '.domain'}.'/'})) { - my $cid = $env{'request.course.id'}; + if (($env{'user.adv'}) && ($env{'request.course.id'}) && + (exists($env{'user.role.dc./'.$env{'course.'.$cid.'.domain'}.'/'}))) { $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'}; $dc_info =~ s/\s+$//; } @@ -5718,11 +5806,11 @@ sub bodytag { $bodytag .= Apache::lonhtmlcommon::scripttag( Apache::lonmenu::utilityfunctions($httphost), 'start'); - my ($left,$right) = Apache::lonmenu::primary_menu(); + my ($left,$right) = Apache::lonmenu::primary_menu($args->{'links_disabled'}); if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) { if ($dc_info) { - $dc_info = qq|$dc_info|; + $dc_info = qq|$dc_info|; } $bodytag .= qq|
$left $role
$realm $dc_info
|; @@ -5746,7 +5834,7 @@ sub bodytag { } #don't show menus for public users if (!$public){ - $bodytag .= Apache::lonmenu::secondary_menu($httphost); + $bodytag .= Apache::lonmenu::secondary_menu($httphost,$args->{'links_disabled'}); $bodytag .= Apache::lonmenu::serverform(); $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); if ($env{'request.state'} eq 'construct') { @@ -5755,8 +5843,8 @@ sub bodytag { } elsif ($forcereg) { $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef, $args->{'group'}, - $args->{'hide_buttons', - $hostname}); + $args->{'hide_buttons'}, + $hostname); } else { my $forbodytag; &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, @@ -5905,6 +5993,9 @@ sub endbodytag { $endbodytag; } } + if ((ref($args) eq 'HASH') && ($args->{'dashjs'})) { + $endbodytag = &Apache::lonhtmlcommon::dash_to_minus_js().$endbodytag; + } return $endbodytag; } @@ -7864,6 +7955,18 @@ ul.LC_funclist li { cursor:pointer; } +.LCisDisabled { + cursor: not-allowed; + opacity: 0.5; +} + +a[aria-disabled="true"] { + color: currentColor; + display: inline-block; /* For IE11/ MS Edge bug */ + pointer-events: none; + text-decoration: none; +} + pre.LC_wordwrap { white-space: pre-wrap; white-space: -moz-pre-wrap; @@ -8032,10 +8135,17 @@ ADDMETA unless (&Apache::lonnet::allowed('mau',$dom_in_use)) { my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use); my $lonhost = $Apache::lonnet::perlvar{'lonHostID'}; - my $offload; + my ($offload,$offloadoth); if (ref($domdefs{'offloadnow'}) eq 'HASH') { if ($domdefs{'offloadnow'}{$lonhost}) { $offload = 1; + if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) && + (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) { + unless (&Apache::lonnet::shared_institution($env{'user.domain'})) { + $offloadoth = 1; + $dom_in_use = $env{'user.domain'}; + } + } } } unless ($offload) { @@ -8045,6 +8155,7 @@ ADDMETA (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) { unless (&Apache::lonnet::shared_institution($env{'user.domain'})) { $offload = 1; + $offloadoth = 1; $dom_in_use = $env{'user.domain'}; } } @@ -8052,7 +8163,13 @@ ADDMETA } } if ($offload) { - my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use); + my $newserver = &Apache::lonnet::spareserver(undef,30000,undef,1,$dom_in_use); + if (($newserver eq '') && ($offloadoth)) { + my @domains = &Apache::lonnet::current_machine_domains(); + if (($dom_in_use ne '') && (!grep(/^\Q$dom_in_use\E$/,@domains))) { + ($newserver) = &Apache::lonnet::choose_server($dom_in_use); + } + } if (($newserver) && ($newserver ne $lonhost)) { my $numsec = 5; my $timeout = $numsec * 1000; @@ -8066,7 +8183,7 @@ ADDMETA } if ($locknum) { my @lockinfo = sort(values(%locks)); - $msg = &mt('Once the following tasks are complete: ')."\n". + $msg = &mt('Once the following tasks are complete:')." \n". join(", ",sort(values(%locks)))."\n"; if (&show_course()) { $msg .= &mt('your session will be transferred to a different server, after you click "Courses".'); @@ -8228,7 +8345,8 @@ sub print_suppression { } 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,undef,1); + my $clientip = &Apache::lonnet::get_requestor_ip(); + my $blocked = &blocking_status('printout',$clientip,$cnum,$cdom,undef,1); if ($blocked) { my $checkrole = "cm./$cdom/$cnum"; if ($env{'request.course.sec'} ne '') { @@ -8349,6 +8467,9 @@ $args - additional optional args support will contain https:// if server uses https (as per hosts.tab), but request is for http hostname -> hostname, originally from $r->hostname(), (optional). + links_disabled -> Links in primary and secondary menus are disabled + (Can enable them once page has loaded - see lonroles.pm + for an example). =back @@ -8555,7 +8676,15 @@ ENDLINK } sub modal_adhoc_script { - my ($funcname,$width,$height,$content)=@_; + my ($funcname,$width,$height,$content,$possmathjax)=@_; + my $mathjax; + if ($possmathjax) { + $mathjax = <<'ENDJAX'; + if (typeof MathJax == 'object') { + MathJax.Hub.Queue(["Typeset",MathJax.Hub]); + } +ENDJAX + } return (< // @@ -8573,7 +8703,7 @@ ENDADHOC } sub modal_adhoc_inner { - my ($funcname,$width,$height,$content)=@_; + my ($funcname,$width,$height,$content,$possmathjax)=@_; my $innerwidth=$width-20; $content=&js_ready( &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}). @@ -8582,12 +8712,12 @@ sub modal_adhoc_inner { &end_scrollbox(). &end_page() ); - return &modal_adhoc_script($funcname,$width,$height,$content); + return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax); } sub modal_adhoc_window { - my ($funcname,$width,$height,$content,$linktext)=@_; - return &modal_adhoc_inner($funcname,$width,$height,$content). + my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_; + return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax). "".$linktext.""; } @@ -10443,11 +10573,15 @@ sub sorted_inst_types { } sub get_institutional_codes { - my ($settings,$allcourses,$LC_code) = @_; + my ($cdom,$crs,$settings,$allcourses,$LC_code) = @_; # Get complete list of course sections to update my @currsections = (); my @currxlists = (); + my (%unclutteredsec,%unclutteredlcsec); my $coursecode = $$settings{'internal.coursecode'}; + my $crskey = $crs.':'.$coursecode; + @{$unclutteredsec{$crskey}} = (); + @{$unclutteredlcsec{$crskey}} = (); if ($$settings{'internal.sectionnums'} ne '') { @currsections = split(/,/,$$settings{'internal.sectionnums'}); @@ -10458,8 +10592,8 @@ sub get_institutional_codes { } if (@currxlists > 0) { - foreach (@currxlists) { - if (m/^([^:]+):(\w*)$/) { + foreach my $xl (@currxlists) { + if ($xl =~ /^([^:]+):(\w*)$/) { unless (grep/^$1$/,@{$allcourses}) { push(@{$allcourses},$1); $$LC_code{$1} = $2; @@ -10467,15 +10601,28 @@ sub get_institutional_codes { } } } - + if (@currsections > 0) { - foreach (@currsections) { - if (m/^(\w+):(\w*)$/) { - my $sec = $coursecode.$1; + foreach my $sec (@currsections) { + if ($sec =~ m/^(\w+):(\w*)$/ ) { + my $instsec = $1; my $lc_sec = $2; - unless (grep/^$sec$/,@{$allcourses}) { + unless (grep/^\Q$instsec\E$/,@{$unclutteredsec{$crskey}}) { + push(@{$unclutteredsec{$crskey}},$instsec); + push(@{$unclutteredlcsec{$crskey}},$lc_sec); + } + } + } + } + + if (@{$unclutteredsec{$crskey}} > 0) { + my %formattedsec = &Apache::lonnet::auto_instsec_reformat($cdom,'clutter',\%unclutteredsec); + if ((ref($formattedsec{$crskey}) eq 'ARRAY') && (ref($unclutteredlcsec{$crskey}) eq 'ARRAY')) { + for (my $i=0; $i<@{$formattedsec{$crskey}}; $i++) { + my $sec = $coursecode.$formattedsec{$crskey}[$i]; + unless (grep/^\Q$sec\E$/,@{$allcourses}) { push(@{$allcourses},$sec); - $$LC_code{$sec} = $lc_sec; + $$LC_code{$sec} = $unclutteredlcsec{$crskey}[$i]; } } } @@ -14767,7 +14914,7 @@ sub recurse_categories { 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(' -> ',(@{$parents},$category)); + my $trailstr = join(' » ',(@{$parents},$category)); if ($allitems->{$item} eq '') { push(@{$trails},$trailstr); $allitems->{$item} = scalar(@{$trails})-1; @@ -15157,8 +15304,7 @@ sub check_clone { my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'}; my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid); my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom); - my $clonetitle; - my @clonemsg; + my $clonemsg; my $can_clone = 0; my $lctype = lc($args->{'crstype'}); if ($lctype ne 'community') { @@ -15166,38 +15312,16 @@ sub check_clone { } if ($clonehome eq 'no_host') { if ($args->{'crstype'} eq 'Community') { - push(@clonemsg,({ - mt => 'No new community created.', - args => [], - }, - { - mt => 'A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.', - args => [$args->{'clonedomain'}.':'.$args->{'clonedomain'}], - })); + $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 { - push(@clonemsg,({ - mt => 'No new course created.', - args => [], - }, - { - mt => 'A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.', - args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}], - })); - } + $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}); - $clonetitle = $clonedesc{'description'}; if ($args->{'crstype'} eq 'Community') { if ($clonedesc{'type'} ne 'Community') { - push(@clonemsg,({ - mt => 'No new community created.', - args => [], - }, - { - mt => 'A new community could not be cloned from the specified original - [_1] - because it is a course not a community.', - args => [$args->{'clonecourse'}.':'.$args->{'clonedomain'}], - })); - return ($can_clone,\@clonemsg,$cloneid,$clonehome); + $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'}) && @@ -15286,34 +15410,20 @@ sub check_clone { } unless ($can_clone) { if ($args->{'crstype'} eq 'Community') { - push(@clonemsg,({ - mt => 'No new community created.', - args => [], - }, - { - 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 => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}], - })); + $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 { - push(@clonemsg,({ - mt => 'No new course created.', - args => [], - }, - { - 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 => [$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}], - })); - } + $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'}); + } } } } - return ($can_clone,\@clonemsg,$cloneid,$clonehome,$clonetitle); + return ($can_clone, $clonemsg, $cloneid, $clonehome); } sub construct_course { my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context, - $cnum,$category,$coderef,$callercontext,$user_lh) = @_; - my ($outcome,$msgref,$clonemsgref); + $cnum,$category,$coderef) = @_; + my $outcome; my $linefeed = '
'."\n"; if ($context eq 'auto') { $linefeed = "\n"; @@ -15322,11 +15432,18 @@ sub construct_course { # # Are we cloning? # - my ($can_clone,$cloneid,$clonehome,$clonetitle); + my ($can_clone, $clonemsg, $cloneid, $clonehome); if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) { - ($can_clone,$clonemsgref,$cloneid,$clonehome,$clonetitle) = &check_clone($args,$linefeed); + ($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed); + if ($context ne 'auto') { + if ($clonemsg ne '') { + $clonemsg = ''.$clonemsg.''; + } + } + $outcome .= $clonemsg.$linefeed; + if (!$can_clone) { - return (0,$outcome,$clonemsgref); + return (0,$outcome); } } @@ -15344,20 +15461,15 @@ sub construct_course { $args->{'ccuname'}.':'. $args->{'ccdomain'}, $args->{'crstype'}, - $cnum,$context,$category, - $callercontext); + $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. - if (($callercontext eq 'auto') && ($user_lh ne '')) { - $outcome .= &mt_user($user_lh,'New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed; - } else { - $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed; - } + $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed; if ($$courseid =~ /^error:/) { - return (0,$outcome,$clonemsgref); + return (0,$outcome); } # @@ -15366,37 +15478,23 @@ sub construct_course { ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid); my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom); if ($crsuhome eq 'no_host') { - if (($callercontext eq 'auto') && ($user_lh ne '')) { - $outcome .= &mt_user($user_lh, - 'Course creation failed, unrecognized course home server.'); - } else { - $outcome .= &mt('Course creation failed, unrecognized course home server.'); - } - $outcome .= $linefeed; - return (0,$outcome,$clonemsgref); + $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed; + return (0,$outcome); } $outcome .= &mt('Created on').': '.$crsuhome.$linefeed; # # Do the cloning # - my @clonemsg; if ($can_clone && $cloneid) { - push(@clonemsg, - { - mt => 'Created [_1] by cloning from [_2]', - args => [$crstype,$clonetitle], - }); + $clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome); + if ($context ne 'auto') { + $clonemsg = ''.$clonemsg.''; + } + $outcome .= $clonemsg.$linefeed; my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum); # Copy all files - my @info = - &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'}, - $args->{'dateshift'},$args->{'crscode'}, - $args->{'ccuname'}.':'.$args->{'ccdomain'}, - $args->{'tinyurls'}); - if (@info) { - push(@clonemsg,@info); - } + &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'}); # Restore URL $cenv{'url'}=$oldcenv{'url'}; # Restore title @@ -15421,8 +15519,7 @@ sub construct_course { 'plc.users.denied', 'hidefromcat', 'checkforpriv', - 'categories', - 'internal.uniquecode'], + 'categories'], $$crsudom,$$crsunum); if ($args->{'textbook'}) { $cenv{'internal.textbook'} = $args->{'textbook'}; @@ -15703,7 +15800,7 @@ sub construct_course { $outcome .= ($fatal?$errtext:'write ok').$linefeed; } - return (1,$outcome,\@clonemsg); + return (1,$outcome); } sub make_unique_code { @@ -15872,6 +15969,24 @@ sub compare_arrays { return @difference; } +sub lon_status_items { + my %defaults = ( + E => 100, + W => 4, + N => 1, + U => 5, + threshold => 200, + sysmail => 2500, + ); + my %names = ( + E => 'Errors', + W => 'Warnings', + N => 'Notices', + U => 'Unsent', + ); + return (\%defaults,\%names); +} + # -------------------------------------------------------- Initialize user login sub init_user_environment { my ($r, $username, $domain, $authhost, $form, $args) = @_; @@ -16853,12 +16968,8 @@ sub needs_coursereinit { $interval = 600; } if (($now-$env{'request.course.timechecked'})>$interval) { - &Apache::lonnet::appenv({'request.course.timechecked'=>$now}); - my $blocked = &blocking_status('reinit',$cnum,$cdom,undef,1); - if ($blocked) { - return (); - } my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum); + &Apache::lonnet::appenv({'request.course.timechecked'=>$now}); if ($lastchange > $env{'request.course.tied'}) { my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired'); if ($curr_reqd_hash{'internal.releaserequired'} ne '') { @@ -17180,13 +17291,17 @@ sub create_captcha { if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') { $output = ''."\n". + ''. &mt('Type in the letters/numbers shown below').' '. ''. - '
'. + '

'. 'captcha'; last; } } + if ($output eq '') { + &Apache::lonnet::logthis("Failed to create Captcha code after $tries attempts."); + } return $output; } @@ -17225,7 +17340,8 @@ sub check_captcha { sub create_recaptcha { my ($pubkey,$version) = @_; if ($version >= 2) { - return '
'; + return '
'. + '
'; } else { my $use_ssl; if ($ENV{'SERVER_PORT'} == 443) { @@ -17243,7 +17359,7 @@ sub create_recaptcha { sub check_recaptcha { my ($privkey,$version) = @_; my $captcha_chk; - my $ip = &Apache::lonnet::get_requestor_ip(); + my $ip = &Apache::lonnet::get_requestor_ip(); if ($version >= 2) { my $ua = LWP::UserAgent->new; $ua->timeout(10); @@ -17315,31 +17431,18 @@ sub cleanup_html { # Checks for critical messages and returns a redirect url if one exists. # $interval indicates how often to check for messages. -# $context is the calling context -- roles, grades, contents, menu or flip. sub critical_redirect { - my ($interval,$context) = @_; + my ($interval) = @_; + unless (($env{'user.domain'} ne '') && ($env{'user.name'} ne '')) { + return (); + } if ((time-$env{'user.criticalcheck.time'})>$interval) { - if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) { - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $blocked = &blocking_status('alert',$cnum,$cdom,undef,1); - 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})) { - return; - } - } - } my @what=&Apache::lonnet::dump('critical', $env{'user.domain'}, $env{'user.name'}); &Apache::lonnet::appenv({'user.criticalcheck.time'=>time}); my $redirecturl; if ($what[0]) { - if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) { + if (($what[0] ne 'con_lost') && ($what[0] ne 'no_such_host') && ($what[0]!~/^error\:/)) { $redirecturl='/adm/email?critical=display'; my $url=&Apache::lonnet::absolute_url().$redirecturl; return (1, $url); @@ -17399,146 +17502,6 @@ sub des_decrypt { return $plaintext; } -sub get_requested_shorturls { - my ($cdom,$cnum,$navmap) = @_; - return unless (ref($navmap)); - my ($numnew,$errors); - my @toshorten = &Apache::loncommon::get_env_multiple('form.addtiny'); - if (@toshorten) { - my (%maps,%resources,%titles); - &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles, - 'shorturls',$cdom,$cnum); - if (keys(%resources)) { - my %tocreate; - foreach my $item (sort {$a <=> $b} (@toshorten)) { - my $symb = $resources{$item}; - if ($symb) { - $tocreate{$cnum.'&'.$symb} = 1; - } - } - if (keys(%tocreate)) { - ($numnew,$errors) = &make_short_symbs($cdom,$cnum, - \%tocreate); - } - } - } - return ($numnew,$errors); -} - -sub make_short_symbs { - my ($cdom,$cnum,$tocreateref,$lockuser) = @_; - my ($numnew,@errors); - if (ref($tocreateref) eq 'HASH') { - my %tocreate = %{$tocreateref}; - if (keys(%tocreate)) { - my %coursetiny = &Apache::lonnet::dump('tiny',$cdom,$cnum); - my $su = Short::URL->new(no_vowels => 1); - my $init = ''; - my (%newunique,%addcourse,%courseonly,%failed); - # get lock on tiny db - my $now = time; - if ($lockuser eq '') { - $lockuser = $env{'user.name'}.':'.$env{'user.domain'}; - } - my $lockhash = { - "lock\0$now" => $lockuser, - }; - my $tries = 0; - my $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom); - my ($code,$error); - while (($gotlock ne 'ok') && ($tries<3)) { - $tries ++; - sleep 1; - $gotlock = &Apache::lonnet::newput_dom('tiny',$lockhash,$cdom); - } - if ($gotlock eq 'ok') { - $init = &shorten_symbs($cdom,$init,$su,\%coursetiny,\%tocreate,\%newunique, - \%addcourse,\%courseonly,\%failed); - if (keys(%failed)) { - my $numfailed = scalar(keys(%failed)); - push(@errors,&mt('error: could not obtain unique six character URL for [quant,_1,resource]',$numfailed)); - } - if (keys(%newunique)) { - my $putres = &Apache::lonnet::newput_dom('tiny',\%newunique,$cdom); - if ($putres eq 'ok') { - $numnew = scalar(keys(%newunique)); - my $newputres = &Apache::lonnet::newput('tiny',\%addcourse,$cdom,$cnum); - unless ($newputres eq 'ok') { - push(@errors,&mt('error: could not store course look-up of short URLs')); - } - } else { - push(@errors,&mt('error: could not store unique six character URLs')); - } - } - } - } - } - return ($numnew,\@errors); -} - -sub shorten_symbs { - my ($cdom,$init,$su,$coursetiny,$tocreate,$newunique,$addcourse,$courseonly,$failed) = @_; - return unless ((ref($su)) && (ref($coursetiny) eq 'HASH') && (ref($tocreate) eq 'HASH') && - (ref($newunique) eq 'HASH') && (ref($addcourse) eq 'HASH') && - (ref($courseonly) eq 'HASH') && (ref($failed) eq 'HASH')); - my (%possibles,%collisions); - foreach my $key (keys(%{$tocreate})) { - my $num = String::CRC32::crc32($key); - my $tiny = $su->encode($num,$init); - if ($tiny) { - $possibles{$tiny} = $key; - } - } - if (!$init) { - $init = 1; - } else { - $init ++; - } - if (keys(%possibles)) { - my @posstiny = keys(%possibles); - my $configuname = &Apache::lonnet::get_domainconfiguser($cdom); - my %currtiny = &Apache::lonnet::get('tiny',\@posstiny,$cdom,$configuname); - if (keys(%currtiny)) { - foreach my $key (keys(%currtiny)) { - next if ($currtiny{$key} eq ''); - if ($currtiny{$key} eq $possibles{$key}) { - my ($tcnum,$tsymb) = split(/\&/,$currtiny{$key}); - unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) { - $courseonly->{$tsymb} = $key; - } - } else { - $collisions{$possibles{$key}} = 1; - } - delete($possibles{$key}); - } - } - foreach my $key (keys(%possibles)) { - $newunique->{$key} = $possibles{$key}; - my ($tcnum,$tsymb) = split(/\&/,$possibles{$key}); - unless (($coursetiny->{$tsymb} eq $key) || ($addcourse->{$tsymb} eq $key) || ($courseonly->{$tsymb} eq $key)) { - $addcourse->{$tsymb} = $key; - } - } - } - if (keys(%collisions)) { - if ($init <5) { - if (!$init) { - $init = 1; - } else { - $init ++; - } - $init = &shorten_symbs($cdom,$init,$su,$coursetiny,\%collisions, - $newunique,$addcourse,$courseonly,$failed); - } else { - foreach my $key (keys(%collisions)) { - $failed->{$key} = 1; - $failed->{$key} = 1; - } - } - } - return $init; -} - sub is_nonframeable { my ($url,$absolute,$hostname,$ip,$nocache) = @_; my ($remprotocol,$remhost) = ($url =~ m{^(https?)\://(([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,})}i);