--- loncom/interface/loncommon.pm 2021/08/04 19:59:10 1.1363
+++ loncom/interface/loncommon.pm 2023/11/17 17:02:20 1.1418
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# a pile of common routines
#
-# $Id: loncommon.pm,v 1.1363 2021/08/04 19:59:10 raeburn Exp $
+# $Id: loncommon.pm,v 1.1418 2023/11/17 17:02:20 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::lonnet();
+use Apache::lonnavmaps();
use HTML::Entities;
use Apache::lonhtmlcommon();
use Apache::loncoursedata();
@@ -71,7 +71,9 @@ use Apache::lonuserutils();
use Apache::lonuserstate();
use Apache::courseclassifier();
use LONCAPA qw(:DEFAULT :match);
+use LONCAPA::ltiutils;
use LONCAPA::LWPReq;
+use LONCAPA::map();
use HTTP::Request;
use DateTime::TimeZone;
use DateTime::Locale;
@@ -435,7 +437,7 @@ sub studentbrowser_javascript {
+ENDJS
+ }
$endbodytag=
- "
".
+ "$endbodyjs
".
&mt('Continue').''.
$endbodytag;
}
}
+ if ((ref($args) eq 'HASH') && ($args->{'dashjs'})) {
+ $endbodytag = &Apache::lonhtmlcommon::dash_to_minus_js().$endbodytag;
+ }
return $endbodytag;
}
@@ -7704,6 +8241,11 @@ fieldset {
/* overflow: hidden; */
}
+fieldset#LC_selectuser {
+ margin: 0;
+ padding: 0;
+}
+
article.geogebraweb div {
margin: 0;
}
@@ -8247,6 +8789,10 @@ 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;
}
@@ -8364,6 +8910,18 @@ ul.LC_funclist li {
cursor:pointer;
}
+.LCisDisabled {
+ cursor: not-allowed;
+ opacity: 0.5;
+}
+
+a[aria-disabled="true"] {
+ color: currentColor;
+ display: inline-block; /* For IE11/ MS Edge bug */
+ pointer-events: none;
+ text-decoration: none;
+}
+
pre.LC_wordwrap {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
@@ -8537,7 +9095,13 @@ 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 too)
+ 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
domain -> force to color decorate a page for a specific
domain
function -> force usage of a specific rolish color scheme
@@ -8600,15 +9164,45 @@ sub headtag {
}
}
if (ref($args->{'redirect'})) {
- my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
- $url = &Apache::lonenc::check_encrypt($url);
+ my ($time,$url,$inhibit_continue,$to_opener,$skip_enc_check) = @{$args->{'redirect'}};
+ if (!$skip_enc_check) {
+ $url = &Apache::lonenc::check_encrypt($url);
+ }
if (!$inhibit_continue) {
$env{'internal.head.redirect'} = $url;
}
- $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'};
@@ -8833,7 +9427,8 @@ sub print_suppression {
}
my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
- my $blocked = &blocking_status('printout',$cnum,$cdom,undef,1);
+ my $clientip = &Apache::lonnet::get_requestor_ip();
+ my $blocked = &blocking_status('printout',$clientip,$cnum,$cdom,undef,1);
if ($blocked) {
my $checkrole = "cm./$cdom/$cnum";
if ($env{'request.course.sec'} ne '') {
@@ -8952,6 +9547,10 @@ $args - additional optional args support
will contain https:// if server uses
https (as per hosts.tab), but request is for http
hostname -> hostname, originally from $r->hostname(), (optional).
+ links_disabled -> Links in primary and secondary menus are disabled
+ (Can enable them once page has loaded - see lonroles.pm
+ for an example).
+ links_target -> Target for links, e.g., _parent (optional).
=back
@@ -9040,6 +9639,7 @@ sub start_page {
}
}
+ my $showncrumbs;
if (! exists($args->{'skip_phases'}{'body'}) ) {
if ($args->{'frameset'}) {
my $attr_string = &make_attr_string($args->{'force_register'},
@@ -9052,7 +9652,8 @@ sub start_page {
$args->{'only_body'}, $args->{'domain'},
$args->{'force_register'}, $args->{'no_nav_bar'},
$args->{'bgcolor'}, $args,
- \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll,\%menu);
+ \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll,
+ \%menu,\$showncrumbs);
}
}
@@ -9074,6 +9675,7 @@ 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') {
@@ -9096,12 +9698,20 @@ sub start_page {
} 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 {
$result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink);
}
+ }
}
return $result;
}
@@ -9143,7 +9753,7 @@ sub menucoll_in_effect {
if ($env{'request.course.id'}) {
$menucoll = $env{'course.'.$env{'request.course.id'}.'.menudefault'};
if ($env{'request.deeplink.login'}) {
- my ($deeplink_symb,$deeplink);
+ 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)/}) {
@@ -9153,11 +9763,21 @@ sub menucoll_in_effect {
$deeplink = $navmap->get_mapparam(undef,
&Apache::lonnet::declutter($env{'request.noversionuri'}),
'0.deeplink');
+ } else {
+ $check_login_symb = 1;
}
} else {
- $deeplink = &Apache::lonnet::EXT('resource.0.deeplink');
+ 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]);
@@ -9170,7 +9790,7 @@ sub menucoll_in_effect {
}
}
if ($deeplink ne '') {
- my ($state,$others,$listed,$scope,$protect,$display) = split(/,/,$deeplink);
+ my ($state,$others,$listed,$scope,$protect,$display,$target) = split(/,/,$deeplink);
if ($display =~ /^\d+$/) {
$deeplinkmenu = 1;
$menucoll = $display;
@@ -9188,29 +9808,85 @@ sub deeplink_login_symb {
my ($cnum,$cdom) = @_;
my $login_symb;
if ($env{'request.deeplink.login'}) {
- if ($env{'request.deeplink.login'} =~ 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);
- }
+ $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 ($tinyurl ne '') {
- my ($cnumreq,$posslogin) = split(/\&/,$tinyurl);
- if ($cnumreq eq $cnum) {
- $login_symb = $posslogin;
+ }
+ }
+ 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 $login_symb;
+ return %tooltypes;
}
sub wishlist_window {
@@ -9302,7 +9978,15 @@ ENDLINK
}
sub modal_adhoc_script {
- my ($funcname,$width,$height,$content)=@_;
+ my ($funcname,$width,$height,$content,$possmathjax)=@_;
+ my $mathjax;
+ if ($possmathjax) {
+ $mathjax = <<'ENDJAX';
+ if (typeof MathJax == 'object') {
+ MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
+ }
+ENDJAX
+ }
return (<
//
@@ -9320,7 +10005,7 @@ ENDADHOC
}
sub modal_adhoc_inner {
- my ($funcname,$width,$height,$content)=@_;
+ my ($funcname,$width,$height,$content,$possmathjax)=@_;
my $innerwidth=$width-20;
$content=&js_ready(
&start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
@@ -9329,12 +10014,12 @@ sub modal_adhoc_inner {
&end_scrollbox().
&end_page()
);
- return &modal_adhoc_script($funcname,$width,$height,$content);
+ return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax);
}
sub modal_adhoc_window {
- my ($funcname,$width,$height,$content,$linktext)=@_;
- return &modal_adhoc_inner($funcname,$width,$height,$content).
+ my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_;
+ return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax).
"".$linktext."";
}
@@ -9892,6 +10577,16 @@ Scalar: 1 if 'Course' to be used, 0 othe
###############################################
sub show_course {
+ my ($udom,$uname) = @_;
+ if (($udom ne '') && ($uname ne '')) {
+ if (($udom ne $env{'user.domain'}) || ($uname ne $env{'user.name'})) {
+ if (&Apache::lonnet::is_advanced_user($udom,$uname)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ }
my $course = !$env{'user.adv'};
if (!$env{'user.adv'}) {
foreach my $env (keys(%env)) {
@@ -13866,7 +14561,9 @@ sub process_extracted_files {
my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
$docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
$title;
- if (($outer !~ /\D/) && ($mapinner{$outer} !~ /\D/) && ($newidx !~ /\D/)) {
+ 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);
}
@@ -15921,27 +16618,33 @@ sub assign_category_rows {
sub commit_customrole {
- my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
+ my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context,$othdomby,$requester) = @_;
+ my $result = &Apache::lonnet::assigncustomrole(
+ $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,
+ $context,$othdomby,$requester);
my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
($start?', '.&mt('starting').' '.localtime($start):'').
- ($end?', ending '.localtime($end):'').': '.
- &Apache::lonnet::assigncustomrole(
- $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
- '
';
- return $output;
+ ($end?', ending '.localtime($end):'').': '.$result.'
';
+ if (wantarray) {
+ return ($output,$result);
+ } else {
+ return $output;
+ }
}
sub commit_standardrole {
- my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
- my ($output,$logmsg,$linefeed);
+ my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits,
+ $othdomby,$requester) = @_;
+ my ($output,$logmsg,$linefeed,$result);
if ($context eq 'auto') {
$linefeed = "\n";
} else {
$linefeed = "
\n";
}
if ($three eq 'st') {
- my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
- $one,$two,$sec,$context,$credits);
+ $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
+ $one,$two,$sec,$context,$credits,$othdomby,
+ $requester);
if (($result =~ /^error/) || ($result eq 'not_in_class') ||
($result eq 'unknown_course') || ($result eq 'refused')) {
$output = $logmsg.' '.&mt('Error: ').$result."\n";
@@ -15961,19 +16664,24 @@ sub commit_standardrole {
$output = &mt('Assigning').' '.$three.' in '.$url.
($start?', '.&mt('starting').' '.localtime($start):'').
($end?', '.&mt('ending').' '.localtime($end):'').': ';
- my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
+ $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,
+ '','',$context,$othdomby,$requester);
if ($context eq 'auto') {
$output .= $result.$linefeed;
} else {
$output .= ''.$result.''.$linefeed;
}
}
- return $output;
+ if (wantarray) {
+ return ($output,$result);
+ } else {
+ return $output;
+ }
}
sub commit_studentrole {
my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
- $credits) = @_;
+ $credits,$othdomby,$requester) = @_;
my ($result,$linefeed,$oldsecurl,$newsecurl);
if ($context eq 'auto') {
$linefeed = "\n";
@@ -15997,8 +16705,9 @@ sub commit_studentrole {
}
$oldsecurl = $uurl;
$expire_role_result =
- &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
- if ($env{'request.course.sec'} ne '') {
+ &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,
+ '','','',$context,$othdomby,$requester);
+ if ($env{'request.course.sec'} ne '') {
if ($expire_role_result eq 'refused') {
my @roles = ('st');
my @statuses = ('previous');
@@ -16024,7 +16733,8 @@ sub commit_studentrole {
&Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
undef,undef,undef,$sec,
$end,$start,'','',$cid,
- '',$context,$credits);
+ '',$context,$credits,'',
+ $othdomby,$requester);
if ($modify_section_result =~ /^ok/) {
if ($secchange == 1) {
if ($sec eq '') {
@@ -16393,6 +17103,9 @@ 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'};
}
@@ -16414,6 +17127,7 @@ 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/,/) {
@@ -16427,7 +17141,11 @@ 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.',';
- unless ($addcheck eq 'ok') {
+ if ($addcheck eq 'ok') {
+ unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
+ push(@oklcsecs,$gp);
+ }
+ } else {
push(@badclasses,$class);
}
}
@@ -16455,7 +17173,11 @@ 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.',';
- unless ($addcheck eq 'ok') {
+ if ($addcheck eq 'ok') {
+ unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
+ push(@oklcsecs,$gp);
+ }
+ } else {
push(@badclasses,$xl);
}
}
@@ -16518,6 +17240,36 @@ sub construct_course {
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'};
@@ -16636,7 +17388,6 @@ sub construct_course {
#
unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
|| ($cloneid)) {
- use LONCAPA::map;
$outcome .= &mt('Setting first resource').': ';
my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
@@ -16879,7 +17630,8 @@ sub init_user_environment {
my $public=($username eq 'public' && $domain eq 'public');
- my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
+ my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv,
+ $coauthorenv);
my $now=time;
if ($public) {
@@ -16945,7 +17697,7 @@ sub init_user_environment {
# Initialize roles
- ($userroles,$firstaccenv,$timerintenv) =
+ ($userroles,$firstaccenv,$timerintenv,$coauthorenv) =
&Apache::lonnet::rolesinit($domain,$username,$authhost);
}
# ------------------------------------ Check browser type and MathML capability
@@ -17023,8 +17775,8 @@ 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') {
- $userenv{'availabletools.'.$tool} =
+ foreach my $tool ('aboutme','blog','webdav','portfolio','portaccess','timezone') {
+ $userenv{'availabletools.'.$tool} =
&Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
undef,\%userenv,\%domdef,\%is_adv);
}
@@ -17036,6 +17788,17 @@ sub init_user_environment {
\%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';
+ }
+ }
+
$userenv{'canrequest.author'} =
&Apache::lonnet::usertools_access($username,$domain,'requestauthor',
'reload','requestauthor',
@@ -17092,6 +17855,11 @@ 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'});
}
@@ -17892,34 +18660,58 @@ sub needs_coursereinit {
}
if (($now-$env{'request.course.timechecked'})>$interval) {
&Apache::lonnet::appenv({'request.course.timechecked'=>$now});
- my $blocked = &blocking_status('reinit',$cnum,$cdom,undef,1);
+ my $blocked = &blocking_status('reinit',undef,$cnum,$cdom,undef,1);
if ($blocked) {
return ();
}
- 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);
- }
+ 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';
}
}
- return ('update');
+ 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 ();
}
sub update_content_constraints {
- my ($cdom,$cnum,$chome,$cid,$keeporder) = @_;
+ my ($cdom,$cnum,$chome,$cid) = @_;
my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
my (%checkresponsetypes,%checkcrsrestypes);
@@ -17967,25 +18759,7 @@ sub update_content_constraints {
}
undef($navmap);
}
- my (@resources,@order,@resparms,@zombies);
- if ($keeporder) {
- use LONCAPA::map;
- @resources = @LONCAPA::map::resources;
- @order = @LONCAPA::map::order;
- @resparms = @LONCAPA::map::resparms;
- @zombies = @LONCAPA::map::zombies;
- }
- my $suppmap = 'supplemental.sequence';
- my ($suppcount,$supptools,$errors) = (0,0,0);
- ($suppcount,$supptools,$errors) = &recurse_supplemental($cnum,$cdom,$suppmap,
- $suppcount,$supptools,$errors);
- if ($keeporder) {
- @LONCAPA::map::resources = @resources;
- @LONCAPA::map::order = @order;
- @LONCAPA::map::resparms = @resparms;
- @LONCAPA::map::zombies = @zombies;
- }
- if ($supptools) {
+ if (&Apache::lonnet::count_supptools($cnum,$cdom,1)) {
my ($major,$minor) = split(/\./,$checkcrsrestypes{'exttool'});
if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
($reqdmajor,$reqdminor) = ($major,$minor);
@@ -18011,7 +18785,7 @@ sub allmaps_incourse {
if ($lastchange > $env{'request.course.tied'}) {
my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
unless ($ferr) {
- &update_content_constraints($cdom,$cnum,$chome,$cid,1);
+ &update_content_constraints($cdom,$cnum,$chome,$cid);
}
}
my $navmap = Apache::lonnavmaps::navmap->new();
@@ -18037,8 +18811,10 @@ 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.':
'.$foldertitle;
+ $title=''.&Apache::lonlocal::locallocaltime($time).' '.$name;
+ if ($foldertitle ne '') {
+ $title .= ':
'.$foldertitle;
+ }
}
if (wantarray) {
return ($title,$foldertitle,$renametitle);
@@ -18046,32 +18822,147 @@ 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,$numfiles,$numexttools,$errors) = @_;
- if ($suppmap) {
+ 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 ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
if ($fatal) {
$errors ++;
} else {
- if ($#LONCAPA::map::resources > 0) {
- foreach my $res (@LONCAPA::map::resources) {
- my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
+ 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 (($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)$}) {
- ($numfiles,$numexttools,$errors) = &recurse_supplemental($cnum,$cdom,$1,
- $numfiles,$numexttools,$errors);
+ $errors = &recurse_supplemental($cnum,$cdom,$1,$errors,$possdel,$suppids,
+ $hiddensupp,$hiddensupp->{$id});
} else {
- if ($src =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) {
- $numexttools ++;
+ 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 ($numfiles,$numexttools,$errors);
+ 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;
}
sub symb_to_docspath {
@@ -18144,6 +19035,67 @@ 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 ($output,$error);
@@ -18264,9 +19216,10 @@ sub create_captcha {
if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
$output = ''."\n".
+ ''.
&mt('Type in the letters/numbers shown below').' '.
- ''.
- '
'.
+ ''.
+ '
'.
'';
last;
}
@@ -18312,7 +19265,8 @@ sub check_captcha {
sub create_recaptcha {
my ($pubkey,$version) = @_;
if ($version >= 2) {
- return '';
+ return ''.
+ '';
} else {
my $use_ssl;
if ($ENV{'SERVER_PORT'} == 443) {
@@ -18417,7 +19371,7 @@ sub critical_redirect {
if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {
my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
- my $blocked = &blocking_status('alert',$cnum,$cdom,undef,1);
+ my $blocked = &blocking_status('alert',undef,$cnum,$cdom,undef,1);
if ($blocked) {
my $checkrole = "cm./$cdom/$cnum";
if ($env{'request.course.sec'} ne '') {
@@ -18805,7 +19759,7 @@ sub page_menu {
my @entries = split(/\&/,$value);
foreach my $entry (@entries) {
my ($name,$fields) = split(/=/,$entry);
- if (($name eq 'top') || ($name eq 'inline') || ($name eq 'main')) {
+ if (($name eq 'top') || ($name eq 'inline') || ($name eq 'foot') || ($name eq 'main')) {
$menu{$name} = $fields;
} else {
my @shown;