--- loncom/interface/loncommon.pm 2024/08/17 23:31:37 1.1075.2.161.2.25 +++ loncom/interface/loncommon.pm 2017/07/13 15:29:56 1.1285 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.1075.2.161.2.25 2024/08/17 23:31:37 raeburn Exp $ +# $Id: loncommon.pm,v 1.1285 2017/07/13 15:29:56 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -61,7 +61,7 @@ use POSIX qw(strftime mktime); use Apache::lonmenu(); use Apache::lonenc(); use Apache::lonlocal; -use Apache::lonnavmaps(); +use Apache::lonnet(); use HTML::Entities; use Apache::lonhtmlcommon(); use Apache::loncoursedata(); @@ -71,21 +71,19 @@ use Apache::lonuserutils(); use Apache::lonuserstate(); use Apache::courseclassifier(); use LONCAPA qw(:DEFAULT :match); -use LONCAPA::map(); -use HTTP::Request; +use LONCAPA::LWPReq; use DateTime::TimeZone; use DateTime::Locale; use Encode(); +use Text::Aspell; use Authen::Captcha; use Captcha::reCAPTCHA; use JSON::DWIW; use LWP::UserAgent; use Crypt::DES; use DynaLoader; # for Crypt::DES version -use File::Copy(); -use File::Path(); -use String::CRC32(); -use Short::URL(); +use MIME::Lite; +use MIME::Types; # ---------------------------------------------- Designs use vars qw(%defaultdesign); @@ -170,6 +168,7 @@ sub ssi_with_retries { # ----------------------------------------------- Filetypes/Languages/Copyright my %language; my %supported_language; +my %supported_codes; my %latex_language; # For choosing hyphenation in my %latex_language_bykey; # for choosing hyphenation from metadata my %cprtag; @@ -200,18 +199,19 @@ BEGIN { { my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}. '/language.tab'; - if ( open(my $fh,'<',$langtabfile) ) { + if ( open(my $fh,"<$langtabfile") ) { while (my $line = <$fh>) { next if ($line=~/^\#/); chomp($line); - my ($key,$two,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line)); + my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line)); $language{$key}=$val.' - '.$enc; if ($sup) { $supported_language{$key}=$sup; + $supported_codes{$key} = $code; } if ($latex) { $latex_language_bykey{$key} = $latex; - $latex_language{$two} = $latex; + $latex_language{$code} = $latex; } } close($fh); @@ -221,7 +221,7 @@ BEGIN { { my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}. '/copyright.tab'; - if ( open (my $fh,'<',$copyrightfile) ) { + if ( open (my $fh,"<$copyrightfile") ) { while (my $line = <$fh>) { next if ($line=~/^\#/); chomp($line); @@ -235,7 +235,7 @@ BEGIN { { my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}. '/source_copyright.tab'; - if ( open (my $fh,'<',$sourcecopyrightfile) ) { + if ( open (my $fh,"<$sourcecopyrightfile") ) { while (my $line = <$fh>) { next if ($line =~ /^\#/); chomp($line); @@ -249,7 +249,7 @@ BEGIN { # -------------------------------------------------------------- default domain designs my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors'; my $designfile = $designdir.'/default.tab'; - if ( open (my $fh,'<',$designfile) ) { + if ( open (my $fh,"<$designfile") ) { while (my $line = <$fh>) { next if ($line =~ /^\#/); chomp($line); @@ -263,7 +263,7 @@ BEGIN { { my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}. '/filecategories.tab'; - if ( open (my $fh,'<',$categoryfile) ) { + if ( open (my $fh,"<$categoryfile") ) { while (my $line = <$fh>) { next if ($line =~ /^\#/); chomp($line); @@ -278,7 +278,7 @@ BEGIN { { my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}. '/filetypes.tab'; - if ( open (my $fh,'<',$typesfile) ) { + if ( open (my $fh,"<$typesfile") ) { while (my $line = <$fh>) { next if ($line =~ /^\#/); chomp($line); @@ -431,7 +431,7 @@ sub studentbrowser_javascript { block @@ -1081,6 +1095,9 @@ linked_select_forms takes the following =item * $onchangesecond, additional javascript call to execute for an onchange event for the second \n"; + $result .= ""; } -=pod - -=item * &iframe_wrapper_headjs() - -emits javascript containing two global vars to facilitate handling of resizing -by code in iframe_wrapper_resizejs() used when an iframe is present in a page -with standard LON-CAPA menus. - -=cut - -# -# Where iframe is in use, if window.onload() executes before the custom resize function -# has been defined (jQuery), two global javascript vars (LCnotready and LCresizedef) -# are used to ensure document.ready() triggers a call to resize, so the iframe contents -# do not obscure the Functions menu. -# - -sub iframe_wrapper_headjs { - return <<"ENDJS"; - - -ENDJS - +sub crsauthor_url { + my ($url) = @_; + if ($url eq '') { + $url = $ENV{'REQUEST_URI'}; + } + my ($cnum,$cdom); + if ($env{'request.course.id'}) { + my ($audom,$auname) = ($url =~ m{^/priv/($match_domain)/($match_name)/}); + if ($audom ne '' && $auname ne '') { + if (($env{'course.'.$env{'request.course.id'}.'.num'} eq $auname) && + ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $audom)) { + $cnum = $auname; + $cdom = $audom; + } + } + } + return ($cnum,$cdom); } -=pod - -=item * &iframe_wrapper_resizejs() - -emits javascript used to handle resizing for a page containing -an iframe, to ensure that the iframe does not obscure any -standard LON-CAPA menu items. - -=back - -=cut - -# -# jQuery to use when iframe is in use and a page resize occurs. -# This script will ensure that the iframe does not obscure any -# standard LON-CAPA inline menus (primary, secondary, and/or -# breadcrumbs and Functions menus. Expects javascript from -# &iframe_wrapper_headjs() to be in head portion of the web page, -# e.g., by inclusion in second arg passed to &start_page(). -# - -sub iframe_wrapper_resizejs { - my $offset = 5; - &get_unprocessed_cgi($ENV{'QUERY_STRING'},['inhibitmenu']); - if (($env{'form.inhibitmenu'} eq 'yes') || ($env{'form.only_body'})) { - $offset = 0; - } - return &Apache::lonhtmlcommon::scripttag(< 'Filename', + dire => 'Directory', + ); + my $numdirs = scalar(keys(%files)); + my (%possexts,$singledir,@singledirfiles); + if ($only) { + map { $possexts{$_} = 1; } split(/\s*,\s*/,$only); + } + my (%nonemptydirs,$possdirs); + if ($numdirs > 1) { + my @order; + foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) { + if (ref($files{$key}) eq 'HASH') { + my $shown = $key; + if ($key eq '') { + $shown = '/'; + } + my @ordered = (); + foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$key}}))) { + if ($only) { + my ($ext) = ($file =~ /\.([^.]+)$/); + unless ($possexts{lc($ext)}) { + next; + } + } + $selimport_menus{$key}->{'select2'}->{$file} = $file; + push(@ordered,$file); + } + if (@ordered) { + push(@order,$key); + $nonemptydirs{$key} = 1; + $selimport_menus{$key}->{'text'} = $shown; + $selimport_menus{$key}->{'default'} = ''; + $selimport_menus{$key}->{'select2'}->{''} = ''; + $selimport_menus{$key}->{'order'} = \@ordered; } } - if (header != null && header.length) { - height = header.height(); - hdrtop = header.position().top; + } + $possdirs = scalar(keys(%nonemptydirs)); + if ($possdirs > 1) { + my @order = sort { lc($a) cmp lc($b) } (keys(%nonemptydirs)); + $output = $lt{'dire'}. + &linked_select_forms($form,'
'. + $lt{'fnam'},'', + $firstselectname,$secondselectname, + \%selimport_menus,\@order, + $onchangefirst,'',$suffix).'
'; + } elsif ($possdirs == 1) { + $singledir = (keys(%nonemptydirs))[0]; + if (ref($selimport_menus{$singledir}->{'order'}) eq 'ARRAY') { + @singledirfiles = @{$selimport_menus{$singledir}->{'order'}}; + } + delete($selimport_menus{$singledir}); + } + } elsif ($numdirs == 1) { + $singledir = (keys(%files))[0]; + foreach my $file (sort { lc($a) cmp lc($b) } (keys(%{$files{$singledir}}))) { + if ($only) { + my ($ext) = ($file =~ /\.([^.]+)$/); + unless ($possexts{lc($ext)}) { + next; + } } - var pos = height + hdrtop + offset; - \$('.LC_iframecontainer').css('top', pos); - }); - LCresizedef = 1; - if (LCnotready == 1) { - LCnotready = 0; - \$(window).trigger('resize'); + push(@singledirfiles,$file); } - }); - window.onload = function(){ - if (LCresizedef) { - LCnotready = 0; - \$(window).trigger('resize'); - } else { - LCnotready = 1; - } - }; -SCRIPT - + if (@singledirfiles) { + $possdirs == 1; + } + } + if (($possdirs == 1) && (@singledirfiles)) { + my $showdir = $singledir; + if ($singledir eq '') { + $showdir = '/'; + } + $output = $lt{'dire'}. + '
'. + $lt{'fnam'}.'
'."\n"; + } + return ($possdirs,$output); } =pod @@ -2383,7 +2557,7 @@ option_name => displayed text. An option a javascript onchange item, e.g., onchange="this.form.submit();". An optional arg -- $readonly -- if true will cause the select form to be disabled, e.g., for the case where an instructor has a section- -specific role, and is viewing/modifying parameters. +specific role, and is viewing/modifying parameters. See lonrights.pm for an example invocation and use. @@ -2586,7 +2760,7 @@ The optional $incdoms is a reference to The optional $excdoms is a reference to an array of domains which will be excluded from the available options. -The optional $disabled argument, if true, adds the disabled attribute to the select tag. +The optional $disabled argument, if true, adds the disabled attribute to the select tag. =cut @@ -2607,7 +2781,7 @@ sub select_dom_form { } if ($includeempty) { @domains=('',@domains); } if (ref($excdoms) eq 'ARRAY') { - map { $exclude{$_} = 1; } @{$excdoms}; + map { $exclude{$_} = 1; } @{$excdoms}; } my $selectdomain = "'; $result = &mt ('[_1] Filesystem Authenticated (with initial password [_2])', - ''.$autharg); - return $result; -} - -sub authform_lti { - my %in = ( - formname => 'document.cu', - kerb_def_dom => 'MSU.EDU', - @_, - ); - my ($lticheck,$result,$authtype,$autharg,$jscall,$disabled); - my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'}); - if ($in{'readonly'}) { - $disabled = ' disabled="disabled"'; - } - if (defined($in{'curr_authtype'})) { - if ($in{'curr_authtype'} eq 'lti') { - if ($can_assign{'lti'}) { - $lticheck = 'checked="checked" '; - if (defined($in{'mode'})) { - if ($in{'mode'} eq 'modifyuser') { - $lticheck = ''; - } - } - } else { - $result = &mt('Currently LTI Authenticated.'); - return $result; - } - } - } else { - if ($authnum == 1) { - $authtype = ''; - } - } - if (!$can_assign{'lti'}) { - return; - } elsif ($authtype eq '') { - if (defined($in{'mode'})) { - if ($in{'mode'} eq 'modifycourse') { - if ($authnum == 1) { - $authtype = ''; - } - } - } - } - $jscall = "javascript:changed_radio('lti',$in{'formname'});"; - if (($authtype eq '') && (($in{'mode'} eq 'modifycourse') || ($in{'curr_authtype'} ne 'lti'))) { - $authtype = ''; - } - $autharg = ''; - if ($authtype) { - $result = &mt('[_1] LTI Authenticated', - ''.$autharg); - } else { - $result = ''.&mt('LTI Authenticated').''. - $autharg; - } + ''); return $result; } @@ -3361,228 +3478,6 @@ sub get_assignable_auth { return ($authnum,%can_assign); } -sub check_passwd_rules { - my ($domain,$plainpass) = @_; - my %passwdconf = &Apache::lonnet::get_passwdconf($domain); - my ($min,$max,@chars,@brokerule,$warning); - $min = $Apache::lonnet::passwdmin; - if (ref($passwdconf{'chars'}) eq 'ARRAY') { - if ($passwdconf{'min'} =~ /^\d+$/) { - if ($passwdconf{'min'} > $min) { - $min = $passwdconf{'min'}; - } - } - if ($passwdconf{'max'} =~ /^\d+$/) { - $max = $passwdconf{'max'}; - } - @chars = @{$passwdconf{'chars'}}; - } - if (($min) && (length($plainpass) < $min)) { - push(@brokerule,'min'); - } - if (($max) && (length($plainpass) > $max)) { - push(@brokerule,'max'); - } - if (@chars) { - my %rules; - map { $rules{$_} = 1; } @chars; - if ($rules{'uc'}) { - unless ($plainpass =~ /[A-Z]/) { - push(@brokerule,'uc'); - } - } - if ($rules{'lc'}) { - unless ($plainpass =~ /[a-z]/) { - push(@brokerule,'lc'); - } - } - if ($rules{'num'}) { - unless ($plainpass =~ /\d/) { - push(@brokerule,'num'); - } - } - if ($rules{'spec'}) { - unless ($plainpass =~ /[!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/) { - push(@brokerule,'spec'); - } - } - } - if (@brokerule) { - my %rulenames = &Apache::lonlocal::texthash( - uc => 'At least one upper case letter', - lc => 'At least one lower case letter', - num => 'At least one number', - spec => 'At least one non-alphanumeric', - ); - $rulenames{'uc'} .= ': ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $rulenames{'lc'} .= ': abcdefghijklmnopqrstuvwxyz'; - $rulenames{'num'} .= ': 0123456789'; - $rulenames{'spec'} .= ': !"\#$%&\'()*+,-./:;<=>?@[\]^_\`{|}~'; - $rulenames{'min'} = &mt('Minimum password length: [_1]',$min); - $rulenames{'max'} = &mt('Maximum password length: [_1]',$max); - $warning = &mt('Password did not satisfy the following:').''; - } - if (wantarray) { - return @brokerule; - } - return $warning; -} - -sub passwd_validation_js { - my ($currpasswdval,$domain,$context,$id) = @_; - my (%passwdconf,$alertmsg); - if ($context eq 'linkprot') { - my %domconfig = &Apache::lonnet::get_dom('configuration',['ltisec'],$domain); - if (ref($domconfig{'ltisec'}) eq 'HASH') { - if (ref($domconfig{'ltisec'}{'rules'}) eq 'HASH') { - %passwdconf = %{$domconfig{'ltisec'}{'rules'}}; - } - } - if ($id eq 'add') { - $alertmsg = &mt('Secret for added launcher did not satisfy requirement(s):').'\n\n'; - } elsif ($id =~ /^\d+$/) { - my $pos = $id+1; - $alertmsg = &mt('Secret for launcher [_1] did not satisfy requirement(s):','#'.$pos).'\n\n'; - } else { - $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n'; - } - } else { - %passwdconf = &Apache::lonnet::get_passwdconf($domain); - $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n'; - } - my ($min,$max,@chars,$numrules,$intargjs,%alert); - $numrules = 0; - $min = $Apache::lonnet::passwdmin; - if (ref($passwdconf{'chars'}) eq 'ARRAY') { - if ($passwdconf{'min'} =~ /^\d+$/) { - if ($passwdconf{'min'} > $min) { - $min = $passwdconf{'min'}; - } - } - if ($passwdconf{'max'} =~ /^\d+$/) { - $max = $passwdconf{'max'}; - $numrules ++; - } - @chars = @{$passwdconf{'chars'}}; - if (@chars) { - $numrules ++; - } - } - if ($min > 0) { - $numrules ++; - } - if (($min > 0) || ($max ne '') || (@chars > 0)) { - if ($min) { - $alert{'min'} = &mt('minimum [quant,_1,character]',$min).'\n'; - } - if ($max) { - $alert{'max'} = &mt('maximum [quant,_1,character]',$max).'\n'; - } - my (@charalerts,@charrules); - if (@chars) { - if (grep(/^uc$/,@chars)) { - push(@charalerts,&mt('contain at least one upper case letter')); - push(@charrules,'uc'); - } - if (grep(/^lc$/,@chars)) { - push(@charalerts,&mt('contain at least one lower case letter')); - push(@charrules,'lc'); - } - if (grep(/^num$/,@chars)) { - push(@charalerts,&mt('contain at least one number')); - push(@charrules,'num'); - } - if (grep(/^spec$/,@chars)) { - push(@charalerts,&mt('contain at least one non-alphanumeric')); - push(@charrules,'spec'); - } - } - $intargjs = qq| var rulesmsg = '';\n|. - qq| var currpwval = $currpasswdval;\n|; - if ($min) { - $intargjs .= qq| - if (currpwval.length < $min) { - rulesmsg += ' - $alert{min}'; - } -|; - } - if ($max) { - $intargjs .= qq| - if (currpwval.length > $max) { - rulesmsg += ' - $alert{max}'; - } -|; - } - if (@chars > 0) { - my $charrulestr = '"'.join('","',@charrules).'"'; - my $charalertstr = '"'.join('","',@charalerts).'"'; - $intargjs .= qq| var brokerules = new Array();\n|. - qq| var charrules = new Array($charrulestr);\n|. - qq| var charalerts = new Array($charalertstr);\n|; - my %rules; - map { $rules{$_} = 1; } @chars; - if ($rules{'uc'}) { - $intargjs .= qq| - var ucRegExp = /[A-Z]/; - if (!ucRegExp.test(currpwval)) { - brokerules.push('uc'); - } -|; - } - if ($rules{'lc'}) { - $intargjs .= qq| - var lcRegExp = /[a-z]/; - if (!lcRegExp.test(currpwval)) { - brokerules.push('lc'); - } -|; - } - if ($rules{'num'}) { - $intargjs .= qq| - var numRegExp = /[0-9]/; - if (!numRegExp.test(currpwval)) { - brokerules.push('num'); - } -|; - } - if ($rules{'spec'}) { - $intargjs .= q| - var specRegExp = /[!"#$%&'()*+,\-.\/:;<=>?@[\\^\]_`{\|}~]/; - if (!specRegExp.test(currpwval)) { - brokerules.push('spec'); - } -|; - } - $intargjs .= qq| - if (brokerules.length > 0) { - for (var i=0; inew; + if ($language) { + $speller->set_option('lang', $language); + } + + # Turn the word list into an array of words by splittingon whitespace + + my @words = split(/\s+/, $wordlist); + + foreach my $word (@words) { + if(! $speller->check($word)) { + push(@misspellings, $word); + } + } + return join(' ', @misspellings); + +} + # -------------------------------------------------------------- Plaintext name =pod @@ -4001,30 +3956,6 @@ sub syllabuswrapper { return qq{$linktext}; } -sub aboutme_on { - my ($uname,$udom)=@_; - unless ($uname) { $uname=$env{'user.name'}; } - unless ($udom) { $udom=$env{'user.domain'}; } - return if ($udom eq 'public' && $uname eq 'public'); - my $hashkey=$uname.':'.$udom; - my ($aboutme,$cached)=&Apache::lonnet::is_cached_new('aboutme',$hashkey); - if ($cached) { - return $aboutme; - } - $aboutme = &Apache::lonnet::usertools_access($uname,$udom,'aboutme'); - &Apache::lonnet::do_cache_new('aboutme',$hashkey,$aboutme,3600); - return $aboutme; -} - -sub devalidate_aboutme_cache { - my ($uname,$udom)=@_; - if (!$udom) { $udom =$env{'user.domain'}; } - if (!$uname) { $uname=$env{'user.name'}; } - return if ($udom eq 'public' && $uname eq 'public'); - my $id=$uname.':'.$udom; - &Apache::lonnet::devalidate_cache_new('aboutme',$id); -} - # ----------------------------------------------------------------------------- sub track_student_link { @@ -4244,7 +4175,11 @@ category sub filecategorytypes { my ($cat) = @_; - return @{$category_extensions{lc($cat)}}; + if (ref($category_extensions{lc($cat)}) eq 'ARRAY') { + return @{$category_extensions{lc($cat)}}; + } else { + return (); + } } =pod @@ -4411,7 +4346,7 @@ Return string with previous attempt on p =item * $usec: section of the desired student -=item * $identifier: counter for student (multiple students one problem) or +=item * $identifier: counter for student (multiple students one problem) or problem (one student; whole sequence). =back @@ -4498,7 +4433,7 @@ sub get_previous_attempt { my (@hidden,@unsolved); if (%typeparts) { foreach my $id (keys(%typeparts)) { - if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || + if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) { push(@hidden,$id); } elsif ($identifier ne '') { @@ -4559,7 +4494,7 @@ sub get_previous_attempt { if ($key =~ /\./) { my $value = $returnhash{$version.':'.$key}; if ($key =~ /\.rndseed$/) { - my ($id) = ($key =~ /^(.+)\.rndseed$/); + my ($id) = ($key =~ /^(.+)\.[^.]+$/); if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) { $value = $returnhash{$version.':'.$id.'.rawrndseed'}; } @@ -4576,7 +4511,7 @@ sub get_previous_attempt { next if ($key =~ /\.foilorder$/); my $value = $returnhash{$version.':'.$key}; if ($key =~ /\.rndseed$/) { - my ($id) = ($key =~ /^(.+)\.rndseed$/); + my ($id) = ($key =~ /^(.+)\.[^.]+$/); if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) { $value = $returnhash{$version.':'.$id.'.rawrndseed'}; } @@ -4607,7 +4542,7 @@ sub get_previous_attempt { if ($key =~/$regexp$/ && (defined &$gradesub)) { $value = &$gradesub($value); } - $prevattempts.=''.$value.' '; + $prevattempts.=''. $value.' '; } else { $prevattempts.=' '; } @@ -4623,20 +4558,14 @@ sub get_previous_attempt { if ($key =~/$regexp$/ && (defined &$gradesub)) { $value = &$gradesub($value); } - $prevattempts.=''.$value.' '; + $prevattempts.=''.$value.' '; } } $prevattempts.= &end_data_table_row().&end_data_table(); } else { - my $msg; - if ($symb =~ /ext\.tool$/) { - $msg = &mt('No grade passed back.'); - } else { - $msg = &mt('Nothing submitted - no attempts.'); - } $prevattempts= &start_data_table().&start_data_table_row(). - ''.$msg.''. + ''.&mt('Nothing submitted - no attempts.').''. &end_data_table_row().&end_data_table(); } } else { @@ -4650,11 +4579,13 @@ sub get_previous_attempt { sub format_previous_attempt_value { my ($key,$value) = @_; if (($key =~ /timestamp/) || ($key=~/duedate/)) { - $value = &Apache::lonlocal::locallocaltime($value); + $value = &Apache::lonlocal::locallocaltime($value); } elsif (ref($value) eq 'ARRAY') { - $value = '('.join(', ', @{ $value }).')'; + $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&'); } elsif ($key =~ /answerstring$/) { my %answers = &Apache::lonnet::str2hash($value); + my @answer = %answers; + %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer; my @anskeys = sort(keys(%answers)); if (@anskeys == 1) { my $answer = $answers{$anskeys[0]}; @@ -4677,7 +4608,7 @@ sub format_previous_attempt_value { } } } else { - $value = &unescape($value); + $value = &HTML::Entities::encode(&unescape($value), '"<>&'); } return $value; } @@ -4783,59 +4714,6 @@ sub get_student_view_with_retries { } } -sub css_links { - my ($currsymb,$level) = @_; - my ($links,@symbs,%cssrefs,%httpref); - if ($level eq 'map') { - my $navmap = Apache::lonnavmaps::navmap->new(); - if (ref($navmap)) { - my ($map,undef,$url)=&Apache::lonnet::decode_symb($currsymb); - my @resources = $navmap->retrieveResources($map,sub { $_[0]->is_problem() },0,0); - foreach my $res (@resources) { - if (ref($res) && $res->symb()) { - push(@symbs,$res->symb()); - } - } - } - } else { - @symbs = ($currsymb); - } - foreach my $symb (@symbs) { - my $css_href = &Apache::lonnet::EXT('resource.0.cssfile',$symb); - if ($css_href =~ /\S/) { - unless ($css_href =~ m{https?://}) { - my $url = (&Apache::lonnet::decode_symb($symb))[-1]; - my $proburl = &Apache::lonnet::clutter($url); - my ($probdir) = ($proburl =~ m{(.+)/[^/]+$}); - unless ($css_href =~ m{^/}) { - $css_href = &Apache::lonnet::hreflocation($probdir,$css_href); - } - if ($css_href =~ m{^/(res|uploaded)/}) { - unless (($httpref{'httpref.'.$css_href}) || - (&Apache::lonnet::is_on_map($css_href))) { - my $thisurl = $proburl; - if ($env{'httpref.'.$proburl}) { - $thisurl = $env{'httpref.'.$proburl}; - } - $httpref{'httpref.'.$css_href} = $thisurl; - } - } - } - $cssrefs{$css_href} = 1; - } - } - if (keys(%httpref)) { - &Apache::lonnet::appenv(\%httpref); - } - if (keys(%cssrefs)) { - foreach my $css_href (keys(%cssrefs)) { - next unless ($css_href =~ m{^(/res/|/uploaded/|https?://)}); - $links .= ''."\n"; - } - } - return $links; -} - =pod =item * &get_student_answers() @@ -5091,96 +4969,13 @@ sub findallcourses { ############################################### sub blockcheck { - my ($setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_; + my ($setters,$activity,$uname,$udom,$url,$is_course) = @_; - unless (($activity eq 'docs') || ($activity eq 'reinit') || ($activity eq 'alert')) { - 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))) { my ($startblock,$endblock,$triggerblock) = - &get_blocks($setters,$activity,$udom,$uname,$url,$symb,$caller); + &get_blocks($setters,$activity,$udom,$uname,$url); return ($startblock,$endblock,$triggerblock); } } else { @@ -5191,18 +4986,15 @@ sub blockcheck { my $startblock = 0; my $endblock = 0; my $triggerblock = ''; - my %live_courses; - unless (($activity eq 'wishlist') || ($activity eq 'annotate')) { - %live_courses = &findallcourses(undef,$uname,$udom); - } + my %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') && ($env{'request.course.id'})) { + $activity eq 'reinit' || $activity eq 'alert') && + ($env{'request.course.id'})) { foreach my $key (keys(%live_courses)) { if ($key ne $env{'request.course.id'}) { delete($live_courses{$key}); @@ -5307,11 +5099,11 @@ sub blockcheck { ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E})); next if ($no_userblock); - # Retrieve blocking times and identity of blocker for course + # Retrieve blocking times and identity of locker for course # of specified user, unless user has 'evb' privilege. - + my ($start,$end,$trigger) = - &get_blocks($setters,$activity,$cdom,$cnum,$url,$symb,$caller); + &get_blocks($setters,$activity,$cdom,$cnum,$url); if (($start != 0) && (($startblock == 0) || ($startblock > $start))) { $startblock = $start; @@ -5331,7 +5123,7 @@ sub blockcheck { } sub get_blocks { - my ($setters,$activity,$cdom,$cnum,$url,$symb,$caller) = @_; + my ($setters,$activity,$cdom,$cnum,$url) = @_; my $startblock = 0; my $endblock = 0; my $triggerblock = ''; @@ -5344,13 +5136,7 @@ sub get_blocks { my $now = time; my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum); if ($activity eq 'docs') { - my ($blocked,$nosymbcache,$noenccheck); - if (($caller eq 'blockedaccess') || ($caller eq 'blockingstatus')) { - $blocked = 1; - $nosymbcache = 1; - $noenccheck = 1; - } - @blockers = &Apache::lonnet::has_comm_blocking('bre',$symb,$url,$nosymbcache,$noenccheck,$blocked,\%commblocks); + @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks); foreach my $block (@blockers) { if ($block =~ /^firstaccess____(.+)$/) { my $item = $1; @@ -5478,17 +5264,14 @@ sub parse_block_record { } sub blocking_status { - my ($activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_; + my ($activity,$uname,$udom,$url,$is_course) = @_; my %setters; # check for active blocking - 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 ($startblock,$endblock,$triggerblock) = + &blockcheck(\%setters,$activity,$uname,$udom,$url,$is_course); my $blocked = 0; - if (($startblock && $endblock) || ($by_ip)) { + if ($startblock && $endblock) { $blocked = 1; } @@ -5497,17 +5280,12 @@ sub blocking_status { # build a link to a popup window containing the details my $querystring = "?activity=$activity"; -# $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$/); +# $uname and $udom decide whose portfolio the user is trying to look at + if (($activity eq 'port') || ($activity eq 'passwd')) { + $querystring .= "&udom=$udom" if ($udom =~ /^$match_domain$/); $querystring .= "&uname=$uname" if ($uname =~ /^$match_username$/); } elsif ($activity eq 'docs') { - my $showurl = &Apache::lonenc::check_encrypt($url); - $querystring .= '&url='.&HTML::Entities::encode($showurl,'\'&"<>'); - if ($symb) { - my $showsymb = &Apache::lonenc::check_encrypt($symb); - $querystring .= '&symb='.&HTML::Entities::encode($showsymb,'\'&"<>'); - } + $querystring .= '&url='.&HTML::Entities::encode($url,'&"'); } my $output .= <<'END_MYBLOCK'; @@ -5532,20 +5310,10 @@ END_MYBLOCK $text = &mt('Printing Blocked'); } elsif ($activity eq 'passwd') { $text = &mt('Password Changing Blocked'); - } elsif ($activity eq 'grades') { - $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";
@@ -5569,15 +5337,8 @@ sub check_ip_acc { if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) { return 1; } - 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(); - $ip = $remote_ip || $env{'request.host'} || $clientip; - } + my $allowed; + my $ip=$ENV{'REMOTE_ADDR'} || $clientip || $env{'request.host'}; my $name; my %access = ( @@ -5589,17 +5350,18 @@ sub check_ip_acc { foreach my $item (split(',',$acc)) { $item =~ s/^\s*//; $item =~ s/\s*$//; + my $pattern; 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 $numdenies = scalar(@denies); + my $numallows = scalar(@allows); + my $count = 0; + foreach my $pattern (@denies,@allows) { + $count ++; my $acctype = 'allowfrom'; if ($count <= $numdenies) { $acctype = 'denyfrom'; @@ -5710,6 +5472,7 @@ sub get_domainconf { my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'}; $designhash{$udom.'.login.loginvia'} = $server; if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') { + $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'}; } else { $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'}; @@ -5726,17 +5489,6 @@ 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','window','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} = @@ -5801,7 +5553,7 @@ sub get_legacy_domconf { my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors'; my $designfile = $designdir.'/'.$udom.'.tab'; if (-e $designfile) { - if ( open (my $fh,'<',$designfile) ) { + if ( open (my $fh,"<$designfile") ) { while (my $line = <$fh>) { next if ($line =~ /^\#/); chomp($line); @@ -5841,12 +5593,8 @@ sub domainlogo { &Apache::lonnet::repcopy($local_name); } $imgsrc = &lonhttpdurl($imgsrc); - } - my $alttext = $domain; - if ($designhash{$domain.'.login.alttext_domlogo'} ne '') { - $alttext = $designhash{$domain.'.login.alttext_domlogo'}; - } - return ''; + } + return ''.$domain.''; } elsif (defined(&Apache::lonnet::domain($domain,'description'))) { return &Apache::lonnet::domain($domain,'description'); } else { @@ -5964,10 +5712,6 @@ sub head_subbox { Input: (optional) filename from which breadcrumb trail is built. In most cases no input as needed, as $env{'request.filename'} is appropriate for use in building the breadcrumb trail. - frameset flag - If page header is being requested for use in a frameset, then - the second (option) argument -- frameset will be true, and - the target attribute set for links should be target="_parent". Returns: HTML div with CSTR path and recent box To be included on Authoring Space pages @@ -5975,7 +5719,7 @@ Returns: HTML div with CSTR path and rec =cut sub CSTR_pageheader { - my ($trailfile,$frameset) = @_; + my ($trailfile) = @_; if ($trailfile eq '') { $trailfile = $env{'request.filename'}; } @@ -5998,24 +5742,23 @@ sub CSTR_pageheader { $lastitem = $thisdisfn; } - my ($target,$crumbtarget) = (' target="_top"','_top'); - if ($frameset) { - $target = ' target="_parent"'; - $crumbtarget = '_parent'; - } elsif (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) { - $target = ''; - $crumbtarget = ''; - } elsif (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'})) { - $target = ' target="'.$env{'request.deeplink.target'}.'"'; - $crumbtarget = $env{'request.deeplink.target'}; + my ($crsauthor,$title); + if (($env{'request.course.id'}) && + ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) && + ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) { + $crsauthor = 1; + $title = &mt('Course Authoring Space'); + } else { + $title = &mt('Authoring Space'); } my $output = '
' .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it? - .''.&mt('Authoring Space:').' ' - .'
' - .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef); + .''.$title.' ' + .'' #FIXME lonpubdir: target="_parent" + .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef); if ($lastitem) { $output .= @@ -6023,156 +5766,20 @@ sub CSTR_pageheader { .$lastitem .''; } - $output .= - '
' - #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/',$crumbtarget,'/priv','','+1',1)."
" - .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()') - .'
' - .&Apache::lonmenu::constspaceform($frameset) - .'
'; - - return $output; -} - -############################################## -=pod - -=item * &nocodemirror() - -Input: None - -Returns: 1 if CodeMirror is deactivated based on - user's preference, or domain default, - if user indicated use of default. -=cut - -sub nocodemirror { - my $nocodem = $env{'environment.nocodemirror'}; - unless ($nocodem) { - my %domdefs = &Apache::lonnet::get_domain_defaults($env{'user.domain'}); - if ($domdefs{'nocodemirror'}) { - $nocodem = 'yes'; - } - } - if ($nocodem eq 'yes') { - return 1; - } - return; -} - -############################################## -=pod - -=item * &permitted_editors() - -Input: $uri (optional) - -Returns: %editors hash in which keys are editors - permitted in current Authoring Space, - or in current course for web pages - created in a course. - - Value for each key is 1. Possible keys - are: edit, xml, and daxe. - - For a regular Authoring Space, if no specific - set of editors has been set for the Author - who owns the Authoring Space, then the - domain default will be used. If no domain - default has been set, then the keys will be - edit and xml. - - For a course author, or for web pages created - in a course, if no specific set of editors has - been set for the course, then the domain - course default will be used. If no domain - course default has been set, then the keys - will be edit and xml. - -=cut - -sub permitted_editors { - my ($uri) = @_; - my ($is_author,$is_coauthor,$is_course,$auname,$audom,%editors); - if ($env{'request.role'} =~ m{^au\./}) { - $is_author = 1; - } elsif ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)}) { - ($audom,$auname) = ($1,$2); - if (($audom ne '') && ($auname ne '')) { - if (($env{'user.domain'} eq $audom) && - ($env{'user.name'} eq $auname)) { - $is_author = 1; - } else { - $is_coauthor = 1; - } - } - } elsif ($env{'request.course.id'}) { - my ($cdom,$cnum); - $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - if (($env{'request.editurl'} =~ m{^/priv/\Q$cdom/$cnum\E/}) || - ($env{'request.editurl'} =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}) || - ($uri =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/})) { - $is_course = 1; - } elsif ($env{'request.editurl'} =~ m{^/priv/($match_domain)/($match_username)/}) { - ($audom,$auname) = ($1,$2); - } elsif ($env{'request.uri'} =~ m{^/priv/($match_domain)/($match_username)/}) { - ($audom,$auname) = ($1,$2); - } elsif (($uri eq '/daxesave') && - (($env{'form.path'} =~ m{^/daxeopen/priv/\Q$cdom/$cnum\E/}) || - ($env{'form.path'} =~ m{^/daxeopen/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}))) { - $is_course = 1; - } elsif (($uri eq '/daxesave') && - ($env{'form.path'} =~ m{^/daxeopen/priv/($match_domain)/($match_username)/})) { - ($audom,$auname) = ($1,$2); - } - unless ($is_course) { - if (($audom ne '') && ($auname ne '')) { - if (($env{'user.domain'} eq $audom) && - ($env{'user.name'} eq $auname)) { - $is_author = 1; - } else { - $is_coauthor = 1; - } - } - } - } - if ($is_author) { - if (exists($env{'environment.editors'})) { - map { $editors{$_} = 1; } split(/,/,$env{'environment.editors'}); - } else { - %editors = ( edit => 1, - xml => 1, - ); - } - } elsif ($is_coauthor) { - if (exists($env{"environment.internal.editors./$audom/$auname"})) { - map { $editors{$_} = 1; } split(/,/,$env{"environment.internal.editors./$audom/$auname"}); - } else { - %editors = ( edit => 1, - xml => 1, - ); - } - } elsif ($is_course) { - if (exists($env{'course.'.$env{'request.course.id'}.'.internal.crseditors'})) { - map { $editors{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.internal.crseditors'}); - } else { - my %domdefaults = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); - if (exists($domdefaults{'crseditors'})) { - map { $editors{$_} = 1; } split(/,/,$domdefaults{'crseditors'}); - } else { - %editors = ( edit => 1, - xml => 1, - ); - } - } + if ($crsauthor) { + $output .= ''.&Apache::lonmenu::constspaceform(); } else { - %editors = ( edit => 1, - xml => 1, - ); + $output .= + '
' + #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."
" + .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()') + .'' + .&Apache::lonmenu::constspaceform(); } - return %editors; + $output .= '
'; + + return $output; } ############################################### @@ -6212,9 +5819,6 @@ Inputs: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value -=item * $no_inline_link, if true and in remote mode, don't show the - 'Switch To Inline Menu' link - =item * $args, optional argument valid values are no_auto_mt_title -> prevents &mt()ing the title arg use_absolute -> for external resource or syllabus, this will @@ -6226,34 +5830,6 @@ Inputs: inlineremote items to be added in "Functions" menu below breadcrumbs. -=item * $ltiscope, optional argument, will be one of: resource, map or - course, if LON-CAPA is in LTI Provider context. Value is - the scope of use, i.e., launch was for access to a single, a map - or the entire course. - -=item * $ltiuri, optional argument, if LON-CAPA is in LTI Provider - context, this will contain the URL for the landing item in - the course, after launch from an LTI Consumer - -=item * $ltimenu, optional argument, if LON-CAPA is in LTI Provider - context, this will contain a reference to hash of items - to be included in the page header and/or inline menu. - -=item * $menucoll, optional argument, if specific menu collection is in - effect, either set as the default for the course, or set for - the deeplink paramater for $env{'request.deeplink.login'} - then $menucoll will be the number of that collection. - -=item * $menuref, optional argument, reference to a hash, containing the - menu options included for the menu in effect, based on the - configuration for the numbered menu collection in use. - -=item * $showncrumbsref, reference to a scalar. Calls to lonmenu::innerregister - within &bodytag() can result in calls to lonhtmlcommon::breadcrumbs(), - if so, $showncrumbsref is set there to 1, and will propagate back - via &bodytag() to &start_page(), to prevent lonhtmlcommon::breadcrumbs() - being called a second time. - =back Returns: A uniform header for LON-CAPA web pages. @@ -6265,8 +5841,7 @@ other decorations will be returned. sub bodytag { my ($title,$function,$addentries,$bodyonly,$domain,$forcereg, - $no_nav_bar,$bgcolor,$no_inline_link,$args,$advtoolsref, - $ltiscope,$ltiuri,$ltimenu,$menucoll,$menuref,$showncrumbsref)=@_; + $no_nav_bar,$bgcolor,$args,$advtoolsref)=@_; my $public; if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')) @@ -6295,24 +5870,12 @@ 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+)$}) { @@ -6322,12 +5885,12 @@ sub bodytag { $role = &mt('Helpdesk[_1]',' '.$2); } } else { - $role = (split(/\//,$role,4))[-1]; + $role = (split(/\//,$role,4))[-1]; } - if ($sec) { - $role .= (' 'x2).'- '.&mt('section:').' '.$sec; + if ($env{'request.course.sec'}) { + $role .= (' 'x2).'- '.&mt('section:').' '.$env{'request.course.sec'}; } - $realm = $env{'course.'.$cid.'.description'}; + $realm = $env{'course.'.$env{'request.course.id'}.'.description'}; } else { $role = &Apache::lonnet::plaintext($role); } @@ -6349,66 +5912,32 @@ sub bodytag { if ($public) { undef($role); } - - my $showcrstitle = 1; - if (($cid) && ($env{'request.lti.login'})) { - if (ref($ltimenu) eq 'HASH') { - unless ($ltimenu->{'role'}) { - undef($role); - } - unless ($ltimenu->{'coursetitle'}) { - $realm=' '; - $showcrstitle = 0; - } - } - } elsif (($cid) && ($menucoll)) { - if (ref($menuref) eq 'HASH') { - unless ($menuref->{'role'}) { - undef($role); - } - unless ($menuref->{'crs'}) { - $realm=' '; - $showcrstitle = 0; - } - } - } - + my $titleinfo = '

'.$title.'

'; # # Extra info if you are the DC my $dc_info = ''; - if (($env{'user.adv'}) && ($env{'request.course.id'}) && $showcrstitle && - (exists($env{'user.role.dc./'.$env{'course.'.$cid.'.domain'}.'/'}))) { + if ($env{'user.adv'} && exists($env{'user.role.dc./'. + $env{'course.'.$env{'request.course.id'}. + '.domain'}.'/'})) { + my $cid = $env{'request.course.id'}; $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'}; $dc_info =~ s/\s+$//; } my $crstype; - if ($cid) { - $crstype = $env{'course.'.$cid.'.type'}; + if ($env{'request.course.id'}) { + $crstype = $env{'course.'.$env{'request.course.id'}.'.type'}; } elsif ($args->{'crstype'}) { $crstype = $args->{'crstype'}; } - - $role = '('.$role.')' if ($role && !$env{'browser.mobile'}); - - if ($env{'request.state'} eq 'construct') { $forcereg=1; } - - - - my $funclist; - if (($env{'environment.remote'} eq 'on') && ($env{'request.state'} ne 'construct')) { - $bodytag .= Apache::lonhtmlcommon::scripttag(Apache::lonmenu::utilityfunctions($httphost), 'start')."\n". - Apache::lonmenu::serverform(); - my $forbodytag; - &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, - $forcereg,$args->{'group'}, - $args->{'bread_crumbs'}, - $advtoolsref,'','',\$forbodytag); - unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') { - $funclist = $forbodytag; - } + if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) { + undef($role); } else { + $role = '('.$role.')' if ($role && !$env{'browser.mobile'}); + } + + if ($env{'request.state'} eq 'construct') { $forcereg=1; } # if ($env{'request.state'} eq 'construct') { # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls @@ -6417,87 +5946,51 @@ sub bodytag { $bodytag .= Apache::lonhtmlcommon::scripttag( Apache::lonmenu::utilityfunctions($httphost), 'start'); - my $collapsible; - if ($args->{'collapsible_header'} ne '') { - $collapsible = 1; - my ($menustate,$tiptext,$divclass); - if ($args->{'start_collapsed'}) { - $menustate = 'collapsed'; - $tiptext = 'display'; - $divclass = 'hidden'; - } else { - $menustate = 'expanded'; - $tiptext = 'hide'; - $divclass = 'shown'; - } - my $alttext = &mt('menu state: '.$menustate); - my $tooltip = &mt($tiptext.' standard menus'); - $bodytag .= <<"END"; -
-
-$alttext
-
-END + my ($left,$right) = Apache::lonmenu::primary_menu($crstype); + + if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) { + if ($dc_info) { + $dc_info = qq|$dc_info|; + } + $bodytag .= qq|
$left $role
+ $realm $dc_info
|; + return $bodytag; } - unless ($args->{'no_primary_menu'}) { - my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref, - $args->{'links_disabled'}, - $args->{'links_target'}, - $collapsible); - if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) { - if ($dc_info) { - $dc_info = qq|$dc_info|; - } - $bodytag .= qq|
$left $role
- $realm $dc_info
|; - return $bodytag; - } - unless ($env{'request.symb'} =~ m/\.page___\d+___/) { - $bodytag .= qq|
$left $role
|; - } + unless ($env{'request.symb'} =~ m/\.page___\d+___/) { + $bodytag .= qq|
$left $role
|; + } - $bodytag .= $right; + $bodytag .= $right; - if ($dc_info) { - $dc_info = &dc_courseid_toggle($dc_info); - } - $bodytag .= qq|
$realm $dc_info
|; + if ($dc_info) { + $dc_info = &dc_courseid_toggle($dc_info); } + $bodytag .= qq|
$realm $dc_info
|; - #if directed to not display the secondary menu, don't. + #if directed to not display the secondary menu, don't. if ($args->{'no_secondary_menu'}) { return $bodytag; } #don't show menus for public users if (!$public){ - unless ($args->{'no_inline_menu'}) { - $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu, - $args->{'no_primary_menu'}, - $menucoll,$menuref, - $args->{'links_disabled'}, - $args->{'links_target'}); - } + $bodytag .= Apache::lonmenu::secondary_menu($httphost); $bodytag .= Apache::lonmenu::serverform(); $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); if ($env{'request.state'} eq 'construct') { $bodytag .= &Apache::lonmenu::innerregister($forcereg, - $args->{'bread_crumbs'},'','',$hostname, - $ltiscope,$ltiuri,$showncrumbsref); + $args->{'bread_crumbs'},'','',$hostname); } elsif ($forcereg) { $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef, - $args->{'group'},$args->{'hide_buttons'}, - $hostname,$ltiscope,$ltiuri,$showncrumbsref); + $args->{'group'}, + $args->{'hide_buttons'}, + $hostname); } else { - my $forbodytag; - &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, - $forcereg,$args->{'group'}, - $args->{'bread_crumbs'}, - $advtoolsref,'',$hostname, - \$forbodytag); - unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') { - $bodytag .= $forbodytag; - } + $bodytag .= + &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, + $forcereg,$args->{'group'}, + $args->{'bread_crumbs'}, + $advtoolsref,'',$hostname); } }else{ # this is to seperate menu from content when there's no secondary @@ -6505,60 +5998,8 @@ END $bodytag .= '
'; $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); } - if ($args->{'collapsible_header'} ne '') { - $bodytag .= $args->{'collapsible_header'}. - '
'. - '
'; - } - return $bodytag; - } -# -# Top frame rendering, Remote is up -# - - my $imgsrc = $img; - if ($img =~ /^\/adm/) { - $imgsrc = &lonhttpdurl($img); - } - my $upperleft=''.$function.''; - - my $help=($no_inline_link?'' - :&Apache::loncommon::top_nav_help('Help')); - - # Explicit link to get inline menu - my $menu= ($no_inline_link?'' - :''.&mt('Switch to Inline Menu Mode').''); - - if ($dc_info) { - $dc_info = qq|($dc_info)|; - } - - my $name = &plainname($env{'user.name'},$env{'user.domain'}); - unless ($public) { - $name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'}, - undef,'LC_menubuttons_link'); - } - - unless ($env{'form.inhibitmenu'}) { - $bodytag .= qq|
$name $role
-
    -
  1. $help
  2. -
  3. $menu
  4. -
$realm $dc_info
|; - } - if ($env{'request.state'} eq 'construct') { - if (!$public){ - if ($env{'request.state'} eq 'construct') { - $funclist = &Apache::lonhtmlcommon::scripttag( - &Apache::lonmenu::utilityfunctions($httphost), 'start'). - &Apache::lonhtmlcommon::scripttag('','end'). - &Apache::lonmenu::innerregister($forcereg, - $args->{'bread_crumbs'}); - } - } - } - return $bodytag."\n".$funclist; + return $bodytag; } sub dc_courseid_toggle { @@ -6590,15 +6031,8 @@ sub make_attr_string { delete($attr_ref->{$key}); } } - if ($env{'environment.remote'} eq 'on') { - $attr_ref->{'onload'} = - &Apache::lonmenu::loadevents(). $on_load; - $attr_ref->{'onunload'}= - &Apache::lonmenu::unloadevents().$on_unload; - } else { - $attr_ref->{'onload'} = $on_load; - $attr_ref->{'onunload'}= $on_unload; - } + $attr_ref->{'onload'} = $on_load; + $attr_ref->{'onunload'}= $on_unload; } my $attr_string; @@ -6634,45 +6068,12 @@ sub endbodytag { } if ( exists( $env{'internal.head.redirect'} ) ) { if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) { - my ($endbodyjs,$idattr); - if ($env{'internal.head.to_opener'}) { - my $linkid = 'LC_continue_link'; - $idattr = ' id="'.$linkid.'"'; - my $redirect_for_js = &js_escape($env{'internal.head.redirect'}); - $endbodyjs=< -// - -ENDJS - } $endbodytag= - "$endbodyjs
". + "
". &mt('Continue').''. $endbodytag; } } - if ((ref($args) eq 'HASH') && ($args->{'dashjs'})) { - $endbodytag = &Apache::lonhtmlcommon::dash_to_minus_js().$endbodytag; - } return $endbodytag; } @@ -6757,14 +6158,6 @@ form, .inline { display: inline; } -.LC_menus_content.shown{ - display: block; -} - -.LC_menus_content.hidden { - display: none; -} - .LC_right { text-align:right; } @@ -6785,12 +6178,6 @@ form, .inline { width:400px; } -#LC_collapsible_separator { - border: 1px solid black; - width: 99.9%; - height: 0px; -} - .LC_iframecontainer { width: 98%; margin: 0; @@ -6969,6 +6356,12 @@ ul.LC_breadcrumb_tools_outerlist li { float: right; } +.LC_placement_prog { + padding-right: 20px; + font-weight: bold; + font-size: 90%; +} + table#LC_title_bar td { background: $tabbg; } @@ -7059,11 +6452,6 @@ td.LC_menubuttons_text { background: $tabbg; } -td.LC_zero_height { - line-height: 0; - cellpadding: 0; -} - table.LC_data_table { border: 1px solid #000000; border-collapse: separate; @@ -7385,6 +6773,12 @@ td.LC_parm_overview_restrictions { border-collapse: collapse; } +span.LC_parm_recursive, +td.LC_parm_recursive { + font-weight: bold; + font-size: smaller; +} + table.LC_parm_overview_restrictions td { border-width: 1px 4px 1px 4px; border-style: solid; @@ -7654,8 +7048,7 @@ table.LC_prior_tries td { padding: 6px; } -.LC_answer_unknown, -.LC_answer_warning { +.LC_answer_unknown { background: orange; color: black; padding: 6px; @@ -7737,7 +7130,11 @@ table.LC_data_table tr > td.LC_docs_entr color: #990000; } -.LC_domprefs_email, +.LC_docs_alias { + color: #440055; +} + +.LC_docs_alias_name, .LC_docs_reinit_warn, .LC_docs_ext_edit { font-size: x-small; @@ -7986,11 +7383,6 @@ fieldset { /* overflow: hidden; */ } -fieldset#LC_selectuser { - margin: 0; - padding: 0; -} - article.geogebraweb div { margin: 0; } @@ -8039,7 +7431,7 @@ ol.LC_primary_menu li { line-height: 1.5em; } -ol.LC_primary_menu li a, +ol.LC_primary_menu li a, ol.LC_primary_menu li p { display: block; margin: 0; @@ -8054,7 +7446,7 @@ ol.LC_primary_menu li p span.LC_primary_ } ol.LC_primary_menu li p span.LC_primary_menu_innerarrow { - display: inline-block; + display: inline-block; width: 5%; float: right; text-align: right; @@ -8089,9 +7481,9 @@ ol.LC_primary_menu li:hover li, ol.LC_pr float: none; border-left: 1px solid black; border-right: 1px solid black; -/* A dark bottom border to visualize different menu options; +/* A dark bottom border to visualize different menu options; overwritten in the create_submenu routine for the last border-bottom of the menu */ - border-bottom: 1px solid $data_table_dark; + border-bottom: 1px solid $data_table_dark; } ol.LC_primary_menu li li p:hover { @@ -8534,10 +7926,6 @@ a#LC_content_toolbar_edittoplevel { background-image:url(/res/adm/pages/edittoplevel.gif); } -a#LC_content_toolbar_printout { - background-image:url(/res/adm/pages/printout.gif); -} - ul#LC_toolbar li a:hover { background-position: bottom center; } @@ -8655,26 +8043,6 @@ 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; - white-space: -pre-wrap; - white-space: -o-pre-wrap; - word-wrap: break-word; -} - /* styles used for response display */ @@ -8840,13 +8208,7 @@ Inputs: $title - optional title for the 3- whether the side effect should occur (side effect of setting $env{'internal.head.redirect'} to the url - redirected to) - 4- whether the redirect target should be - the opener of the current (pop-up) - window (side effect of setting - $env{'internal.head.to_opener'} to - 1, if true. - 5- whether encrypt check should be skipped + redirected too) domain -> force to color decorate a page for a specific domain function -> force usage of a specific rolish color scheme @@ -8883,8 +8245,8 @@ sub headtag { if (!$args->{'frameset'}) { $result .= &Apache::lonhtmlcommon::htmlareaheaders(); } - if ($args->{'force_register'}) { - $result .= &Apache::lonmenu::registerurl(1); + if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) { + $result .= Apache::lonxml::display_title(); } if (!$args->{'no_nav_bar'} && !$args->{'only_body'} @@ -8909,45 +8271,15 @@ sub headtag { } } if (ref($args->{'redirect'})) { - my ($time,$url,$inhibit_continue,$to_opener,$skip_enc_check) = @{$args->{'redirect'}}; - if (!$skip_enc_check) { - $url = &Apache::lonenc::check_encrypt($url); - } + my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}}; + $url = &Apache::lonenc::check_encrypt($url); if (!$inhibit_continue) { $env{'internal.head.redirect'} = $url; } - $result.=<<"ADDMETA"; + $result.=< -ADDMETA - if ($to_opener) { - $env{'internal.head.to_opener'} = 1; - my $dest = &js_escape($url); - my $timeout = int($time * 1000); - $result .=<<"ENDJS"; - -ENDJS - } else { - $result.=<<"ADDMETA"; ADDMETA - } } else { unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) { my $requrl = $env{'request.uri'}; @@ -8961,99 +8293,43 @@ ADDMETA my $dom_in_use = $Apache::lonnet::perlvar{'lonDefDomain'}; 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,$offloadoth); if (ref($domdefs{'offloadnow'}) eq 'HASH') { + my $lonhost = $Apache::lonnet::perlvar{'lonHostID'}; 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'}; + my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use); + if (($newserver) && ($newserver ne $lonhost)) { + my $numsec = 5; + my $timeout = $numsec * 1000; + my ($newurl,$locknum,%locks,$msg); + if ($env{'request.role.adv'}) { + ($locknum,%locks) = &Apache::lonnet::get_locks(); } - } - } - } - unless ($offload) { - if (ref($domdefs{'offloadoth'}) eq 'HASH') { - if ($domdefs{'offloadoth'}{$lonhost}) { - 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'})) { - $offload = 1; - $offloadoth = 1; - $dom_in_use = $env{'user.domain'}; - } + my $disable_submit = 0; + if ($requrl =~ /$LONCAPA::assess_re/) { + $disable_submit = 1; } - } - } - } - if ($offload) { - 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; - my ($newurl,$locknum,%locks,$msg); - if ($env{'request.role.adv'}) { - ($locknum,%locks) = &Apache::lonnet::get_locks(); - } - my $disable_submit = 0; - if ($requrl =~ /$LONCAPA::assess_re/) { - $disable_submit = 1; - } - if ($locknum) { - my @lockinfo = sort(values(%locks)); - $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".'); + if ($locknum) { + my @lockinfo = sort(values(%locks)); + $msg = &mt('Once the following tasks are complete: ')."\\n". + join(", ",sort(values(%locks)))."\\n". + &mt('your session will be transferred to a different server, after you click "Roles".'); } else { - $msg .= &mt('your session will be transferred to a different server, after you click "Roles".'); - } - } else { - if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) { - $msg = &mt('Your LON-CAPA submission has been recorded')."\n"; - } - $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec); - $newurl = '/adm/switchserver?otherserver='.$newserver; - if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) { - $newurl .= '&role='.$env{'request.role'}; - } - if ($env{'request.symb'}) { - my $shownsymb = &Apache::lonenc::check_encrypt($env{'request.symb'}); - if ($shownsymb =~ m{^/enc/}) { - my $reqdmajor = 2; - my $reqdminor = 11; - my $reqdsubminor = 3; - my $newserverrev = &Apache::lonnet::get_server_loncaparev('',$newserver); - my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$newserver); - my ($major,$minor,$subminor) = ($remoterev =~ /^\'?(\d+)\.(\d+)\.(\d+|)[\w.\-]+\'?$/); - if (($major eq '' && $minor eq '') || - (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)) || - (($reqdmajor == $major) && ($reqdminor == $minor) && (($subminor eq '') || - ($reqdsubminor > $subminor))))) { - undef($shownsymb); - } + if (($requrl =~ m{^/res/}) && ($env{'form.submitted'} =~ /^part_/)) { + $msg = &mt('Your LON-CAPA submission has been recorded')."\\n"; } - if ($shownsymb) { - &js_escape(\$shownsymb); - $newurl .= '&symb='.$shownsymb; + $msg .= &mt('Your current LON-CAPA session will be transferred to a different server in [quant,_1,second].',$numsec); + $newurl = '/adm/switchserver?otherserver='.$newserver; + if (($env{'request.role'}) && ($env{'request.role'} ne 'cm')) { + $newurl .= '&role='.$env{'request.role'}; + } + if ($env{'request.symb'}) { + $newurl .= '&symb='.$env{'request.symb'}; + } else { + $newurl .= '&origurl='.$requrl; } - } else { - my $shownurl = &Apache::lonenc::check_encrypt($requrl); - &js_escape(\$shownurl); - $newurl .= '&origurl='.$shownurl; } - } - &js_escape(\$msg); - $result.=< OFFLOAD + } } } } @@ -9084,16 +8361,12 @@ OFFLOAD $title = 'The LearningOnline Network with CAPA'; } if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); } - if ($title =~ /^LON-CAPA\s+/) { - $result .= ' '.$title.''; - } else { - $result .= ' LON-CAPA '.$title.''; - } - $result .= "\n".'' + .'{'frameset'}) { $result .= ' /'; } - $result .= '>' + $result .= '>' .$inhibitprint .$head_extra; my $clientmobile; @@ -9126,12 +8399,12 @@ sub font_settings { my $headerstring=''; if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) || ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) { - $headerstring.= - '{'frameset'}) { - $headerstring.= ' /'; + $headerstring.= ' /'; } - $headerstring .= '>'."\n"; + $headerstring .= '>'."\n"; } return $headerstring; } @@ -9176,8 +8449,7 @@ sub print_suppression { } my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $clientip = &Apache::lonnet::get_requestor_ip(); - my $blocked = &blocking_status('printout',$clientip,$cnum,$cdom,undef,1); + my $blocked = &blocking_status('printout',$cnum,$cdom,undef,1); if ($blocked) { my $checkrole = "cm./$cdom/$cnum"; if ($env{'request.course.sec'} ne '') { @@ -9285,23 +8557,17 @@ $args - additional optional args support skip_phases -> hash ref of head -> skip the generation body -> skip all generation - no_inline_link -> if true and in remote mode, don't show the - 'Switch To Inline Menu' link no_auto_mt_title -> prevent &mt()ing the title arg bread_crumbs -> Array containing breadcrumbs bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs bread_crumbs_nomenu -> if true will pass false as the value of $menulink to lonhtmlcommon::breadcrumbs - group -> includes the current group, if page is for a + group -> includes the current group, if page is for a specific group use_absolute -> for request for external resource or syllabus, this 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). - links_target -> Target for links, e.g., _parent (optional). =back @@ -9314,83 +8580,12 @@ sub start_page { #&Apache::lonnet::logthis("start_page ".join(':',caller(0))); $env{'internal.start_page'}++; - my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu,$menucoll,%menu); + my ($result,@advtools); if (! exists($args->{'skip_phases'}{'head'}) ) { $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args); } - - if (($env{'request.course.id'}) && ($env{'request.lti.login'})) { - if ($env{'course.'.$env{'request.course.id'}.'.lti.override'}) { - unless ($env{'course.'.$env{'request.course.id'}.'.lti.topmenu'}) { - $args->{'no_primary_menu'} = 1; - } - unless ($env{'course.'.$env{'request.course.id'}.'.lti.inlinemenu'}) { - $args->{'no_inline_menu'} = 1; - } - if ($env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}) { - map { $ltimenu{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}); - } - } else { - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider'); - if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') { - unless ($lti{$env{'request.lti.login'}}{'topmenu'}) { - $args->{'no_primary_menu'} = 1; - } - unless ($lti{$env{'request.lti.login'}}{'inlinemenu'}) { - $args->{'no_inline_menu'} = 1; - } - if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') { - map { $ltimenu{$_} = 1; } @{$lti{$env{'request.lti.login'}}{'lcmenu'}}; - } - } - } - ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'}, - $env{'course.'.$env{'request.course.id'}.'.domain'}, - $env{'course.'.$env{'request.course.id'}.'.num'}); - } elsif ($env{'request.course.id'}) { - my $expiretime=600; - if ((time-$env{'course.'.$env{'request.course.id'}.'.last_cache'}) > $expiretime) { - &Apache::lonnet::coursedescription($env{'request.course.id'},{'freshen_cache' => 1}); - } - my ($deeplinkmenu,$menuref); - ($menucoll,$deeplinkmenu,$menuref) = &menucoll_in_effect(); - if ($menucoll) { - if (ref($menuref) eq 'HASH') { - %menu = %{$menuref}; - } - if ($menu{'top'} eq 'n') { - $args->{'no_primary_menu'} = 1; - } - if ($menu{'inline'} eq 'n') { - unless (&Apache::lonnet::allowed('opa')) { - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $crstype = &course_type(); - my $now = time; - my $ccrole; - if ($crstype eq 'Community') { - $ccrole = 'co'; - } else { - $ccrole = 'cc'; - } - if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) { - my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}); - if ((($start) && ($start<0)) || - (($end) && ($end<$now)) || - (($start) && ($now<$start))) { - $args->{'no_inline_menu'} = 1; - } - } else { - $args->{'no_inline_menu'} = 1; - } - } - } - } - } - - my $showncrumbs; + if (! exists($args->{'skip_phases'}{'body'}) ) { if ($args->{'frameset'}) { my $attr_string = &make_attr_string($args->{'force_register'}, @@ -9402,9 +8597,8 @@ sub start_page { $args->{'function'}, $args->{'add_entries'}, $args->{'only_body'}, $args->{'domain'}, $args->{'force_register'}, $args->{'no_nav_bar'}, - $args->{'bgcolor'}, $args->{'no_inline_link'}, - $args, \@advtools, - $ltiscope,$ltiuri,\%ltimenu,$menucoll,\%menu,\$showncrumbs); + $args->{'bgcolor'}, $args, + \@advtools); } } @@ -9426,7 +8620,6 @@ sub start_page { #Breadcrumbs if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) { - unless ($showncrumbs) { &Apache::lonhtmlcommon::clear_breadcrumbs(); #if any br links exists, add them to the breadcrumbs if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') { @@ -9441,30 +8634,19 @@ sub start_page { my $menulink; # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item. if ((exists($args->{'bread_crumbs_nomenu'})) || - ($ltiscope eq 'map') || ($ltiscope eq 'resource')) { + ((($args->{'crstype'} eq 'Placement') || (($env{'request.course.id'}) && + ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement'))) && + (!$env{'request.role.adv'}))) { $menulink = 0; } else { undef($menulink); } - my $linkprotout; - if ($env{'request.deeplink.login'}) { - my $linkprotout = &Apache::lonmenu::linkprot_exit(); - if ($linkprotout) { - &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout); - } - } #if bread_crumbs_component exists show it as headline else show only the breadcrumbs if(exists($args->{'bread_crumbs_component'})){ $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink); - } else { + } else { $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink); } - } - } elsif (($env{'environment.remote'} eq 'on') && - ($env{'form.inhibitmenu'} ne 'yes') && - ($env{'request.noversionuri'} =~ m{^/res/}) && - ($env{'request.noversionuri'} !~ m{^/res/adm/pages/})) { - $result .= '

'; } return $result; } @@ -9501,147 +8683,6 @@ sub end_page { return $result; } -sub menucoll_in_effect { - my ($menucoll,$deeplinkmenu,%menu); - if ($env{'request.course.id'}) { - $menucoll = $env{'course.'.$env{'request.course.id'}.'.menudefault'}; - if ($env{'request.deeplink.login'}) { - my ($deeplink_symb,$deeplink,$check_login_symb); - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - if ($env{'request.noversionuri'} =~ m{^/(res|uploaded)/}) { - if ($env{'request.noversionuri'} =~ /\.(page|sequence)$/) { - my $navmap = Apache::lonnavmaps::navmap->new(); - if (ref($navmap)) { - $deeplink = $navmap->get_mapparam(undef, - &Apache::lonnet::declutter($env{'request.noversionuri'}), - '0.deeplink'); - } else { - $check_login_symb = 1; - } - } else { - my $symb=&Apache::lonnet::symbread(); - if ($symb) { - $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$symb); - } else { - $check_login_symb = 1; - } - } - } else { - $check_login_symb = 1; - } - if ($check_login_symb) { - $deeplink_symb = &deeplink_login_symb($cnum,$cdom); - if ($deeplink_symb =~ /\.(page|sequence)$/) { - my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]); - my $navmap = Apache::lonnavmaps::navmap->new(); - if (ref($navmap)) { - $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink'); - } - } else { - $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb); - } - } - if ($deeplink ne '') { - my ($state,$others,$listed,$scope,$protect,$display,$target) = split(/,/,$deeplink); - if ($display =~ /^\d+$/) { - $deeplinkmenu = 1; - $menucoll = $display; - } - } - } - if ($menucoll) { - %menu = &page_menu($env{'course.'.$env{'request.course.id'}.'.menucollections'},$menucoll); - } - } - return ($menucoll,$deeplinkmenu,\%menu); -} - -sub deeplink_login_symb { - my ($cnum,$cdom) = @_; - my $login_symb; - if ($env{'request.deeplink.login'}) { - $login_symb = &symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom); - } - return $login_symb; -} - -sub symb_from_tinyurl { - my ($url,$cnum,$cdom) = @_; - if ($url =~ m{^\Q/tiny/$cdom/\E(\w+)$}) { - my $key = $1; - my ($tinyurl,$login); - my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key); - if (defined($cached)) { - $tinyurl = $result; - } else { - my $configuname = &Apache::lonnet::get_domainconfiguser($cdom); - my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname); - if ($currtiny{$key} ne '') { - $tinyurl = $currtiny{$key}; - &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600); - } - } - if ($tinyurl ne '') { - my ($cnumreq,$symb) = split(/\&/,$tinyurl); - if (wantarray) { - return ($cnumreq,$symb); - } elsif ($cnumreq eq $cnum) { - return $symb; - } - } - } - if (wantarray) { - return (); - } else { - return; - } -} - -sub usable_exttools { - my %tooltypes; - if ($env{'request.course.id'}) { - if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'}) { - if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'both') { - %tooltypes = ( - crs => 1, - dom => 1, - ); - } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'crs') { - $tooltypes{'crs'} = 1; - } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'dom') { - $tooltypes{'dom'} = 1; - } - } else { - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - my $crstype = lc($env{'course.'.$env{'request.course.id'}.'.type'}); - if ($crstype eq '') { - $crstype = 'course'; - } - if ($crstype eq 'course') { - if ($env{'course.'.$env{'request.course.id'}.'internal.coursecode'}) { - $crstype = 'official'; - } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.textbook'}) { - $crstype = 'textbook'; - } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.lti'}) { - $crstype = 'lti'; - } else { - $crstype = 'unofficial'; - } - } - my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom); - if ($domdefaults{$crstype.'domexttool'}) { - $tooltypes{'dom'} = 1; - } - if ($domdefaults{$crstype.'exttool'}) { - $tooltypes{'crs'} = 1; - } - } - } - return %tooltypes; -} - sub wishlist_window { return(<<'ENDWISHLIST'); @@ -9758,21 +8791,21 @@ ENDADHOC } sub modal_adhoc_inner { - my ($funcname,$width,$height,$content,$possmathjax)=@_; + my ($funcname,$width,$height,$content)=@_; my $innerwidth=$width-20; $content=&js_ready( - &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}). + &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() ); - return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax); + return &modal_adhoc_script($funcname,$width,$height,$content); } sub modal_adhoc_window { - my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_; - return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax). + my ($funcname,$width,$height,$content,$linktext)=@_; + return &modal_adhoc_inner($funcname,$width,$height,$content). "".$linktext.""; } @@ -9838,9 +8871,8 @@ sub end_togglebox { } sub LCprogressbar_script { - my ($id,$number_to_do)=@_; - if ($number_to_do) { - return(< // ENDPROGRESS - } else { - return(< -// - -ENDPROGRESS - } } sub LCprogressbarUpdate_script { return(< .ui-progressbar { position:relative; } -.progress-label {position: absolute; width: 100%; text-align: center; top: 1px; font-weight: bold; text-shadow: 1px 1px 0 #fff;margin: 0; line-height: 200%; } .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; } @@ -9901,54 +8911,37 @@ my $LCidcnt; my $LCcurrentid; sub LCprogressbar { - my ($r,$number_to_do,$preamble)=@_; + my ($r)=(@_); $LClastpercent=0; $LCidcnt++; $LCcurrentid=$$.'_'.$LCidcnt; - my ($starting,$content); - if ($number_to_do) { - $starting=&mt('Starting'); - $content=(< $starting ENDPROGBAR - } else { - $starting=&mt('Loading...'); - $LClastpercent='false'; - $content=(< -
$starting
- -ENDPROGBAR - } - &r_print($r,$content.&LCprogressbar_script($LCcurrentid,$number_to_do)); + &r_print($r,$content.&LCprogressbar_script($LCcurrentid)); } sub LCprogressbarUpdate { - my ($r,$val,$text,$number_to_do)=@_; - if ($number_to_do) { - unless ($val) { - if ($LClastpercent) { - $val=$LClastpercent; - } else { - $val=0; - } - } - if ($val<0) { $val=0; } - if ($val>100) { $val=0; } - $LClastpercent=$val; - unless ($text) { $text=$val.'%'; } - } else { - $val = 'false'; + my ($r,$val,$text)=@_; + unless ($val) { + if ($LClastpercent) { + $val=$LClastpercent; + } else { + $val=0; + } } + if ($val<0) { $val=0; } + if ($val>100) { $val=0; } + $LClastpercent=$val; + unless ($text) { $text=$val.'%'; } $text=&js_ready($text); &r_print($r,< // ENDUPDATE @@ -10133,21 +9126,14 @@ function expand_div(caller) { sub simple_error_page { my ($r,$title,$msg,$args) = @_; - my %displayargs; if (ref($args) eq 'HASH') { if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); } - if ($args->{'only_body'}) { - $displayargs{'only_body'} = 1; - } - if ($args->{'no_nav_bar'}) { - $displayargs{'no_nav_bar'} = 1; - } } else { $msg = &mt($msg); } my $page = - &Apache::loncommon::start_page($title,'',\%displayargs). + &Apache::loncommon::start_page($title). '

'.$msg.'

'. &Apache::loncommon::end_page(); if (ref($r)) { @@ -10453,7 +9439,7 @@ sub get_sections { } } - if ($check_students) { + if ($check_students) { my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum); my $sec_index = &Apache::loncoursedata::CL_SECTION(); my $status_index = &Apache::loncoursedata::CL_STATUS(); @@ -10775,8 +9761,8 @@ Incoming parameters: 2. user's domain 3. quota name - portfolio, author, or course (if no quota name provided, defaults to portfolio). -4. crstype - official, unofficial, textbook or community, if quota name is - course +4. crstype - official, unofficial, textbook, placement or community, + if quota name is course Returns: 1. Disk quota (in MB) assigned to student. @@ -10849,8 +9835,9 @@ sub get_user_quota { 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') || ($crstype eq 'textbook')) { + if (($crstype eq 'official') || ($crstype eq 'unofficial') || + ($crstype eq 'community') || ($crstype eq 'textbook') || + ($crstype eq 'placement')) { $defquota = $domdefs{$crstype.'quota'}; } if ($defquota eq '') { @@ -10998,7 +9985,7 @@ Inputs: 7 4. filename of file for which action is being requested 5. filesize (kB) of file 6. action being taken: copy or upload. -7. quotatype (in course context -- official, unofficial, community or textbook). +7. quotatype (in course context -- official, unofficial, textbook, placement or community). Returns: 1 scalar: HTML to display containing warning if quota would be exceeded, otherwise return null. @@ -11034,6 +10021,8 @@ sub excess_filesize_warning { ############################################### + + sub get_secgrprole_info { my ($cdom,$cnum,$needroles,$type) = @_; my %sections_count = &get_sections($cdom,$cnum); @@ -11347,7 +10336,7 @@ sub user_rule_check { if (ref($usershash) eq 'HASH') { if (keys(%{$usershash}) > 1) { my (%by_username,%by_id,%userdoms); - my $checkid; + my $checkid; if (ref($checks) eq 'HASH') { if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) { $checkid = 1; @@ -11358,7 +10347,7 @@ sub user_rule_check { if ($checkid) { if (ref($usershash->{$user}) eq 'HASH') { if ($usershash->{$user}->{'id'} ne '') { - $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname; + $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname; $userdoms{$udom} = 1; if (ref($inst_results) eq 'HASH') { $inst_results->{$uname.':'.$udom} = {}; @@ -11428,7 +10417,7 @@ sub user_rule_check { if (ref($usershash->{$user}) eq 'HASH') { if (ref($checks) eq 'HASH') { if (defined($checks->{'username'})) { - ($inst_response{$user},%{$inst_results->{$user}}) = + ($inst_response{$user},%{$inst_results->{$user}}) = &Apache::lonnet::get_instuser($udom,$uname); } elsif (defined($checks->{'id'})) { if ($usershash->{$user}->{'id'} ne '') { @@ -11451,7 +10440,7 @@ sub user_rule_check { if (ref($domconfig{'usercreation'}) eq 'HASH') { foreach my $item ('username','id') { if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') { - $$curr_rules{$udom}{$item} = + $$curr_rules{$udom}{$item} = $domconfig{'usercreation'}{$item.'_rule'}; } } @@ -11474,7 +10463,7 @@ sub user_rule_check { $id = $inst_results->{$user}->{'id'}; } } - if ($id eq '') { + if ($id eq '') { if (ref($usershash->{$user})) { $id = $usershash->{$user}->{'id'}; } @@ -11635,15 +10624,11 @@ sub sorted_inst_types { } sub get_institutional_codes { - my ($cdom,$crs,$settings,$allcourses,$LC_code) = @_; + my ($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'}); @@ -11654,8 +10639,8 @@ sub get_institutional_codes { } if (@currxlists > 0) { - foreach my $xl (@currxlists) { - if ($xl =~ /^([^:]+):(\w*)$/) { + foreach (@currxlists) { + if (m/^([^:]+):(\w*)$/) { unless (grep/^$1$/,@{$allcourses}) { push(@{$allcourses},$1); $$LC_code{$1} = $2; @@ -11663,28 +10648,15 @@ sub get_institutional_codes { } } } - + if (@currsections > 0) { - foreach my $sec (@currsections) { - if ($sec =~ m/^(\w+):(\w*)$/ ) { - my $instsec = $1; + foreach (@currsections) { + if (m/^(\w+):(\w*)$/) { + my $sec = $coursecode.$1; my $lc_sec = $2; - 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}) { + unless (grep/^$sec$/,@{$allcourses}) { push(@{$allcourses},$sec); - $$LC_code{$sec} = $unclutteredlcsec{$crskey}[$i]; + $$LC_code{$sec} = $lc_sec; } } } @@ -11793,7 +10765,7 @@ future_reservable - ref to hash of stude Keys in inner hash are: (a) symb: either blank or symb to which slot use is restricted. - (b) startreserve: start date of reservation period. + (b) startreserve: start date of reservation period. (c) uniqueperiod: start,end dates when slot is to be uniquely selected. @@ -11803,13 +10775,35 @@ future_reservable - ref to hash of stude sub get_future_slots { my ($cnum,$cdom,$now,$symb) = @_; + my $map; + if ($symb) { + ($map) = &Apache::lonnet::decode_symb($symb); + } my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future); my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom); foreach my $slot (keys(%slots)) { next unless($slots{$slot}->{'type'} eq 'schedulable_student'); if ($symb) { - next if (($slots{$slot}->{'symb'} ne '') && - ($slots{$slot}->{'symb'} ne $symb)); + if ($slots{$slot}->{'symb'} ne '') { + my $canuse; + my %oksymbs; + my @slotsymbs = split(/\s*,\s*/,$slots{$slot}->{'symb'}); + map { $oksymbs{$_} = 1; } @slotsymbs; + if ($oksymbs{$symb}) { + $canuse = 1; + } else { + foreach my $item (@slotsymbs) { + if ($item =~ /\.(page|sequence)$/) { + (undef,undef,my $sloturl) = &Apache::lonnet::decode_symb($item); + if (($map ne '') && ($map eq $sloturl)) { + $canuse = 1; + last; + } + } + } + } + next unless ($canuse); + } } if (($slots{$slot}->{'starttime'} > $now) && ($slots{$slot}->{'endtime'} > $now)) { @@ -11862,7 +10856,7 @@ sub get_future_slots { $reservable_now{$slot} = { symb => $symb, endreserve => $lastres, - uniqueperiod => $uniqueperiod, + uniqueperiod => $uniqueperiod, }; } elsif (($startreserve > $now) && (!$endreserve || $endreserve > $startreserve)) { @@ -12027,7 +11021,23 @@ sub get_env_multiple { return(@values); } +# Looks at given dependencies, and returns something depending on the context. +# For coursedocs paste, returns (undef, $counter, $numpathchg, \%existing). +# For syllabus rewrites, returns (undef, $counter, $numpathchg, \%existing, \%mapping). +# For all other contexts, returns ($output, $counter, $numpathchg). +# $output: string with the HTML output. Can contain missing dependencies with an upload form, existing dependencies, and dependencies no longer in use. +# $counter: integer with the number of existing dependencies when no HTML output is returned, and the number of missing dependencies when an HTML output is returned. +# $numpathchg: integer with the number of cleaned up dependency paths. +# \%existing: hash reference clean path -> 1 only for existing dependencies. +# \%mapping: hash reference clean path -> original path for all dependencies. +# @param {string} actionurl - The path to the handler, indicative of the context. +# @param {string} state - Can contain HTML with hidden inputs that will be added to the output form. +# @param {hash reference} allfiles - List of file info from lonnet::extract_embedded_items +# @param {hash reference} codebase - undef, not modified by lonnet::extract_embedded_items ? +# @param {hash reference} args - More parameters ! Possible keys: error_on_invalid_names (boolean), ignore_remote_references (boolean), current_path (string), docs_url (string), docs_title (string), context (string) +# @return {Array} - array depending on the context (not a reference) sub ask_for_embedded_content { + # NOTE: documentation was added afterwards, it could be wrong my ($actionurl,$state,$allfiles,$codebase,$args)=@_; my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges, %currsubfile,%unused,$rem); @@ -12043,6 +11053,9 @@ sub ask_for_embedded_content { my $heading = &mt('Upload embedded files'); my $buttontext = &mt('Upload'); + # fills these variables based on the context: + # $navmap, $cdom, $cnum, $udom, $uname, $url, $toplevel, $getpropath, + # $path, $fileloc, $title, $rem, $filename if ($env{'request.course.id'}) { if ($actionurl eq '/adm/dependencies') { $navmap = Apache::lonnavmaps::navmap->new(); @@ -12050,7 +11063,7 @@ sub ask_for_embedded_content { $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; } - if (($actionurl eq '/adm/portfolio') || + if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { my $current_path='/'; if ($env{'form.currentpath'}) { @@ -12082,18 +11095,18 @@ sub ask_for_embedded_content { $toplevel = $url; if ($args->{'context'} eq 'paste') { ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/}); - ($path) = + ($path) = ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/}); $fileloc = &Apache::lonnet::filelocation('',$toplevel); $fileloc =~ s{^/}{}; } } - } elsif ($actionurl eq '/adm/dependencies') { + } elsif ($actionurl eq '/adm/dependencies') { if ($env{'request.course.id'} ne '') { if (ref($args) eq 'HASH') { $url = $args->{'docs_url'}; $title = $args->{'docs_title'}; - $toplevel = $url; + $toplevel = $url; unless ($toplevel =~ m{^/}) { $toplevel = "/$url"; } @@ -12127,6 +11140,16 @@ sub ask_for_embedded_content { $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/'; $fileloc =~ s{^/}{}; } + + # parses the dependency paths to get some info + # fills $newfiles, $mapping, $subdependencies, $dependencies + # $newfiles: hash URL -> 1 for new files or external URLs + # (will be completed later) + # $mapping: + # for external URLs: external URL -> external URL + # for relative paths: clean path -> original path + # $subdependencies: hash clean path -> clean file name -> 1 for relative paths in subdirectories + # $dependencies: hash clean or not file name -> 1 for relative paths not in subdirectories foreach my $file (keys(%{$allfiles})) { my $embed_file; if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) { @@ -12169,11 +11192,24 @@ sub ask_for_embedded_content { } } } + + # looks for all existing files in dependency subdirectories (from $subdependencies filled above) + # and lists + # fills $currsubfile, $pathchanges, $existing, $numexisting, $newfiles, $unused + # $currsubfile: hash clean path -> file name -> 1 for all existing files in the path + # $pathchanges: hash clean path -> 1 if the file in subdirectory exists and + # the path had to be cleaned up + # $existing: hash clean path -> 1 if the file exists + # $numexisting: number of keys in $existing + # $newfiles: updated with clean path -> 1 for files in subdirectories that do not exist + # $unused: only for /adm/dependencies, hash clean path -> 1 for existing files in + # dependency subdirectories that are + # not listed as dependencies, with some exceptions using $rem 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') { @@ -12244,6 +11280,9 @@ sub ask_for_embedded_content { } } } + + # fills $currfile, hash file name -> 1 or [$size,$mtime] + # for files in $url or $fileloc (target directory) in some contexts my %currfile; if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { @@ -12282,6 +11321,8 @@ sub ask_for_embedded_content { } } } + # updates $pathchanges, $existing, $numexisting, $newfiles and $unused for files that + # are not in subdirectories, using $currfile foreach my $file (keys(%dependencies)) { if (exists($currfile{$file})) { unless ($mapping{$file} eq $file) { @@ -12310,17 +11351,25 @@ sub ask_for_embedded_content { $unused{$file} = 1; } } + + # returns some results for coursedocs paste and syllabus rewrites ($output is undef) if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') && ($args->{'context'} eq 'paste')) { $counter = scalar(keys(%existing)); $numpathchg = scalar(keys(%pathchanges)); return ($output,$counter,$numpathchg,\%existing); - } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") && + } 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); } + + # returns HTML otherwise, with dependency results and to ask for more uploads + + # $upload_output: missing dependencies (with upload form) + # $modify_output: uploaded dependencies (in use) + # $delete_output: files no longer in use (unused files are not listed for londocs, bug?) foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) { if ($actionurl eq '/adm/dependencies') { next if ($embed_file =~ m{^\w+://}); @@ -12544,7 +11593,7 @@ sub ask_for_embedded_content { 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 +to a course or portfolio, where "Upload embedded images/multimedia files if HTML file" checkbox was checked. @@ -12563,7 +11612,7 @@ sub clean_path { @contents = ($embed_file); } my $lastidx = scalar(@contents)-1; - for (my $i=0; $i<=$lastidx; $i++) { + for (my $i=0; $i<=$lastidx; $i++) { $contents[$i]=~s{\\}{/}g; $contents[$i]=~s/\s+/\_/g; $contents[$i]=~s{[^/\w\.\-]}{}g; @@ -12902,7 +11951,7 @@ sub modify_html_refs { } my (%allfiles,%codebase,$output,$content); my @changes = &get_env_multiple('form.namechange'); - unless ((@changes > 0) || ($context eq 'syllabus')) { + unless ((@changes > 0) || ($context eq 'syllabus')) { if (wantarray) { return ('',0,0); } else { @@ -12934,7 +11983,7 @@ sub modify_html_refs { return; } } - if (open(my $fh,'<',$container)) { + if (open(my $fh,"<$container")) { $content = join('', <$fh>); close($fh); } else { @@ -12999,7 +12048,7 @@ sub modify_html_refs { } } } else { - if (open(my $fh,'>',$container)) { + if (open(my $fh,">$container")) { print $fh $content; close($fh); $output = '

'.&mt('Updated [quant,_1,reference] in [_2].', @@ -13037,7 +12086,7 @@ sub modify_html_refs { } } if ($rewrites) { - my $saveresult; + my $saveresult; my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult); if ($url eq $container) { my ($fname) = ($container =~ m{/([^/]+)$}); @@ -13516,18 +12565,6 @@ sub decompress_uploaded_file { sub process_decompression { my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_; - unless (($dir_root eq '/userfiles') && ($destination =~ m{^(docs|supplemental)/(default|\d+)/\d+$})) { - return '

'.&mt('Not extracted.').'
'. - &mt('Unexpected file path.').'

'."\n"; - } - unless (($docudom =~ /^$match_domain$/) && ($docuname =~ /^$match_courseid$/)) { - return '

'.&mt('Not extracted.').'
'. - &mt('Unexpected course context.').'

'."\n"; - } - unless ($file eq &Apache::lonnet::clean_filename($file)) { - return '

'.&mt('Not extracted.').'
'. - &mt('Filename contained unexpected characters.').'

'."\n"; - } my ($dir,$error,$warning,$output); if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i) { $error = &mt('Filename not a supported archive file type.'). @@ -13562,44 +12599,30 @@ sub process_decompression { } } my $numskip = scalar(@to_skip); - my $numoverwrite = scalar(@to_overwrite); - if (($numskip) && (!$numoverwrite)) { + if (($numskip > 0) && + ($numskip == $env{'form.archive_itemcount'})) { $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.'); } elsif ($dir eq '') { $error = &mt('Directory containing archive file unavailable.'); } elsif (!$error) { my ($decompressed,$display); - if (($numskip) || ($numoverwrite)) { + if ($numskip > 0) { my $tempdir = time.'_'.$$.int(rand(10000)); mkdir("$dir/$tempdir",0755); - if (&File::Copy::move("$dir/$file","$dir/$tempdir/$file")) { - ($decompressed,$display) = - &decompress_uploaded_file($file,"$dir/$tempdir"); - foreach my $item (@to_skip) { - if (($item ne '') && ($item !~ /\.\./)) { - if (-f "$dir/$tempdir/$item") { - unlink("$dir/$tempdir/$item"); - } elsif (-d "$dir/$tempdir/$item") { - &File::Path::remove_tree("$dir/$tempdir/$item",{ safe => 1 }); - } - } - } - foreach my $item (@to_overwrite) { - if ((-e "$dir/$tempdir/$item") && (-e "$dir/$item")) { - if (($item ne '') && ($item !~ /\.\./)) { - if (-f "$dir/$item") { - unlink("$dir/$item"); - } elsif (-d "$dir/$item") { - &File::Path::remove_tree("$dir/$item",{ safe => 1 }); - } - &File::Copy::move("$dir/$tempdir/$item","$dir/$item"); - } + system("mv $dir/$file $dir/$tempdir/$file"); + ($decompressed,$display) = + &decompress_uploaded_file($file,"$dir/$tempdir"); + foreach my $item (@to_skip) { + if (($item ne '') && ($item !~ /\.\./)) { + if (-f "$dir/$tempdir/$item") { + unlink("$dir/$tempdir/$item"); + } elsif (-d "$dir/$tempdir/$item") { + system("rm -rf $dir/$tempdir/$item"); } } - if (&File::Copy::move("$dir/$tempdir/$file","$dir/$file")) { - &File::Path::remove_tree("$dir/$tempdir",{ safe => 1 }); - } } + system("mv $dir/$tempdir/* $dir"); + rmdir("$dir/$tempdir"); } else { ($decompressed,$display) = &decompress_uploaded_file($file,$dir); @@ -13617,7 +12640,8 @@ sub process_decompression { if (ref($newdirlistref) eq 'ARRAY') { foreach my $dir_line (@{$newdirlistref}) { my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5); - unless (($item =~ /^\.+$/) || ($item eq $file)) { + unless (($item =~ /^\.+$/) || ($item eq $file) || + ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) { push(@newitems,$item); if ($dirptr&$testdir) { $is_dir{$item} = 1; @@ -13672,7 +12696,7 @@ sub process_decompression { $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'}; $displayed{'folder'} = $i; } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) || - (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) { + (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) { $env{'form.archive_'.$i} = 'display'; $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'}; $displayed{'web'} = $i; @@ -14102,7 +13126,7 @@ END sub process_extracted_files { my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_; my $numitems = $env{'form.archive_count'}; - return if ((!$numitems) || ($numitems =~ /\D/)); + return unless ($numitems); my @ids=&Apache::lonnet::current_machine_ids(); my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir, %folders,%containers,%mapinner,%prompttofetch); @@ -14115,7 +13139,7 @@ sub process_extracted_files { } else { $prefix = $Apache::lonnet::perlvar{'lonDocRoot'}; $pathtocheck = "$dir_root/$docudom/$docuname/$destination"; - $dir = "$dir_root/$docudom/$docuname"; + $dir = "$dir_root/$docudom/$docuname"; } my $currdir = "$dir_root/$destination"; (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/}); @@ -14124,7 +13148,7 @@ sub process_extracted_files { $folders{'0'} = $items[-2]; if ($env{'form.folderpath'} =~ /\:1$/) { $containers{'0'}='page'; - } else { + } else { $containers{'0'}='sequence'; } } @@ -14204,9 +13228,7 @@ sub process_extracted_files { '.'.$containers{$outer},1,1); $newseqid{$i} = $newidx; unless ($errtext) { - $result .= '
  • '.&mt('Folder: [_1] added to course', - &HTML::Entities::encode($docstitle,'<>&"')).. - '
  • '."\n"; + $result .= '
  • '.&mt('Folder: [_1] added to course',$docstitle).'
  • '."\n"; } } } else { @@ -14215,49 +13237,38 @@ sub process_extracted_files { my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'. $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'. $title; - if (($outer !~ /\D/) && - (($mapinner{$outer} eq 'default') || ($mapinner{$outer} !~ /\D/)) && - ($newidx !~ /\D/)) { - if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") { - mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755); - } - if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") { - mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx"); - } - if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") { - if (rename("$prefix$path","$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title")) { - $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx"; - unless ($ishome) { - my $fetch = "$newdest{$i}/$title"; - $fetch =~ s/^\Q$prefix$dir\E//; - $prompttofetch{$fetch} = 1; - } - } + if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") { + mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755); + } + if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") { + mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx"); + } + if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") { + system("mv $prefix$path $prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title"); + $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx"; + unless ($ishome) { + my $fetch = "$newdest{$i}/$title"; + $fetch =~ s/^\Q$prefix$dir\E//; + $prompttofetch{$fetch} = 1; } - $LONCAPA::map::resources[$newidx]= - $docstitle.':'.$url.':false:normal:res'; - push(@LONCAPA::map::order, $newidx); - my ($outtext,$errtext)= - &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'. - $docuname.'/'.$folders{$outer}. - '.'.$containers{$outer},1,1); - unless ($errtext) { - if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") { - $result .= '
  • '.&mt('File: [_1] added to course', - &HTML::Entities::encode($docstitle,'<>&"')). - '
  • '."\n"; - } + } + $LONCAPA::map::resources[$newidx]= + $docstitle.':'.$url.':false:normal:res'; + push(@LONCAPA::map::order, $newidx); + my ($outtext,$errtext)= + &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'. + $docuname.'/'.$folders{$outer}. + '.'.$containers{$outer},1,1); + unless ($errtext) { + if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") { + $result .= '
  • '.&mt('File: [_1] added to course',$docstitle).'
  • '."\n"; } - } else { - $warning .= &mt('Item extracted from archive: [_1] has unexpected path.', - &HTML::Entities::encode($path,'<>&"')).'
    '; } } } } } else { - $warning .= &mt('Item extracted from archive: [_1] has unexpected path.', - &HTML::Entities::encode($path,'<>&"')).'
    '; + $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'
    '; } } for (my $i=1; $i<=$numitems; $i++) { @@ -14279,7 +13290,7 @@ sub process_extracted_files { } if ($itemidx eq '') { $itemidx = 0; - } + } if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) { if ($mapinner{$referrer{$i}}) { $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}"; @@ -14318,9 +13329,7 @@ sub process_extracted_files { } if ($fullpath ne '') { if (-e "$prefix$path") { - unless (rename("$prefix$path","$fullpath/$title")) { - $warning .= &mt('Failed to rename dependency').'
    '; - } + system("mv $prefix$path $fullpath/$title"); } if (-e "$fullpath/$title") { my $showpath; @@ -14328,27 +13337,22 @@ sub process_extracted_files { $showpath = "$relpath/$title"; } else { $showpath = "/$title"; - } - $result .= '
  • '.&mt('[_1] included as a dependency', - &HTML::Entities::encode($showpath,'<>&"')). - '
  • '."\n"; - unless ($ishome) { - my $fetch = "$fullpath/$title"; - $fetch =~ s/^\Q$prefix$dir\E//; - $prompttofetch{$fetch} = 1; - } + } + $result .= '
  • '.&mt('[_1] included as a dependency',$showpath).'
  • '."\n"; + } + unless ($ishome) { + my $fetch = "$fullpath/$title"; + $fetch =~ s/^\Q$prefix$dir\E//; + $prompttofetch{$fetch} = 1; } } } } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') { $warning .= &mt('[_1] is a dependency of [_2], which was discarded.', - &HTML::Entities::encode($path,'<>&"'), - &HTML::Entities::encode($env{'form.archive_content_'.$referrer{$i}},'<>&"')). - '
    '; + $path,$env{'form.archive_content_'.$referrer{$i}}).'
    '; } } else { - $warning .= &mt('Item extracted from archive: [_1] has unexpected path.', - &HTML::Entities::encode($path)).'
    '; + $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'
    '; } } if (keys(%todelete)) { @@ -14622,15 +13626,12 @@ sub upfile_store { $env{'form.upfile'}=~s/\n+/\n/gs; $env{'form.upfile'}=~s/\n+$//gs; - my $datatoken = &valid_datatoken($env{'user.name'}.'_'.$env{'user.domain'}. - '_enroll_'.$env{'request.course.id'}.'_'. - time.'_'.$$); - return if ($datatoken eq ''); - + my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}. + '_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$; { my $datafile = $r->dir_config('lonDaemons'). '/tmp/'.$datatoken.'.tmp'; - if ( open(my $fh,'>',$datafile) ) { + if ( open(my $fh,">$datafile") ) { print $fh $env{'form.upfile'}; close($fh); } @@ -14640,22 +13641,21 @@ sub upfile_store { =pod -=item * &load_tmp_file($r,$datatoken) +=item * &load_tmp_file($r) Load uploaded file from tmp, $r should be the HTTP Request object, -$datatoken is the name to assign to the temporary file. +needs $env{'form.datatoken'}, sets $env{'form.upfile'} to the contents of the file =cut sub load_tmp_file { - my ($r,$datatoken) = @_; - return if ($datatoken eq ''); + my $r=shift; my @studentdata=(); { my $studentfile = $r->dir_config('lonDaemons'). - '/tmp/'.$datatoken.'.tmp'; - if ( open(my $fh,'<',$studentfile) ) { + '/tmp/'.$env{'form.datatoken'}.'.tmp'; + if ( open(my $fh,"<$studentfile") ) { @studentdata=<$fh>; close($fh); } @@ -14663,14 +13663,6 @@ sub load_tmp_file { $env{'form.upfile'}=join('',@studentdata); } -sub valid_datatoken { - my ($datatoken) = @_; - if ($datatoken =~ /^$match_username\_$match_domain\_enroll_(|$match_domain\_$match_courseid)\_\d+_\d+$/) { - return $datatoken; - } - return; -} - =pod =item * &upfile_record_sep() @@ -15550,20 +14542,14 @@ generated by lonerrorhandler.pm, CHECKRP lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively. Inputs: -defmail (scalar - email address of default recipient), +defmail (scalar - email address of default recipient), 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 - -$requname username of requester (if mailing type is helpdeskmail) - -$requdom domain of requester (if mailing type is helpdeskmail) - -$reqemail e-mail address of requester (if mailing type is helpdeskmail) +origmail (scalar - email address of recipient from loncapa.conf, +i.e., predates configuration by DC via domainprefs.pm Returns: comma separated list of addresses to which to send e-mail. @@ -15574,7 +14560,7 @@ Returns: comma separated list of address ############################################################ ############################################################ sub build_recipient_list { - my ($defmail,$mailing,$defdom,$origmail,$requname,$requdom,$reqemail) = @_; + my ($defmail,$mailing,$defdom,$origmail) = @_; my @recipients; my ($otheremails,$lastresort,$allbcc,$addtext); my %domconfig = @@ -15615,98 +14601,11 @@ sub build_recipient_list { } elsif ($origmail ne '') { $lastresort = $origmail; } - if ($mailing eq 'helpdeskmail') { - if ((ref($domconfig{'contacts'}{'overrides'}) eq 'HASH') && - (keys(%{$domconfig{'contacts'}{'overrides'}}))) { - my ($inststatus,$inststatus_checked); - if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '') && - ($env{'user.domain'} ne 'public')) { - $inststatus_checked = 1; - $inststatus = $env{'environment.inststatus'}; - } - unless ($inststatus_checked) { - if (($requname ne '') && ($requdom ne '')) { - if (($requname =~ /^$match_username$/) && - ($requdom =~ /^$match_domain$/) && - (&Apache::lonnet::domain($requdom))) { - my $requhome = &Apache::lonnet::homeserver($requname, - $requdom); - unless ($requhome eq 'no_host') { - my %userenv = &Apache::lonnet::userenvironment($requdom,$requname,'inststatus'); - $inststatus = $userenv{'inststatus'}; - $inststatus_checked = 1; - } - } - } - } - unless ($inststatus_checked) { - if ($reqemail =~ /^[^\@]+\@[^\@]+$/) { - my %srch = (srchby => 'email', - srchdomain => $defdom, - srchterm => $reqemail, - srchtype => 'exact'); - my %srch_results = &Apache::lonnet::usersearch(\%srch); - foreach my $uname (keys(%srch_results)) { - if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') { - $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}}); - $inststatus_checked = 1; - last; - } - } - unless ($inststatus_checked) { - my ($dirsrchres,%srch_results) = &Apache::lonnet::inst_directory_query(\%srch); - if ($dirsrchres eq 'ok') { - foreach my $uname (keys(%srch_results)) { - if (ref($srch_results{$uname}{'inststatus'}) eq 'ARRAY') { - $inststatus = join(',',@{$srch_results{$uname}{'inststatus'}}); - $inststatus_checked = 1; - last; - } - } - } - } - } - } - if ($inststatus ne '') { - foreach my $status (split(/\:/,$inststatus)) { - if (ref($domconfig{'contacts'}{'overrides'}{$status}) eq 'HASH') { - my @contacts = ('adminemail','supportemail'); - foreach my $item (@contacts) { - if ($domconfig{'contacts'}{'overrides'}{$status}{$item}) { - my $addr = $domconfig{'contacts'}{'overrides'}{$status}; - if (!grep(/^\Q$addr\E$/,@recipients)) { - push(@recipients,$addr); - } - } - } - $otheremails = $domconfig{'contacts'}{'overrides'}{$status}{'others'}; - if ($domconfig{'contacts'}{'overrides'}{$status}{'bcc'}) { - my @bccs = split(/,/,$domconfig{'contacts'}{'overrides'}{$status}{'bcc'}); - my @ok_bccs; - foreach my $bcc (@bccs) { - $bcc =~ s/^\s+//g; - $bcc =~ s/\s+$//g; - if ($bcc =~ m/^[^\@]+\@[^\@]+$/) { - if (!(grep(/^\Q$bcc\E$/,@ok_bccs))) { - push(@ok_bccs,$bcc); - } - } - } - if (@ok_bccs > 0) { - $allbcc = join(', ',@ok_bccs); - } - } - $addtext = $domconfig{'contacts'}{'overrides'}{$status}{'include'}; - last; - } - } - } - } - } } elsif ($origmail ne '') { $lastresort = $origmail; } - if (($mailing eq 'helpdeskmail') && ($lastresort ne '')) { + + if (($mailing eq 'helpdesk') && ($lastresort ne '')) { unless (grep(/^\Q$defdom\E$/,&Apache::lonnet::current_machine_domains())) { my $lonhost = $Apache::lonnet::perlvar{'lonHostID'}; my $machinedom = $Apache::lonnet::perlvar{'lonDefDomain'}; @@ -15786,7 +14685,7 @@ sub build_recipient_list { } } } - if ($mailing eq 'helpdeskmail') { + if ($mailing eq 'helpdesk') { if ((!@recipients) && ($lastresort ne '')) { push(@recipients,$lastresort); } @@ -15808,6 +14707,87 @@ sub build_recipient_list { =pod +=over 4 + +=item * &mime_email() + +Sends an email with a possible attachment + +Inputs: + +=over 4 + +from - Sender's email address + +to - Email address of recipient + +subject - Subject of email + +body - Body of email + +cc_string - Carbon copy email address + +bcc - Blind carbon copy email address + +type - File type of attachment + +attachment_path - Path of file to be attached + +file_name - Name of file to be attached + +attachment_text - The body of an attachment of type "TEXT" + +=back + +=back + +=cut + +############################################################ +############################################################ + +sub mime_email { + my ($from, $to, $subject, $body, $cc_string, $bcc, $attachment_path, + $file_name, $attachment_text) = @_; + my $msg = MIME::Lite->new( + From => $from, + To => $to, + Subject => $subject, + Type =>'TEXT', + Data => $body, + ); + if ($cc_string ne '') { + $msg->add("Cc" => $cc_string); + } + if ($bcc ne '') { + $msg->add("Bcc" => $bcc); + } + $msg->attr("content-type" => "text/plain"); + $msg->attr("content-type.charset" => "UTF-8"); + # Attach file if given + if ($attachment_path) { + unless ($file_name) { + if ($attachment_path =~ m-/([^/]+)$-) { $file_name = $1; } + } + my ($type, $encoding) = MIME::Types::by_suffix($attachment_path); + $msg->attach(Type => $type, + Path => $attachment_path, + Filename => $file_name + ); + # Otherwise attach text if given + } elsif ($attachment_text) { + $msg->attach(Type => 'TEXT', + Data => $attachment_text); + } + # Send it + $msg->send('sendmail'); +} + +############################################################ +############################################################ + +=pod + =head1 Course Catalog Routines =over 4 @@ -15891,8 +14871,6 @@ jsarray (reference to array of categorie subcats (reference to hash of arrays containing all subcategories within each category, -recursive) -maxd (reference to hash used to hold max depth for all top-level categories). - Returns: nothing Side effects: populates trails and allitems hash references. @@ -15900,7 +14878,7 @@ Side effects: populates trails and allit =cut sub extract_categories { - my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats,$maxd) = @_; + my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_; if (ref($categories) eq 'HASH') { &gather_categories($categories,$cats,$idx,$jsarray); if (ref($cats->[0]) eq 'ARRAY') { @@ -15912,6 +14890,8 @@ sub extract_categories { $trailstr = &mt('Official courses (with institutional codes)'); } elsif ($name eq 'communities') { $trailstr = &mt('Communities'); + } elsif ($name eq 'placement') { + $trailstr = &mt('Placement Tests'); } else { $trailstr = $name; } @@ -15926,15 +14906,12 @@ sub extract_categories { if (ref($subcats) eq 'HASH') { push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1'); } - &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats,$maxd); + &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats); } } else { if (ref($subcats) eq 'HASH') { $subcats->{$item} = []; } - if (ref($maxd) eq 'HASH') { - $maxd->{$name} = 1; - } } } } @@ -15972,13 +14949,13 @@ Side effects: populates trails and allit =cut sub recurse_categories { - my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats,$maxd) = @_; + my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_; my $shallower = $depth - 1; if (ref($cats->[$depth]{$category}) eq 'ARRAY') { for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) { my $name = $cats->[$depth]{$category}[$k]; my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower; - my $trailstr = join(' » ',(@{$parents},$category)); + my $trailstr = join(' -> ',(@{$parents},$category)); if ($allitems->{$item} eq '') { push(@{$trails},$trailstr); $allitems->{$item} = scalar(@{$trails})-1; @@ -15999,21 +14976,16 @@ sub recurse_categories { } } &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents, - $subcats,$maxd); + $subcats); pop(@{$parents}); } } else { 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; } - if (ref($maxd) eq 'HASH') { - if ($depth > $maxd->{$parents->[0]}) { - $maxd->{$parents->[0]} = $depth; - } - } } return; } @@ -16045,8 +15017,8 @@ sub assign_categories_table { my ($cathash,$currcat,$type,$disabled) = @_; my $output; if (ref($cathash) eq 'HASH') { - my (@cats,@trails,%allitems,%idx,@jsarray,%maxd,@path,$maxdepth); - &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray,\%maxd); + my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth); + &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray); $maxdepth = scalar(@cats); if (@cats > 0) { my $itemcount = 0; @@ -16061,8 +15033,10 @@ sub assign_categories_table { next if ($parent eq 'instcode'); if ($type eq 'Community') { next unless ($parent eq 'communities'); + } elsif ($type eq 'Placement') { + next unless ($parent eq 'placement'); } else { - next if ($parent eq 'communities'); + next if (($parent eq 'communities') || ($parent eq 'placement')); } my $css_class = $itemcount%2?' class="LC_odd_row"':''; my $item = &escape($parent).'::0'; @@ -16075,6 +15049,8 @@ sub assign_categories_table { my $parent_title = $parent; if ($parent eq 'communities') { $parent_title = &mt('Communities'); + } elsif ($parent eq 'placement') { + $parent_title = &mt('Placement Tests'); } $table .= ''. '{'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') { @@ -16377,38 +15352,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'}) && @@ -16425,7 +15378,7 @@ sub check_clone { if ($args->{'ccdomain'} eq $args->{'clonedomain'}) { $can_clone = 1; } - } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && + } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && ($args->{'clonedomain'} eq $args->{'course_domain'})) { if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'}, $clonehash{'internal.coursecode'},$args->{'crscode'})) { @@ -16444,7 +15397,7 @@ sub check_clone { $can_clone = 1; } unless ($can_clone) { - if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && + if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && ($args->{'clonedomain'} eq $args->{'course_domain'})) { my (%gotdomdefaults,%gotcodedefaults); foreach my $cloner (@cloners) { @@ -16483,12 +15436,12 @@ sub check_clone { if ($args->{'crstype'} eq 'Community') { $ccrole = 'co'; } - my %roleshash = - &Apache::lonnet::get_my_roles($args->{'ccuname'}, - $args->{'ccdomain'}, + my %roleshash = + &Apache::lonnet::get_my_roles($args->{'ccuname'}, + $args->{'ccdomain'}, 'userroles',['active'],[$ccrole], - [$args->{'clonedomain'}]); - if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) { + [$args->{'clonedomain'}]); + if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) { $can_clone = 1; } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'}, $args->{'ccuname'},$args->{'ccdomain'})) { @@ -16497,34 +15450,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"; @@ -16533,18 +15472,30 @@ 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); } } # # Open course # - my $crstype = lc($args->{'crstype'}); + my $showncrstype; + if ($args->{'crstype'} eq 'Placement') { + $showncrstype = 'placement test'; + } else { + $showncrstype = lc($args->{'crstype'}); + } my %cenv=(); $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'}, $args->{'cdescr'}, @@ -16555,20 +15506,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]',$showncrstype,$$courseid).$linefeed; if ($$courseid =~ /^error:/) { - return (0,$outcome,$clonemsgref); + return (0,$outcome); } # @@ -16577,37 +15523,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]',$showncrstype,$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 @@ -16632,7 +15564,8 @@ sub construct_course { 'plc.users.denied', 'hidefromcat', 'checkforpriv', - 'categories'], + 'categories', + 'internal.uniquecode'], $$crsudom,$$crsunum); if ($args->{'textbook'}) { $cenv{'internal.textbook'} = $args->{'textbook'}; @@ -16647,9 +15580,6 @@ sub construct_course { if ($args->{'crstype'}) { $cenv{'type'}=$args->{'crstype'}; } - if ($args->{'lti'}) { - $cenv{'internal.lti'}=$args->{'lti'}; - } if ($args->{'crsid'}) { $cenv{'courseid'}=$args->{'crsid'}; } @@ -16671,7 +15601,6 @@ sub construct_course { $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'}; } my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner. - my @oklcsecs = (); # Used to accumulate LON-CAPA sections for validated institutional sections. if ($args->{'crssections'}) { $cenv{'internal.sectionnums'} = ''; if ($args->{'crssections'} =~ m/,/) { @@ -16685,11 +15614,7 @@ sub construct_course { my $class = $args->{'crscode'}.$sec; my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'}); $cenv{'internal.sectionnums'} .= $item.','; - if ($addcheck eq 'ok') { - unless (grep(/^\Q$gp\E$/,@oklcsecs)) { - push(@oklcsecs,$gp); - } - } else { + unless ($addcheck eq 'ok') { push(@badclasses,$class); } } @@ -16717,11 +15642,7 @@ sub construct_course { my ($xl,$gp) = split/:/,$item; my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'}); $cenv{'internal.crosslistings'} .= $item.','; - if ($addcheck eq 'ok') { - unless (grep(/^\Q$gp\E$/,@oklcsecs)) { - push(@oklcsecs,$gp); - } - } else { + unless ($addcheck eq 'ok') { push(@badclasses,$xl); } } @@ -16779,41 +15700,11 @@ sub construct_course { $outcome .= $linefeed; } else { $outcome .= "

    \n"; - } + } } if ($args->{'no_end_date'}) { $args->{'endaccess'} = 0; } -# If an official course with institutional sections is created by cloning -# an existing course, section-specific hiding of course totals in student's -# view of grades as copied from cloned course, will be checked for valid -# sections. - if (($can_clone && $cloneid) && - ($cenv{'internal.coursecode'} ne '') && - ($cenv{'grading'} eq 'standard') && - ($cenv{'hidetotals'} ne '') && - ($cenv{'hidetotals'} ne 'all')) { - my @hidesecs; - my $deletehidetotals; - if (@oklcsecs) { - foreach my $sec (split(/,/,$cenv{'hidetotals'})) { - if (grep(/^\Q$sec$/,@oklcsecs)) { - push(@hidesecs,$sec); - } - } - if (@hidesecs) { - $cenv{'hidetotals'} = join(',',@hidesecs); - } else { - $deletehidetotals = 1; - } - } else { - $deletehidetotals = 1; - } - if ($deletehidetotals) { - delete($cenv{'hidetotals'}); - &Apache::lonnet::del('environment',['hidetotals'],$$crsudom,$$crsunum); - } - } $cenv{'internal.autostart'}=$args->{'enrollstart'}; $cenv{'internal.autoend'}=$args->{'enrollend'}; $cenv{'default_enrollment_start_date'}=$args->{'startaccess'}; @@ -16878,7 +15769,7 @@ sub construct_course { if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') { $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code; my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime'); - } + } if (ref($coderef)) { $$coderef = $code; } @@ -16915,23 +15806,19 @@ sub construct_course { # Open all assignments # if ($args->{'openall'}) { - my $opendate = time; - if ($args->{'openallfrom'} =~ /^\d+$/) { - $opendate = $args->{'openallfrom'}; - } my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate'; - my %storecontent = ($storeunder => $opendate, + my %storecontent = ($storeunder => time, $storeunder.'.type' => 'date_start'); - $outcome .= &mt('All assignments open starting [_1]', - &Apache::lonlocal::locallocaltime($opendate)).': '. - &Apache::lonnet::cput - ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed; + + $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput + ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed; } # # Set first page # unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank') || ($cloneid)) { + use LONCAPA::map; $outcome .= &mt('Setting first resource').': '; my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence'; @@ -16954,7 +15841,31 @@ sub construct_course { $outcome .= ($fatal?$errtext:'write ok').$linefeed; } - return (1,$outcome,\@clonemsg); +# +# Set params for Placement Tests +# + if ($args->{'crstype'} eq 'Placement') { + my %storecontent; + my $prefix=$$crsudom.'_'.$$crsunum.'.0.'; + my %defaults = ( + buttonshide => { value => 'yes', + type => 'string_yesno',}, + type => { value => 'randomizetry', + type => 'string_questiontype',}, + maxtries => { value => 1, + type => 'int_pos',}, + problemstatus => { value => 'no', + type => 'string_problemstatus',}, + ); + foreach my $key (keys(%defaults)) { + $storecontent{$prefix.$key} = $defaults{$key}{'value'}; + $storecontent{$prefix.$key.'.type'} = $defaults{$key}{'type'}; + } + &Apache::lonnet::cput + ('resourcedata',\%storecontent,$$crsudom,$$crsunum); + } + + return (1,$outcome); } sub make_unique_code { @@ -16967,7 +15878,7 @@ sub make_unique_code { my $tries = 0; my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom); my ($code,$error); - + while (($gotlock ne 'ok') && ($tries<3)) { $tries ++; sleep 1; @@ -17014,8 +15925,7 @@ sub generate_code { ############################################################ ############################################################ -#SD -# only Community and Course, or anything else? +# Community, Course and Placement Test sub course_type { my ($cid) = @_; if (!defined($cid)) { @@ -17033,18 +15943,19 @@ sub group_term { my %names = ( 'Course' => 'group', 'Community' => 'group', + 'Placement' => 'group', ); return $names{$crstype}; } sub course_types { - my @types = ('official','unofficial','community','textbook','lti'); + my @types = ('official','unofficial','community','textbook','placement'); my %typename = ( official => 'Official course', unofficial => 'Unofficial course', community => 'Community', textbook => 'Textbook course', - lti => 'LTI provider', + placement => 'Placement test', ); return (\@types,\%typename); } @@ -17124,24 +16035,6 @@ 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) = @_; @@ -17149,10 +16042,7 @@ sub init_user_environment { my $public=($username eq 'public' && $domain eq 'public'); -# See if old ID present, if so, remove - - my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv, - $coauthorenv); + my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv); my $now=time; if ($public) { @@ -17173,28 +16063,13 @@ sub init_user_environment { } if (!$cookie) { $cookie="publicuser_$oldest"; } } else { - # if this isn't a robot, kill any existing non-robot sessions + # See if old ID present, if so, remove if this isn't a robot, + # killing any existing non-robot sessions if (!$args->{'robot'}) { opendir(DIR,$lonids); while ($filename=readdir(DIR)) { if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) { - if (tie(my %oldenv,'GDBM_File',"$lonids/$filename", - &GDBM_READER(),0640)) { - my $linkedfile; - if (exists($oldenv{'user.linkedenv'})) { - $linkedfile = $oldenv{'user.linkedenv'}; - } - untie(%oldenv); - if (unlink("$lonids/$filename")) { - if ($linkedfile =~ /^[a-f0-9]+_linked$/) { - if (-l "$lonids/$linkedfile.id") { - unlink("$lonids/$linkedfile.id"); - } - } - } - } else { - unlink($lonids.'/'.$filename); - } + unlink($lonids.'/'.$filename); } } closedir(DIR); @@ -17217,7 +16092,7 @@ sub init_user_environment { # Initialize roles - ($userroles,$firstaccenv,$timerintenv,$coauthorenv) = + ($userroles,$firstaccenv,$timerintenv) = &Apache::lonnet::rolesinit($domain,$username,$authhost); } # ------------------------------------ Check browser type and MathML capability @@ -17229,8 +16104,7 @@ sub init_user_environment { my %userenv = &Apache::lonnet::dump('environment',$domain,$username); my ($tmp) = keys(%userenv); - if ($tmp !~ /^(con_lost|error|no_such_host)/i) { - } else { + if ($tmp =~ /^(con_lost|error|no_such_host)/i) { undef(%userenv); } if (($userenv{'interface'}) && (!$form->{'interface'})) { @@ -17245,7 +16119,6 @@ sub init_user_environment { # --------------------------------------------------------- Write first profile { - my $ip = &Apache::lonnet::get_requestor_ip(); my %initial_env = ("user.name" => $username, "user.domain" => $domain, @@ -17264,7 +16137,7 @@ sub init_user_environment { "request.course.sec" => '', "request.role" => 'cm', "request.role.adv" => $env{'user.adv'}, - "request.host" => $ip,); + "request.host" => $ENV{'REMOTE_ADDR'},); if ($form->{'localpath'}) { $initial_env{"browser.localpath"} = $form->{'localpath'}; @@ -17296,36 +16169,19 @@ sub init_user_environment { my %is_adv = ( is_adv => $env{'user.adv'} ); my %domdef = &Apache::lonnet::get_domain_defaults($domain); - foreach my $tool ('aboutme','blog','webdav','portfolio','timezone') { + foreach my $tool ('aboutme','blog','webdav','portfolio') { $userenv{'availabletools.'.$tool} = &Apache::lonnet::usertools_access($username,$domain,$tool,'reload', undef,\%userenv,\%domdef,\%is_adv); } - foreach my $crstype ('official','unofficial','community','textbook','lti') { + foreach my $crstype ('official','unofficial','community','textbook','placement') { $userenv{'canrequest.'.$crstype} = &Apache::lonnet::usertools_access($username,$domain,$crstype, 'reload','requestcourses', \%userenv,\%domdef,\%is_adv); } - if ((ref($userroles) eq 'HASH') && ($userroles->{'user.author'}) && - (exists($userroles->{"user.role.au./$domain/"}))) { - if ($userenv{'authoreditors'}) { - $userenv{'editors'} = $userenv{'authoreditors'}; - } elsif ($domdef{'editors'} ne '') { - $userenv{'editors'} = $domdef{'editors'}; - } else { - $userenv{'editors'} = 'edit,xml'; - } - if ($userenv{'authorarchive'}) { - $userenv{'canarchive'} = 1; - } elsif (($userenv{'authorarchive'} eq '') && - ($domdef{'archive'})) { - $userenv{'canarchive'} = 1; - } - } - $userenv{'canrequest.author'} = &Apache::lonnet::usertools_access($username,$domain,'requestauthor', 'reload','requestauthor', @@ -17333,7 +16189,7 @@ sub init_user_environment { my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'], $domain,$username); my $reqstatus = $reqauthor{'author_status'}; - if ($reqstatus eq 'approval' || $reqstatus eq 'approved') { + if ($reqstatus eq 'approval' || $reqstatus eq 'approved') { if (ref($reqauthor{'author'}) eq 'HASH') { $userenv{'requestauthorqueued'} = $reqstatus.':'. $reqauthor{'author'}{'timestamp'}; @@ -17354,11 +16210,6 @@ sub init_user_environment { if (ref($timerintenv) eq 'HASH') { &_add_to_env(\%disk_env,$timerintenv); } - if (ref($coauthorenv) eq 'HASH') { - if (keys(%{$coauthorenv})) { - &_add_to_env(\%disk_env,$coauthorenv); - } - } if (ref($args->{'extra_env'})) { &_add_to_env(\%disk_env,$args->{'extra_env'}); } @@ -17449,12 +16300,12 @@ and quotacheck.pl Inputs: -filterlist - anonymous array of fields to include as potential filters +filterlist - anonymous array of fields to include as potential filters crstype - course type roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used - to pop-open a course selector (will contain "extra element"). + to pop-open a course selector (will contain "extra element"). multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1 @@ -17470,19 +16321,19 @@ cloneruname - username of owner of new c clonerudom - domain of owner of new course who wants to clone -typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community) +typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community) codetitlesref - reference to array of titles of components in institutional codes (official courses) codedom - domain -formname - value of form element named "form". +formname - value of form element named "form". fixeddom - domain, if fixed. -prevphase - value to assign to form element named "phase" when going back to the previous screen +prevphase - value to assign to form element named "phase" when going back to the previous screen -cnameelement - name of form element in form on opener page which will receive title of selected course +cnameelement - name of form element in form on opener page which will receive title of selected course cnumelement - name of form element in form on opener page which will receive courseID of selected course @@ -17573,15 +16424,19 @@ sub build_filters { $createdfilterform = &timebased_select_form('createdfilter',$filter); } + my $prefix = $crstype; + if ($crstype eq 'Placement') { + $prefix = 'Placement Test' + } my %lt = &Apache::lonlocal::texthash( - 'cac' => "$crstype Activity", - 'ccr' => "$crstype Created", - 'cde' => "$crstype Title", - 'cdo' => "$crstype Domain", + 'cac' => "$prefix Activity", + 'ccr' => "$prefix Created", + 'cde' => "$prefix Title", + 'cdo' => "$prefix Domain", 'ins' => 'Institutional Code', 'inc' => 'Institutional Categorization', - 'cow' => "$crstype Owner/Co-owner", - 'cop' => "$crstype Personnel Includes", + 'cow' => "$prefix Owner/Co-owner", + 'cop' => "$prefix Personnel Includes", 'cog' => 'Type', ); @@ -17589,6 +16444,8 @@ sub build_filters { my $typeval = 'Course'; if ($crstype eq 'Community') { $typeval = 'Community'; + } elsif ($crstype eq 'Placement') { + $typeval = 'Placement'; } $typeselectform = ''; } else { @@ -17597,9 +16454,15 @@ sub build_filters { $typeselectform .= ' onchange="'.$onchange.'"'; } $typeselectform .= '>'."\n"; - foreach my $posstype ('Course','Community') { + foreach my $posstype ('Course','Community','Placement') { + my $shown; + if ($posstype eq 'Placement') { + $shown = &mt('Placement Test'); + } else { + $shown = &mt($posstype); + } $typeselectform.='\n"; + ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".$shown."\n"; } $typeselectform.=""; } @@ -17624,7 +16487,7 @@ sub build_filters { if (exists($filter->{'instcodefilter'})) { # if (($fixeddom) || ($formname eq 'requestcrs') || # ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) { - if ($codedom) { + if ($codedom) { $officialjs = 1; ($instcodeform,$jscript,$$numtitlesref) = &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker', @@ -17753,7 +16616,7 @@ $typeelement return $jscript.$clonewarning.$output; } -=pod +=pod =item * &timebased_select_form() @@ -17768,7 +16631,7 @@ item - name of form element (sincefilter filter - anonymous hash of criteria and their values Returns: HTML for a select box contained a blank, then six time selections, - with value set in incoming form variables currently selected. + with value set in incoming form variables currently selected. Side Effects: None @@ -17805,7 +16668,7 @@ page load completion for page showing se Inputs: None -Returns: markup containing updateFilters() and hideSearching() javascript functions. +Returns: markup containing updateFilters() and hideSearching() javascript functions. Side Effects: None @@ -17844,7 +16707,7 @@ to retrieve a hash for which keys are co Inputs: -dom - domain being searched +dom - domain being searched type - course type ('Course' or 'Community' or '.' if any). @@ -17856,7 +16719,7 @@ cloneruname - optional username of new c clonerudom - optional domain of new course owner -domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by, +domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by, (used when DC is using course creation form) codetitles - reference to array of titles of components in institutional codes (official courses). @@ -17866,8 +16729,8 @@ cc_clone - escaped comma separated list reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone -reqinstcode - institutional code of new course, where search_courses is used to identify potential - courses to clone +reqinstcode - institutional code of new course, where search_courses is used to identify potential + courses to clone Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type. @@ -17993,8 +16856,8 @@ $required - LON-CAPA version needed by c Returns: -$switchserver - query string tp append to /adm/switchserver call (if - current server's LON-CAPA version is too old. +$switchserver - query string tp append to /adm/switchserver call (if + current server's LON-CAPA version is too old. $warning - Message is displayed if no suitable server could be found. @@ -18107,7 +16970,7 @@ Inputs: $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp) $interval (optional) - Time which may elapse (in s) between last check for content - change in current course. (default: 600 s). + change in current course. (default: 600 s). Returns: an array; first element is: @@ -18115,9 +16978,9 @@ Returns: an array; first element is: 'switch' - if content updates mean user's session needs to be switched to a server running a newer LON-CAPA version - + 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded) - on current server hosting user's session + on current server hosting user's session '' - if no action required. @@ -18125,10 +16988,10 @@ Returns: an array; first element is: If first item element is 'switch': -second item is $switchwarning - Warning message if no suitable server found to host session. +second item is $switchwarning - Warning message if no suitable server found to host session. third item is $switchserver - query string to append to /adm/switchserver containing lonHostID - and current role. + and current role. otherwise: no other elements returned. @@ -18147,51 +17010,27 @@ sub needs_coursereinit { } if (($now-$env{'request.course.timechecked'})>$interval) { &Apache::lonnet::appenv({'request.course.timechecked'=>$now}); - my $blocked = &blocking_status('reinit',undef,$cnum,$cdom,undef,1); + my $blocked = &blocking_status('reinit',$cnum,$cdom,undef,1); if ($blocked) { return (); } - my $update; - my $lastmainchange = &Apache::lonnet::get_coursechange($cdom,$cnum); - my $lastsuppchange = &Apache::lonnet::get_suppchange($cdom,$cnum); - if ($lastmainchange > $env{'request.course.tied'}) { - my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum); - if ($needswitch) { - return ('switch',$switchwarning,$switchserver); - } - $update = 'main'; - } - if ($lastsuppchange > $env{'request.course.suppupdated'}) { - if ($update) { - $update = 'both'; - } else { - my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum); - if ($needswitch) { - return ('switch',$switchwarning,$switchserver); - } else { - $update = 'supp'; + my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum); + if ($lastchange > $env{'request.course.tied'}) { + my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired'); + if ($curr_reqd_hash{'internal.releaserequired'} ne '') { + my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'}; + if ($curr_reqd_hash{'internal.releaserequired'} ne $required) { + &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' => + $curr_reqd_hash{'internal.releaserequired'}}); + my ($switchserver,$switchwarning) = + &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'}, + $curr_reqd_hash{'internal.releaserequired'}); + if ($switchwarning ne '' || $switchserver ne '') { + return ('switch',$switchwarning,$switchserver); + } } } - return ($update); - } - } - return (); -} - -sub switch_for_update { - my ($loncaparev,$cdom,$cnum) = @_; - my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired'); - if ($curr_reqd_hash{'internal.releaserequired'} ne '') { - my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'}; - if ($curr_reqd_hash{'internal.releaserequired'} ne $required) { - &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' => - $curr_reqd_hash{'internal.releaserequired'}}); - my ($switchserver,$switchwarning) = - &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'}, - $curr_reqd_hash{'internal.releaserequired'}); - if ($switchwarning ne '' || $switchserver ne '') { - return ('switch',$switchwarning,$switchserver); - } + return ('update'); } } return (); @@ -18274,10 +17113,8 @@ sub parse_supplemental_title { my $name = &plainname($uname,$udom); $name = &HTML::Entities::encode($name,'"<>&\''); $renametitle = &HTML::Entities::encode($renametitle,'"<>&\''); - $title=''.&Apache::lonlocal::locallocaltime($time).' '.$name; - if ($foldertitle ne '') { - $title .= ':
    '.$foldertitle; - } + $title=''.&Apache::lonlocal::locallocaltime($time).' '. + $name.':
    '.$foldertitle; } if (wantarray) { return ($title,$foldertitle,$renametitle); @@ -18285,147 +17122,28 @@ sub parse_supplemental_title { return $title; } -sub get_supplemental { - my ($cnum,$cdom,$ignorecache,$possdel)=@_; - my $hashid=$cnum.':'.$cdom; - my ($supplemental,$cached,$set_httprefs); - unless ($ignorecache) { - ($supplemental,$cached) = &Apache::lonnet::is_cached_new('supplemental',$hashid); - } - unless (defined($cached)) { - my $chome=&Apache::lonnet::homeserver($cnum,$cdom); - unless ($chome eq 'no_host') { - my @order = @LONCAPA::map::order; - my @resources = @LONCAPA::map::resources; - my @resparms = @LONCAPA::map::resparms; - my @zombies = @LONCAPA::map::zombies; - my ($errors,%ids,%hidden); - $errors = - &recurse_supplemental($cnum,$cdom,'supplemental.sequence', - $errors,$possdel,\%ids,\%hidden); - @LONCAPA::map::order = @order; - @LONCAPA::map::resources = @resources; - @LONCAPA::map::resparms = @resparms; - @LONCAPA::map::zombies = @zombies; - $set_httprefs = 1; - if ($env{'request.course.id'} eq $cdom.'_'.$cnum) { - &Apache::lonnet::appenv({'request.course.suppupdated' => time}); - } - $supplemental = { - ids => \%ids, - hidden => \%hidden, - }; - &Apache::lonnet::do_cache_new('supplemental',$hashid,$supplemental,600); - } - } - return ($supplemental,$set_httprefs); -} - sub recurse_supplemental { - my ($cnum,$cdom,$suppmap,$errors,$possdel,$suppids,$hiddensupp,$hidden) = @_; - if (($suppmap) && (ref($suppids) eq 'HASH') && (ref($hiddensupp) eq 'HASH')) { - my $mapnum; - if ($suppmap eq 'supplemental.sequence') { - $mapnum = 0; - } else { - ($mapnum) = ($suppmap =~ /^supplemental_(\d+)\.sequence$/); - } + my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_; + if ($suppmap) { my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap); if ($fatal) { $errors ++; } else { - my @order = @LONCAPA::map::order; - if (@order > 0) { - my @resources = @LONCAPA::map::resources; - my @resparms = @LONCAPA::map::resparms; - foreach my $idx (@order) { - my ($title,$src,$ext,$type,$status)=split(/\:/,$resources[$idx]); + 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')) { - my $id = $mapnum.':'.$idx; - push(@{$suppids->{$src}},$id); - if (($hidden) || (&get_supp_parameter($resparms[$idx],'parameter_hiddenresource') =~ /^yes/i)) { - $hiddensupp->{$id} = 1; - } if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) { - $errors = &recurse_supplemental($cnum,$cdom,$1,$errors,$possdel,$suppids, - $hiddensupp,$hiddensupp->{$id}); + ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors); } else { - my $allowed; - if (($env{'request.role.adv'}) || (!$hiddensupp->{$id})) { - $allowed = 1; - } elsif ($possdel) { - foreach my $item (@{$suppids->{$src}}) { - next if ($item eq $id); - unless ($hiddensupp->{$item}) { - $allowed = 1; - last; - } - } - if ((!$allowed) && (exists($env{'httpref.'.$src}))) { - &Apache::lonnet::delenv('httpref.'.$src); - } - } - if ($allowed && (!exists($env{'httpref.'.$src}))) { - &Apache::lonnet::allowuploaded('/adm/coursedoc',$src); - } + $numfiles ++; } } } } } } - return $errors; -} - -sub set_supp_httprefs { - my ($cnum,$cdom,$supplemental,$possdel) = @_; - if (ref($supplemental) eq 'HASH') { - if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) { - foreach my $src (keys(%{$supplemental->{'ids'}})) { - next if ($src =~ /\.sequence$/); - if (ref($supplemental->{'ids'}->{$src}) eq 'ARRAY') { - my $allowed; - if ($env{'request.role.adv'}) { - $allowed = 1; - } else { - foreach my $id (@{$supplemental->{'ids'}->{$src}}) { - unless ($supplemental->{'hidden'}->{$id}) { - $allowed = 1; - last; - } - } - } - if (exists($env{'httpref.'.$src})) { - if ($possdel) { - unless ($allowed) { - &Apache::lonnet::delenv('httpref.'.$src); - } - } - } elsif ($allowed) { - &Apache::lonnet::allowuploaded('/adm/coursedoc',$src); - } - } - } - if ($env{'request.course.id'} eq $cdom.'_'.$cnum) { - &Apache::lonnet::appenv({'request.course.suppupdated' => time}); - } - } - } -} - -sub get_supp_parameter { - my ($resparm,$name)=@_; - return if ($resparm eq ''); - my $value=undef; - my $ptype=undef; - foreach (split('&&&',$resparm)) { - my ($thistype,$thisname,$thisvalue)=split('___',$_); - if ($thisname eq $name) { - $value=$thisvalue; - $ptype=$thistype; - } - } - return $value; + return ($numfiles,$errors); } sub symb_to_docspath { @@ -18498,72 +17216,11 @@ sub symb_to_docspath { return $path; } -sub validate_folderpath { - my ($supplementalflag,$allowed,$coursenum,$coursedom) = @_; - if ($env{'form.folderpath'} ne '') { - my @items = split(/\&/,$env{'form.folderpath'}); - my ($badpath,$changed,$got_supp,$supppath,%supphidden,%suppids); - for (my $i=0; $i<@items; $i++) { - my $odd = $i%2; - if (($odd) && (!$supplementalflag) && ($items[$i] !~ /^[^:]*:(|\d+):(|1):(|1):(|1):(|1)$/)) { - $badpath = 1; - } elsif ($odd && $supplementalflag) { - my $idx = $i-1; - if ($items[$i] =~ /^([^:]*)::(|1):::$/) { - my $esc_name = $1; - if ((!$allowed) || ($items[$idx] eq 'supplemental')) { - $supppath .= '&'.$esc_name; - $changed = 1; - } else { - $supppath .= '&'.$items[$i]; - } - } elsif (($allowed) && ($items[$idx] ne 'supplemental')) { - $changed = 1; - my $is_hidden; - unless ($got_supp) { - my ($supplemental) = &get_supplemental($coursenum,$coursedom); - if (ref($supplemental) eq 'HASH') { - if (ref($supplemental->{'hidden'}) eq 'HASH') { - %supphidden = %{$supplemental->{'hidden'}}; - } - if (ref($supplemental->{'ids'}) eq 'HASH') { - %suppids = %{$supplemental->{'ids'}}; - } - } - $got_supp = 1; - } - if (ref($suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}) eq 'ARRAY') { - my $mapid = $suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}->[0]; - if ($supphidden{$mapid}) { - $is_hidden = 1; - } - } - $supppath .= '&'.$items[$i].'::'.$is_hidden.':::'; - } else { - $supppath .= '&'.$items[$i]; - } - } elsif ((!$odd) && ($items[$i] !~ /^(default|supplemental)(|_\d+)$/)) { - $badpath = 1; - } elsif ($supplementalflag) { - $supppath .= '&'.$items[$i]; - } - last if ($badpath); - } - if ($badpath) { - delete($env{'form.folderpath'}); - } elsif ($changed && $supplementalflag) { - $supppath =~ s/^\&//; - $env{'form.folderpath'} = $supppath; - } - } - return; -} - sub captcha_display { - my ($context,$lonhost,$defdom) = @_; + my ($context,$lonhost) = @_; my ($output,$error); - my ($captcha,$pubkey,$privkey,$version) = - &get_captcha_config($context,$lonhost,$defdom); + my ($captcha,$pubkey,$privkey,$version) = + &get_captcha_config($context,$lonhost); if ($captcha eq 'original') { $output = &create_captcha(); unless ($output) { @@ -18579,9 +17236,9 @@ sub captcha_display { } sub captcha_response { - my ($context,$lonhost,$defdom) = @_; + my ($context,$lonhost) = @_; my ($captcha_chk,$captcha_error); - my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost,$defdom); + my ($captcha,$pubkey,$privkey,$version) = &get_captcha_config($context,$lonhost); if ($captcha eq 'original') { ($captcha_chk,$captcha_error) = &check_captcha(); } elsif ($captcha eq 'recaptcha') { @@ -18593,7 +17250,7 @@ sub captcha_response { } sub get_captcha_config { - my ($context,$lonhost,$dom_in_effect) = @_; + my ($context,$lonhost) = @_; my ($captcha,$pubkey,$privkey,$version,$hashtocheck); my $hostname = &Apache::lonnet::hostname($lonhost); my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname); @@ -18633,7 +17290,7 @@ sub get_captcha_config { $captcha = 'recaptcha'; $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'}; if ($version ne '2') { - $version = 1; + $version = 1; } } else { $captcha = 'original'; @@ -18641,27 +17298,6 @@ sub get_captcha_config { } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') { $captcha = 'original'; } - } elsif ($context eq 'passwords') { - if ($dom_in_effect) { - my %passwdconf = &Apache::lonnet::get_passwdconf($dom_in_effect); - if ($passwdconf{'captcha'} eq 'recaptcha') { - if (ref($passwdconf{'recaptchakeys'}) eq 'HASH') { - $pubkey = $passwdconf{'recaptchakeys'}{'public'}; - $privkey = $passwdconf{'recaptchakeys'}{'private'}; - } - if ($privkey && $pubkey) { - $captcha = 'recaptcha'; - $version = $passwdconf{'recaptchaversion'}; - if ($version ne '2') { - $version = 1; - } - } else { - $captcha = 'original'; - } - } elsif ($passwdconf{'captcha'} ne 'notused') { - $captcha = 'original'; - } - } } return ($captcha,$pubkey,$privkey,$version); } @@ -18679,17 +17315,13 @@ 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; } @@ -18728,8 +17360,7 @@ sub check_captcha { sub create_recaptcha { my ($pubkey,$version) = @_; if ($version >= 2) { - return '
    '. - '
    '; + return '
    '; } else { my $use_ssl; if ($ENV{'SERVER_PORT'} == 443) { @@ -18741,22 +17372,26 @@ sub create_recaptcha { &mt('If the text is hard to read, [_1] will replace them.', 'reCAPTCHA refresh'). '

    '; - } + } } sub check_recaptcha { my ($privkey,$version) = @_; my $captcha_chk; - my $ip = &Apache::lonnet::get_requestor_ip(); if ($version >= 2) { - my $ua = LWP::UserAgent->new; - $ua->timeout(10); my %info = ( - secret => $privkey, + secret => $privkey, response => $env{'form.g-recaptcha-response'}, - remoteip => $ip, + remoteip => $ENV{'REMOTE_ADDR'}, ); - my $response = $ua->post('https://www.google.com/recaptcha/api/siteverify',\%info); + my $request=new HTTP::Request('POST','https://www.google.com/recaptcha/api/siteverify'); + $request->content(join('&',map { + my $name = escape($_); + "$name=" . ( ref($info{$_}) eq 'ARRAY' + ? join("&$name=", map {escape($_) } @{$info{$_}}) + : &escape($info{$_}) ); + } keys(%info))); + my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',10,1); if ($response->is_success) { my $data = JSON::DWIW->from_json($response->decoded_content); if (ref($data) eq 'HASH') { @@ -18770,7 +17405,7 @@ sub check_recaptcha { my $captcha_result = $captcha->check_answer( $privkey, - $ip, + $ENV{'REMOTE_ADDR'}, $env{'form.recaptcha_challenge_field'}, $env{'form.recaptcha_response_field'}, ); @@ -18819,17 +17454,14 @@ 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. +# $context is the calling context -- roles, grades, contents, menu or flip. sub critical_redirect { my ($interval,$context) = @_; - 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',undef,$cnum,$cdom,undef,1); + my $blocked = &blocking_status('alert',$cnum,$cdom,undef,1); if ($blocked) { my $checkrole = "cm./$cdom/$cnum"; if ($env{'request.course.sec'} ne '') { @@ -18841,18 +17473,18 @@ sub critical_redirect { } } } - my @what=&Apache::lonnet::dump('critical', $env{'user.domain'}, + 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] ne 'no_such_host') && ($what[0]!~/^error\:/)) { - $redirecturl='/adm/email?critical=display'; - my $url=&Apache::lonnet::absolute_url().$redirecturl; + if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) { + $redirecturl='/adm/email?critical=display'; + my $url=&Apache::lonnet::absolute_url().$redirecturl; return (1, $url); } } - } + } return (); } @@ -18906,342 +17538,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')); - } - } - my $dellockres = &Apache::lonnet::del_dom('tiny',["lock\0$now"],$cdom); - unless ($dellockres eq 'ok') { - push(@errors,&mt('error: could not release lockfile')); - } - } else { - push(@errors,&mt('error: could not obtain lockfile')); - } - if (keys(%courseonly)) { - my $result = &Apache::lonnet::newput('tiny',\%courseonly,$cdom,$cnum); - if ($result ne 'ok') { - push(@errors,&mt('error: could not update course look-up of short 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); - return if (($remprotocol eq '') || ($remhost eq '')); - - $remprotocol = lc($remprotocol); - $remhost = lc($remhost); - my $remport = 80; - if ($remprotocol eq 'https') { - $remport = 443; - } - my ($result,$cached) = &Apache::lonnet::is_cached_new('noiframe',$remhost.':'.$remport); - if ($cached) { - unless ($nocache) { - if ($result) { - return 1; - } else { - return 0; - } - } - } - my $uselink; - my $request = new HTTP::Request('HEAD',$url); - my $ua = LWP::UserAgent->new; - $ua->timeout(5); - my $response=$ua->request($request); - if ($response->is_success()) { - my $secpolicy = lc($response->header('content-security-policy')); - my $xframeop = lc($response->header('x-frame-options')); - $secpolicy =~ s/^\s+|\s+$//g; - $xframeop =~ s/^\s+|\s+$//g; - if (($secpolicy ne '') || ($xframeop ne '')) { - my $remotehost = $remprotocol.'://'.$remhost; - my ($origin,$protocol,$port); - if ($ENV{'SERVER_PORT'} =~/^\d+$/) { - $port = $ENV{'SERVER_PORT'}; - } else { - $port = 80; - } - if ($absolute eq '') { - $protocol = 'http:'; - if ($port == 443) { - $protocol = 'https:'; - } - $origin = $protocol.'//'.lc($hostname); - } else { - $origin = lc($absolute); - ($protocol,$hostname) = ($absolute =~ m{^(https?:)//([^/]+)$}); - } - if (($secpolicy) && ($secpolicy =~ /\Qframe-ancestors\E([^;]*)(;|$)/)) { - my $framepolicy = $1; - $framepolicy =~ s/^\s+|\s+$//g; - my @policies = split(/\s+/,$framepolicy); - if (@policies) { - if (grep(/^\Q'none'\E$/,@policies)) { - $uselink = 1; - } else { - $uselink = 1; - if ((grep(/^\Q*\E$/,@policies)) || (grep(/^\Q$protocol\E$/,@policies)) || - (($origin ne '') && (grep(/^\Q$origin\E$/,@policies))) || - (($ip ne '') && (grep(/^\Q$ip\E$/,@policies)))) { - undef($uselink); - } - if ($uselink) { - if (grep(/^\Q'self'\E$/,@policies)) { - if (($origin ne '') && ($remotehost eq $origin)) { - undef($uselink); - } - } - } - if ($uselink) { - my @possok; - if ($ip ne '') { - push(@possok,$ip); - } - my $hoststr = ''; - foreach my $part (reverse(split(/\./,$hostname))) { - if ($hoststr eq '') { - $hoststr = $part; - } else { - $hoststr = "$part.$hoststr"; - } - if ($hoststr eq $hostname) { - push(@possok,$hostname); - } else { - push(@possok,"*.$hoststr"); - } - } - if (@possok) { - foreach my $poss (@possok) { - last if (!$uselink); - foreach my $policy (@policies) { - if ($policy =~ m{^(\Q$protocol\E//|)\Q$poss\E(\Q:$port\E|)$}) { - undef($uselink); - last; - } - } - } - } - } - } - } - } elsif ($xframeop ne '') { - $uselink = 1; - my @policies = split(/\s*,\s*/,$xframeop); - if (@policies) { - unless (grep(/^deny$/,@policies)) { - if ($origin ne '') { - if (grep(/^sameorigin$/,@policies)) { - if ($remotehost eq $origin) { - undef($uselink); - } - } - if ($uselink) { - foreach my $policy (@policies) { - if ($policy =~ /^allow-from\s*(.+)$/) { - my $allowfrom = $1; - if (($allowfrom ne '') && ($allowfrom eq $origin)) { - undef($uselink); - last; - } - } - } - } - } - } - } - } - } - } - if ($nocache) { - if ($cached) { - my $devalidate; - if ($uselink && !$result) { - $devalidate = 1; - } elsif (!$uselink && $result) { - $devalidate = 1; - } - if ($devalidate) { - &Apache::lonnet::devalidate_cache_new('noiframe',$remhost.':'.$remport); - } - } - } else { - if ($uselink) { - $result = 1; - } else { - $result = 0; - } - &Apache::lonnet::do_cache_new('noiframe',$remhost.':'.$remport,$result,3600); - } - return $uselink; -} - -sub page_menu { - my ($menucolls,$menunum) = @_; - my %menu; - foreach my $item (split(/;/,$menucolls)) { - my ($num,$value) = split(/\%/,$item); - if ($num eq $menunum) { - my @entries = split(/\&/,$value); - foreach my $entry (@entries) { - my ($name,$fields) = split(/=/,$entry); - if (($name eq 'top') || ($name eq 'inline') || ($name eq 'foot') || ($name eq 'main')) { - $menu{$name} = $fields; - } else { - my @shown; - if ($fields =~ /,/) { - @shown = split(/,/,$fields); - } else { - @shown = ($fields); - } - if (@shown) { - foreach my $field (@shown) { - next if ($field eq ''); - $menu{$field} = 1; - } - } - } - } - } - } - return %menu; -} - 1; __END__;