--- loncom/interface/loncommon.pm 2010/10/29 20:41:43 1.985
+++ loncom/interface/loncommon.pm 2013/06/05 13:25:41 1.1075.2.39
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# a pile of common routines
#
-# $Id: loncommon.pm,v 1.985 2010/10/29 20:41:43 raeburn Exp $
+# $Id: loncommon.pm,v 1.1075.2.39 2013/06/05 13:25:41 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -67,9 +67,13 @@ use Apache::lonhtmlcommon();
use Apache::loncoursedata();
use Apache::lontexconvert();
use Apache::lonclonecourse();
+use Apache::lonuserutils();
+use Apache::lonuserstate();
use LONCAPA qw(:DEFAULT :match);
use DateTime::TimeZone;
use DateTime::Locale::Catalog;
+use Authen::Captcha;
+use Captcha::reCAPTCHA;
# ---------------------------------------------- Designs
use vars qw(%defaultdesign);
@@ -154,6 +158,8 @@ sub ssi_with_retries {
# ----------------------------------------------- Filetypes/Languages/Copyright
my %language;
my %supported_language;
+my %latex_language; # For choosing hyphenation in
+my %latex_language_bykey; # for choosing hyphenation from metadata
my %cprtag;
my %scprtag;
my %fe; my %fd; my %fm;
@@ -186,11 +192,15 @@ BEGIN {
while (my $line = <$fh>) {
next if ($line=~/^\#/);
chomp($line);
- my ($key,$two,$country,$three,$enc,$val,$sup)=(split(/\t/,$line));
+ my ($key,$two,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
$language{$key}=$val.' - '.$enc;
if ($sup) {
$supported_language{$key}=$sup;
}
+ if ($latex) {
+ $latex_language_bykey{$key} = $latex;
+ $latex_language{$two} = $latex;
+ }
}
close($fh);
}
@@ -409,7 +419,7 @@ sub studentbrowser_javascript {
+ENDRESBRW
+}
+
sub selectstudent_link {
- my ($form,$unameele,$udomele,$courseadvonly)=@_;
- my $callargs = "'".$form."','".$unameele."','".$udomele."'";
+ my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
+ my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
+ &Apache::lonhtmlcommon::entity_encode($unameele)."','".
+ &Apache::lonhtmlcommon::entity_encode($udomele)."'";
if ($env{'request.course.id'}) {
if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
&& !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
'/'.$env{'request.course.sec'})) {
return '';
}
+ $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
if ($courseadvonly) {
$callargs .= ",'',1,1";
}
@@ -452,7 +485,7 @@ sub selectstudent_link {
&mt('Select User').'';
}
if ($env{'request.role'}=~/^(au|dc|su)/) {
- $callargs .= ",1";
+ $callargs .= ",'',1";
return ''.
''.
&mt('Select User').' ';
@@ -460,6 +493,19 @@ sub selectstudent_link {
return '';
}
+sub selectresource_link {
+ my ($form,$reslink,$arg)=@_;
+
+ my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
+ &Apache::lonhtmlcommon::entity_encode($reslink)."'";
+ unless ($env{'request.course.id'}) { return $arg; }
+ return ''.
+ ''.
+ $arg.' ';
+}
+
+
+
sub authorbrowser_javascript {
return <<"ENDAUTHORBRW";
+
+ENDJS
+
+}
+
sub userbrowser_javascript {
my $id_functions = &javascript_index_functions();
return <<"ENDUSERBRW";
@@ -658,7 +751,7 @@ ENDUSERBRW
}
sub setsec_javascript {
- my ($sec_element,$formname,$role_element) = @_;
+ my ($sec_element,$formname,$role_element,$credits_element) = @_;
my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
$communityrolestr);
if ($role_element ne '') {
@@ -753,6 +846,14 @@ function setRole(crstype) {
}
|;
}
+ if ($credits_element) {
+ $setsections .= qq|
+function setCredits(defaultcredits) {
+ document.$formname.$credits_element.value = defaultcredits;
+ return;
+}
+|;
+ }
return $setsections;
}
@@ -766,6 +867,9 @@ sub selectcourse_link {
} elsif ($selecttype eq 'Course/Community') {
$linktext = &mt('Select Course/Community');
$type = '';
+ } elsif ($selecttype eq 'Select') {
+ $linktext = &mt('Select');
+ $type = '';
}
return ''
."
END
# output the initial values for the selection lists
- $result .= "\n";
+ $result .= "\n";
my @order = sort(keys(%{$hashref}));
if (ref($menuorder) eq 'ARRAY') {
@order = @{$menuorder};
@@ -1053,7 +1170,11 @@ END
$result .= " \n";
my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
$result .= $middletext;
- $result .= "\n";
+ $result .= "{$firstdefault}->{'default'};
my @secondorder = sort(keys(%select2));
@@ -1102,7 +1223,7 @@ sub help_open_topic {
my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
$text = "" if (not defined $text);
$stayOnPage = 0 if (not defined $stayOnPage);
- $width = 350 if (not defined $width);
+ $width = 500 if (not defined $width);
$height = 400 if (not defined $height);
my $filename = $topic;
$filename =~ s/ /_/g;
@@ -1113,7 +1234,9 @@ sub help_open_topic {
$topic=~s/\W/\_/g;
if (!$stayOnPage) {
- $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
+ $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
+ } elsif ($stayOnPage eq 'popup') {
+ $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
} else {
$link = "/adm/help/${filename}.hlp";
}
@@ -1146,27 +1269,22 @@ sub help_open_topic {
# This is a quicky function for Latex cheatsheet editing, since it
# appears in at least four places
sub helpLatexCheatsheet {
- my ($topic,$text,$not_author) = @_;
+ my ($topic,$text,$not_author,$stayOnPage) = @_;
my $out;
my $addOther = '';
if ($topic) {
- $addOther = ''.&Apache::loncommon::help_open_topic($topic,&mt($text),
- undef, undef, 600).
- ' ';
+ $addOther = ''.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).' ';
}
$out = '' # Start cheatsheet
.$addOther
.''
- .&Apache::loncommon::help_open_topic('Greek_Symbols',&mt('Greek Symbols'),
- undef,undef,600)
+ .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
.' '
- .&Apache::loncommon::help_open_topic('Other_Symbols',&mt('Other Symbols'),
- undef,undef,600)
+ .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
.' ';
unless ($not_author) {
$out .= ' '
- .&Apache::loncommon::help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),
- undef,undef,600)
+ .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
.' ';
}
$out .= ' '; # End cheatsheet
@@ -1287,7 +1405,7 @@ function helpMenu(target) {
return;
}
function writeHelp(caller) {
- caller.document.writeln('$start_page $end_page')
+ caller.document.writeln('$start_page\\n \\n \\n$end_page')
caller.document.close()
caller.focus()
}
@@ -1661,6 +1779,7 @@ Inputs: $workbook
Returns: $format, a hash reference.
+
=cut
###############################################################
@@ -1722,7 +1841,7 @@ sub create_workbook {
return (undef);
}
#
- $workbook->set_tempdir('/home/httpd/perl/tmp');
+ $workbook->set_tempdir(LONCAPA::tempdir());
#
my $format = &Apache::loncommon::define_excel_formats($workbook);
return ($workbook,$filename,$format);
@@ -1893,19 +2012,112 @@ sub select_form {
# For display filters
sub display_filter {
+ my ($context) = @_;
if (!$env{'form.show'}) { $env{'form.show'}=10; }
if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
- return ''.&mt('Records [_1]',
+ my $phraseinput = 'hidden';
+ my $includeinput = 'hidden';
+ my ($checked,$includetypestext);
+ if ($env{'form.displayfilter'} eq 'containing') {
+ $phraseinput = 'text';
+ if ($context eq 'parmslog') {
+ $includeinput = 'checkbox';
+ if ($env{'form.includetypes'}) {
+ $checked = ' checked="checked"';
+ }
+ $includetypestext = &mt('Include parameter types');
+ }
+ } else {
+ $includetypestext = ' ';
+ }
+ my ($additional,$secondid,$thirdid);
+ if ($context eq 'parmslog') {
+ $additional =
+ ' '.
+ ' '.$includetypestext.' '.
+ ' ';
+ $secondid = 'includetypes';
+ $thirdid = 'includetypestext';
+ }
+ my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
+ '$secondid','$thirdid')";
+ return ''.&mt('Records: [_1]',
&Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
(&mt('all'),10,20,50,100,1000,10000))).
' '.
- &mt('Filter [_1]',
+ &mt('Filter: [_1]',
&select_form($env{'form.displayfilter'},
'displayfilter',
{'currentfolder' => 'Current folder/page',
'containing' => 'Containing phrase',
- 'none' => 'None'})).
- ' ';
+ 'none' => 'None'},$onchange)).' '.
+ ' '.$additional;
+}
+
+sub display_filter_js {
+ my $includetext = &mt('Include parameter types');
+ return <<"ENDJS";
+
+function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
+ var firstType = 'hidden';
+ if (setter.options[setter.selectedIndex].value == 'containing') {
+ firstType = 'text';
+ }
+ firstObject = document.getElementById(firstid);
+ if (typeof(firstObject) == 'object') {
+ if (firstObject.type != firstType) {
+ changeInputType(firstObject,firstType);
+ }
+ }
+ if (context == 'parmslog') {
+ var secondType = 'hidden';
+ if (firstType == 'text') {
+ secondType = 'checkbox';
+ }
+ secondObject = document.getElementById(secondid);
+ if (typeof(secondObject) == 'object') {
+ if (secondObject.type != secondType) {
+ changeInputType(secondObject,secondType);
+ }
+ }
+ var textItem = document.getElementById(thirdid);
+ var currtext = textItem.innerHTML;
+ var newtext;
+ if (firstType == 'text') {
+ newtext = '$includetext';
+ } else {
+ newtext = ' ';
+ }
+ if (currtext != newtext) {
+ textItem.innerHTML = newtext;
+ }
+ }
+ return;
+}
+
+function changeInputType(oldObject,newType) {
+ var newObject = document.createElement('input');
+ newObject.type = newType;
+ if (oldObject.size) {
+ newObject.size = oldObject.size;
+ }
+ if (oldObject.value) {
+ newObject.value = oldObject.value;
+ }
+ if (oldObject.name) {
+ newObject.name = oldObject.name;
+ }
+ if (oldObject.id) {
+ newObject.id = oldObject.id;
+ }
+ oldObject.parentNode.replaceChild(newObject,oldObject);
+ return;
+}
+
+ENDJS
}
sub gradeleveldescription {
@@ -1949,7 +2161,7 @@ sub select_level_form {
=pod
-=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms)
+=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoma,$excdoms)
Returns a string containing a form to
allow a user to select the domain to preform an operation in.
@@ -1962,25 +2174,31 @@ If the $showdomdesc flag is set, the dom
The optional $onchange argument specifies what should occur if the domain selector is changed, e.g., 'this.form.submit()' if the form is to be automatically submitted.
-The optional $incdoms is a reference to an array of domains which will be the only available options.
+The optional $incdoms is a reference to an array of domains which will be the only available options.
+
+The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
=cut
#-------------------------------------------
sub select_dom_form {
- my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms) = @_;
+ my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms) = @_;
if ($onchange) {
$onchange = ' onchange="'.$onchange.'"';
}
- my @domains;
+ my (@domains,%exclude);
if (ref($incdoms) eq 'ARRAY') {
@domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
} else {
@domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
}
if ($includeempty) { @domains=('',@domains); }
+ if (ref($excdoms) eq 'ARRAY') {
+ map { $exclude{$_} = 1; } @{$excdoms};
+ }
my $selectdomain = "\n";
foreach my $dom (@domains) {
+ next if ($exclude{$dom});
$selectdomain.="'.$dom;
if ($showdomdesc) {
@@ -2274,12 +2492,16 @@ function changed_text(choice,currentform
}
function set_auth_radio_buttons(newvalue,currentform) {
+ var numauthchoices = currentform.login.length;
+ if (typeof numauthchoices == "undefined") {
+ return;
+ }
var i=0;
- while (i < currentform.login.length) {
+ while (i < numauthchoices) {
if (currentform.login[i].value == newvalue) { break; }
i++;
}
- if (i == currentform.login.length) {
+ if (i == numauthchoices) {
return;
}
current.radiovalue = newvalue;
@@ -2290,7 +2512,7 @@ END
return $result;
}
-sub authform_authorwarning{
+sub authform_authorwarning {
my $result='';
$result=''.
&mt('As a general rule, only authors or co-authors should be '.
@@ -2299,16 +2521,16 @@ sub authform_authorwarning{
return $result;
}
-sub authform_nochange{
+sub authform_nochange {
my %in = (
formname => 'document.cu',
kerb_def_dom => 'MSU.EDU',
@_,
);
- my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
+ my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
my $result;
- if (keys(%can_assign) == 0) {
- $result = &mt('Under you current role you are not permitted to change login settings for this user');
+ if (!$authnum) {
+ $result = &mt('Under your current role you are not permitted to change login settings for this user');
} else {
$result = ''.&mt('[_1] Do not change login data',
' ';
+ $authtype = ' ';
}
}
}
@@ -2390,9 +2612,9 @@ sub authform_kerberos {
$krbcheck.' />';
}
if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
- ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
+ ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
$in{'curr_authtype'} eq 'krb5') ||
- (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
+ (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
$in{'curr_authtype'} eq 'krb4')) {
$result .= &mt
('[_1] Kerberos authenticated with domain [_2] '.
@@ -2428,14 +2650,14 @@ sub authform_kerberos {
return $result;
}
-sub authform_internal{
+sub authform_internal {
my %in = (
formname => 'document.cu',
kerb_def_dom => 'MSU.EDU',
@_,
);
my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
- my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
+ my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
if (defined($in{'curr_authtype'})) {
if ($in{'curr_authtype'} eq 'int') {
if ($can_assign{'int'}) {
@@ -2464,7 +2686,7 @@ sub authform_internal{
if (defined($in{'mode'})) {
if ($in{'mode'} eq 'modifycourse') {
if ($authnum == 1) {
- $authtype = ' ';
+ $authtype = ' ';
}
}
}
@@ -2483,14 +2705,14 @@ sub authform_internal{
return $result;
}
-sub authform_local{
+sub authform_local {
my %in = (
formname => 'document.cu',
kerb_def_dom => 'MSU.EDU',
@_,
);
my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
- my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
+ my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
if (defined($in{'curr_authtype'})) {
if ($in{'curr_authtype'} eq 'loc') {
if ($can_assign{'loc'}) {
@@ -2519,7 +2741,7 @@ sub authform_local{
if (defined($in{'mode'})) {
if ($in{'mode'} eq 'modifycourse') {
if ($authnum == 1) {
- $authtype = ' ';
+ $authtype = ' ';
}
}
}
@@ -2537,14 +2759,14 @@ sub authform_local{
return $result;
}
-sub authform_filesystem{
+sub authform_filesystem {
my %in = (
formname => 'document.cu',
kerb_def_dom => 'MSU.EDU',
@_,
);
my ($fsyscheck,$result,$authtype,$autharg,$jscall);
- my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
+ my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
if (defined($in{'curr_authtype'})) {
if ($in{'curr_authtype'} eq 'fsys') {
if ($can_assign{'fsys'}) {
@@ -2570,7 +2792,7 @@ sub authform_filesystem{
if (defined($in{'mode'})) {
if ($in{'mode'} eq 'modifycourse') {
if ($authnum == 1) {
- $authtype = ' ';
+ $authtype = ' ';
}
}
}
@@ -2765,6 +2987,7 @@ database which holds them.
Uses global $thesaurus_db_file.
+
=cut
###############################################################
@@ -3036,12 +3259,12 @@ sub noteswrapper {
# ------------------------------------------------------------- Aboutme Wrapper
sub aboutmewrapper {
- my ($link,$username,$domain,$target)=@_;
+ my ($link,$username,$domain,$target,$class)=@_;
if (!defined($username) && !defined($domain)) {
return;
}
- return ''.$link.' ';
+ return ''.$link.' ';
}
# ------------------------------------------------------------ Syllabus Wrapper
@@ -3142,11 +3365,29 @@ sub languagedescription {
($supported_language{$code}?' ('.&mt('interface available').')':'');
}
+=pod
+
+=item * &plainlanguagedescription
+
+Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
+and the language character encoding (e.g. ISO) separated by a ' - ' string.
+
+=cut
+
sub plainlanguagedescription {
my $code=shift;
return $language{$code};
}
+=pod
+
+=item * &supportedlanguagecode
+
+Returns the supported language code (e.g. sptutf maps to pt) given a language
+code.
+
+=cut
+
sub supportedlanguagecode {
my $code=shift;
return $supported_language{$code};
@@ -3154,6 +3395,35 @@ sub supportedlanguagecode {
=pod
+=item * &latexlanguage()
+
+Given a language key code returns the correspondnig language to use
+to select the correct hyphenation on LaTeX printouts. This is undef if there
+is no supported hyphenation for the language code.
+
+=cut
+
+sub latexlanguage {
+ my $code = shift;
+ return $latex_language{$code};
+}
+
+=pod
+
+=item * &latexhyphenation()
+
+Same as above but what's supplied is the language as it might be stored
+in the metadata.
+
+=cut
+
+sub latexhyphenation {
+ my $key = shift;
+ return $latex_language_bykey{$key};
+}
+
+=pod
+
=item * ©rightids()
returns list of all copyrights
@@ -3417,7 +3687,9 @@ sub get_previous_attempt {
my ($ign,@parts) = split(/\./,$key);
if ($#parts > 0) {
my $data=$parts[-1];
+ next if ($data eq 'foilorder');
pop(@parts);
+ $prevattempts.=''.&mt('Part ').join('.',@parts).' '.$data.' ';
if ($data eq 'type') {
unless ($showsurv) {
my $id = join(',',@parts);
@@ -3426,10 +3698,7 @@ sub get_previous_attempt {
$lasthidden{$ign.'.'.$id} = 1;
}
}
- delete($lasthash{$key});
- } else {
- $prevattempts.=''.&mt('Part ').join('.',@parts).' '.$data.' ';
- }
+ }
} else {
if ($#parts == 0) {
$prevattempts.=''.$parts[0].' ';
@@ -3453,6 +3722,7 @@ sub get_previous_attempt {
''.&mt('Transaction [_1]',$version).' ';
if (@hidden) {
foreach my $key (sort(keys(%lasthash))) {
+ next if ($key =~ /\.foilorder$/);
my $hide;
foreach my $id (@hidden) {
if ($key =~ /^\Q$id\E/) {
@@ -3481,6 +3751,7 @@ sub get_previous_attempt {
}
} else {
foreach my $key (sort(keys(%lasthash))) {
+ next if ($key =~ /\.foilorder$/);
my $value = &format_previous_attempt_value($key,
$returnhash{$version.':'.$key});
$prevattempts.=''.$value.' ';
@@ -3492,6 +3763,7 @@ sub get_previous_attempt {
my @currhidden = keys(%lasthidden);
$prevattempts.=&start_data_table_row().''.&mt('Current').' ';
foreach my $key (sort(keys(%lasthash))) {
+ next if ($key =~ /\.foilorder$/);
if (%typeparts) {
my $hidden;
foreach my $id (@currhidden) {
@@ -3543,10 +3815,33 @@ sub get_previous_attempt {
sub format_previous_attempt_value {
my ($key,$value) = @_;
- if ($key =~ /timestamp/) {
+ if (($key =~ /timestamp/) || ($key=~/duedate/)) {
$value = &Apache::lonlocal::locallocaltime($value);
} elsif (ref($value) eq 'ARRAY') {
$value = '('.join(', ', @{ $value }).')';
+ } elsif ($key =~ /answerstring$/) {
+ my %answers = &Apache::lonnet::str2hash($value);
+ my @anskeys = sort(keys(%answers));
+ if (@anskeys == 1) {
+ my $answer = $answers{$anskeys[0]};
+ if ($answer =~ m{\0}) {
+ $answer =~ s{\0}{,}g;
+ }
+ my $tag_internal_answer_name = 'INTERNAL';
+ if ($anskeys[0] eq $tag_internal_answer_name) {
+ $value = $answer;
+ } else {
+ $value = $anskeys[0].'='.$answer;
+ }
+ } else {
+ foreach my $ans (@anskeys) {
+ my $answer = $answers{$ans};
+ if ($answer =~ m{\0}) {
+ $answer =~ s{\0}{,}g;
+ }
+ $value .= $ans.'='.$answer.' ';;
+ }
+ }
} else {
$value = &unescape($value);
}
@@ -3825,9 +4120,7 @@ sub findallcourses {
$udom = $env{'user.domain'};
}
if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
- my $extra = &Apache::lonnet::freeze_escape({'skipcheck' => 1});
- my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname,'.',undef,
- $extra);
+ my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
if (!%roles) {
%roles = (
cc => 1,
@@ -3852,18 +4145,25 @@ sub findallcourses {
if ($tstart) {
next if ($tstart > $now);
}
- my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
+ my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
(undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
+ my $value = $trole.'/'.$cdom.'/';
if ($secpart eq '') {
($cnum,$role) = split(/_/,$cnumpart);
$sec = 'none';
- $realsec = '';
+ $value .= $cnum.'/';
} else {
$cnum = $cnumpart;
($sec,$role) = split(/_/,$secpart);
- $realsec = $sec;
+ $value .= $cnum.'/'.$sec;
+ }
+ if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
+ unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
+ push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
+ }
+ } else {
+ @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
}
- $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
}
} else {
foreach my $key (keys(%env)) {
@@ -3881,11 +4181,19 @@ sub findallcourses {
if ($now>$endtime) { $active=0; }
}
if ($active) {
+ my $value = $role.'/'.$cdom.'/'.$cnum.'/';
if ($sec eq '') {
$sec = 'none';
+ } else {
+ $value .= $sec;
+ }
+ if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
+ unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
+ push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
+ }
+ } else {
+ @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
}
- $courses{$cdom.'_'.$cnum}{$sec} =
- $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
}
}
}
@@ -3896,7 +4204,7 @@ sub findallcourses {
###############################################
sub blockcheck {
- my ($setters,$activity,$uname,$udom) = @_;
+ my ($setters,$activity,$uname,$udom,$url) = @_;
if (!defined($udom)) {
$udom = $env{'user.domain'};
@@ -3908,13 +4216,14 @@ sub blockcheck {
# If uname and udom are for a course, check for blocks in the course.
if (&Apache::lonnet::is_course($udom,$uname)) {
- my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
- my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
- return ($startblock,$endblock);
+ my ($startblock,$endblock,$triggerblock) =
+ &get_blocks($setters,$activity,$udom,$uname,$url);
+ return ($startblock,$endblock,$triggerblock);
}
my $startblock = 0;
my $endblock = 0;
+ my $triggerblock = '';
my %live_courses = &findallcourses(undef,$uname,$udom);
# If uname is for a user, and activity is course-specific, i.e.,
@@ -3978,34 +4287,38 @@ sub blockcheck {
if ($otheruser) {
# Resource belongs to user other than current user.
# Assemble privs for that user, and check for 'evb' priv.
- my ($trole,$tdom,$tnum,$tsec);
- my $entry = $live_courses{$course}{$sec};
- if ($entry =~ /^cr/) {
- ($trole,$tdom,$tnum,$tsec) =
- ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
- } else {
- ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
- }
- my ($spec,$area,$trest,%allroles,%userroles);
- $area = '/'.$tdom.'/'.$tnum;
- $trest = $tnum;
- if ($tsec ne '') {
- $area .= '/'.$tsec;
- $trest .= '/'.$tsec;
- }
- $spec = $trole.'.'.$area;
- if ($trole =~ /^cr/) {
- &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
- $tdom,$spec,$trest,$area);
- } else {
- &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
- $tdom,$spec,$trest,$area);
- }
- my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
- if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
- if ($1) {
- $no_userblock = 1;
- last;
+ my (%allroles,%userroles);
+ if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
+ foreach my $entry (@{$live_courses{$course}{$sec}}) {
+ my ($trole,$tdom,$tnum,$tsec);
+ if ($entry =~ /^cr/) {
+ ($trole,$tdom,$tnum,$tsec) =
+ ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
+ } else {
+ ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
+ }
+ my ($spec,$area,$trest);
+ $area = '/'.$tdom.'/'.$tnum;
+ $trest = $tnum;
+ if ($tsec ne '') {
+ $area .= '/'.$tsec;
+ $trest .= '/'.$tsec;
+ }
+ $spec = $trole.'.'.$area;
+ if ($trole =~ /^cr/) {
+ &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
+ $tdom,$spec,$trest,$area);
+ } else {
+ &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
+ $tdom,$spec,$trest,$area);
+ }
+ }
+ my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
+ if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
+ if ($1) {
+ $no_userblock = 1;
+ last;
+ }
}
}
} else {
@@ -4025,46 +4338,139 @@ sub blockcheck {
# Retrieve blocking times and identity of locker for course
# of specified user, unless user has 'evb' privilege.
- my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
+ my ($start,$end,$trigger) =
+ &get_blocks($setters,$activity,$cdom,$cnum,$url);
if (($start != 0) &&
(($startblock == 0) || ($startblock > $start))) {
$startblock = $start;
+ if ($trigger ne '') {
+ $triggerblock = $trigger;
+ }
}
if (($end != 0) &&
(($endblock == 0) || ($endblock < $end))) {
$endblock = $end;
+ if ($trigger ne '') {
+ $triggerblock = $trigger;
+ }
}
}
- return ($startblock,$endblock);
+ return ($startblock,$endblock,$triggerblock);
}
sub get_blocks {
- my ($setters,$activity,$cdom,$cnum) = @_;
+ my ($setters,$activity,$cdom,$cnum,$url) = @_;
my $startblock = 0;
my $endblock = 0;
+ my $triggerblock = '';
my $course = $cdom.'_'.$cnum;
$setters->{$course} = {};
$setters->{$course}{'staff'} = [];
$setters->{$course}{'times'} = [];
- my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
- foreach my $record (keys(%records)) {
- my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
- if ($start <= time && $end >= time) {
- my ($staff_name,$staff_dom,$title,$blocks) =
- &parse_block_record($records{$record});
- if ($blocks->{$activity} eq 'on') {
- push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
- push(@{$$setters{$course}{'times'}}, [$start,$end]);
- if ( ($startblock == 0) || ($startblock > $start) ) {
- $startblock = $start;
+ $setters->{$course}{'triggers'} = [];
+ my (@blockers,%triggered);
+ my $now = time;
+ my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
+ if ($activity eq 'docs') {
+ @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks);
+ foreach my $block (@blockers) {
+ if ($block =~ /^firstaccess____(.+)$/) {
+ my $item = $1;
+ my $type = 'map';
+ my $timersymb = $item;
+ if ($item eq 'course') {
+ $type = 'course';
+ } elsif ($item =~ /___\d+___/) {
+ $type = 'resource';
+ } else {
+ $timersymb = &Apache::lonnet::symbread($item);
}
- if ( ($endblock == 0) || ($endblock < $end) ) {
- $endblock = $end;
+ my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
+ my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
+ $triggered{$block} = {
+ start => $start,
+ end => $end,
+ type => $type,
+ };
+ }
+ }
+ } else {
+ foreach my $block (keys(%commblocks)) {
+ if ($block =~ m/^(\d+)____(\d+)$/) {
+ my ($start,$end) = ($1,$2);
+ if ($start <= time && $end >= time) {
+ if (ref($commblocks{$block}) eq 'HASH') {
+ if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
+ if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
+ unless(grep(/^\Q$block\E$/,@blockers)) {
+ push(@blockers,$block);
+ }
+ }
+ }
+ }
+ }
+ } elsif ($block =~ /^firstaccess____(.+)$/) {
+ my $item = $1;
+ my $timersymb = $item;
+ my $type = 'map';
+ if ($item eq 'course') {
+ $type = 'course';
+ } elsif ($item =~ /___\d+___/) {
+ $type = 'resource';
+ } else {
+ $timersymb = &Apache::lonnet::symbread($item);
+ }
+ my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
+ my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
+ if ($start && $end) {
+ if (($start <= time) && ($end >= time)) {
+ unless (grep(/^\Q$block\E$/,@blockers)) {
+ push(@blockers,$block);
+ $triggered{$block} = {
+ start => $start,
+ end => $end,
+ type => $type,
+ };
+ }
+ }
}
}
}
}
- return ($startblock,$endblock);
+ foreach my $blocker (@blockers) {
+ my ($staff_name,$staff_dom,$title,$blocks) =
+ &parse_block_record($commblocks{$blocker});
+ push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
+ my ($start,$end,$triggertype);
+ if ($blocker =~ m/^(\d+)____(\d+)$/) {
+ ($start,$end) = ($1,$2);
+ } elsif (ref($triggered{$blocker}) eq 'HASH') {
+ $start = $triggered{$blocker}{'start'};
+ $end = $triggered{$blocker}{'end'};
+ $triggertype = $triggered{$blocker}{'type'};
+ }
+ if ($start) {
+ push(@{$$setters{$course}{'times'}}, [$start,$end]);
+ if ($triggertype) {
+ push(@{$$setters{$course}{'triggers'}},$triggertype);
+ } else {
+ push(@{$$setters{$course}{'triggers'}},0);
+ }
+ if ( ($startblock == 0) || ($startblock > $start) ) {
+ $startblock = $start;
+ if ($triggertype) {
+ $triggerblock = $blocker;
+ }
+ }
+ if ( ($endblock == 0) || ($endblock < $end) ) {
+ $endblock = $end;
+ if ($triggertype) {
+ $triggerblock = $blocker;
+ }
+ }
+ }
+ }
+ return ($startblock,$endblock,$triggerblock);
}
sub parse_block_record {
@@ -4088,39 +4494,50 @@ sub parse_block_record {
}
sub blocking_status {
- my ($activity,$uname,$udom) = @_;
- my %setters;
-
- # check for active blocking
- my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
+ my ($activity,$uname,$udom,$url) = @_;
+ my %setters;
- my $blocked = $startblock && $endblock ? 1 : 0;
-
- # caller just wants to know whether a block is active
- if (!wantarray) { return $blocked; }
-
- # build a link to a popup window containing the details
- my $querystring = "?activity=$activity";
- # $uname and $udom decide whose portfolio the user is trying to look at
- $querystring .= "&udom=$udom" if $udom;
- $querystring .= "&uname=$uname" if $uname;
-
- my $output .= <<'END_MYBLOCK';
- function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
- var options = "width=" + w + ",height=" + h + ",";
- options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
- options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
- var newWin = window.open(url, wdwName, options);
- newWin.focus();
- }
+# check for active blocking
+ my ($startblock,$endblock,$triggerblock) =
+ &blockcheck(\%setters,$activity,$uname,$udom,$url);
+ my $blocked = 0;
+ if ($startblock && $endblock) {
+ $blocked = 1;
+ }
+
+# caller just wants to know whether a block is active
+ if (!wantarray) { return $blocked; }
+
+# build a link to a popup window containing the details
+ my $querystring = "?activity=$activity";
+# $uname and $udom decide whose portfolio the user is trying to look at
+ if ($activity eq 'port') {
+ $querystring .= "&udom=$udom" if $udom;
+ $querystring .= "&uname=$uname" if $uname;
+ } elsif ($activity eq 'docs') {
+ $querystring .= '&url='.&HTML::Entities::encode($url,'&"');
+ }
+
+ my $output .= <<'END_MYBLOCK';
+function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
+ var options = "width=" + w + ",height=" + h + ",";
+ options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
+ options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
+ var newWin = window.open(url, wdwName, options);
+ newWin.focus();
+}
END_MYBLOCK
- $output = Apache::lonhtmlcommon::scripttag($output);
+ $output = Apache::lonhtmlcommon::scripttag($output);
- my $popupUrl = "/adm/blockingstatus/$querystring";
- my $text = mt('Communication Blocked');
-
- $output .= <<"END_BLOCK";
+ my $popupUrl = "/adm/blockingstatus/$querystring";
+ my $text = &mt('Communication Blocked');
+ if ($activity eq 'docs') {
+ $text = &mt('Content Access Blocked');
+ } elsif ($activity eq 'printout') {
+ $text = &mt('Printing Blocked');
+ }
+ $output .= <<"END_BLOCK";
@@ -4131,7 +4548,7 @@ END_MYBLOCK
END_BLOCK
- return ($blocked, $output);
+ return ($blocked, $output);
}
###############################################
@@ -4241,8 +4658,7 @@ sub get_domainconf {
if (ref($domconfig{'login'}{$key}) eq 'HASH') {
if ($key eq 'loginvia') {
if (ref($domconfig{'login'}{'loginvia'}) eq 'HASH') {
- my @ids = &Apache::lonnet::current_machine_ids();
- foreach my $hostname (@ids) {
+ foreach my $hostname (keys(%{$domconfig{'login'}{'loginvia'}})) {
if (ref($domconfig{'login'}{'loginvia'}{$hostname}) eq 'HASH') {
if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
@@ -4251,7 +4667,7 @@ sub get_domainconf {
$designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
} else {
- $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
+ $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
}
if ($domconfig{'login'}{'loginvia'}{$hostname}{'exempt'}) {
$designhash{$udom.'.login.loginvia_exempt_'.$hostname} = $domconfig{'login'}{'loginvia'}{$hostname}{'exempt'};
@@ -4334,7 +4750,7 @@ sub get_legacy_domconf {
close($fh);
}
}
- if (-e '/home/httpd/html/adm/lonDomLogos/'.$udom.'.gif') {
+ if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
$legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
}
return %legacyhash;
@@ -4392,7 +4808,10 @@ sub designparm {
return $env{'environment.color.'.$which};
}
$domain=&determinedomain($domain);
- my %domdesign = &get_domainconf($domain);
+ my %domdesign;
+ unless ($domain eq 'public') {
+ %domdesign = &get_domainconf($domain);
+ }
my $output;
if ($domdesign{$domain.'.'.$which} ne '') {
$output = $domdesign{$domain.'.'.$which};
@@ -4417,27 +4836,39 @@ sub designparm {
=item * &authorspace()
-Inputs: ./.
+Inputs: $url (usually will be undef).
-Returns: Path to the Construction Space of the current user's
- accessed author space
- The author space will be that of the current user
- when accessing the own author space
- and that of the co-author/assistent co-author
- when accessing the co-author's/assistent co-author's
- space
+Returns: Path to Construction Space containing the resource or
+ directory being viewed (or for which action is being taken).
+ If $url is provided, and begins /priv//
+ the path will be that portion of the $context argument.
+ Otherwise the path will be for the author space of the current
+ user when the current role is author, or for that of the
+ co-author/assistant co-author space when the current role
+ is co-author or assistant co-author.
=cut
sub authorspace {
+ my ($url) = @_;
+ if ($url ne '') {
+ if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
+ return $1;
+ }
+ }
my $caname = '';
- if ($env{'request.role'} =~ /^ca|^aa/) {
- (undef,$caname) =
+ my $cadom = '';
+ if ($env{'request.role'} =~ /^(?:ca|aa)/) {
+ ($cadom,$caname) =
($env{'request.role'}=~/($match_domain)\/($match_username)$/);
- } else {
+ } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
$caname = $env{'user.name'};
+ $cadom = $env{'user.domain'};
}
- return '/priv/'.$caname.'/';
+ if (($caname ne '') && ($cadom ne '')) {
+ return "/priv/$cadom/$caname/";
+ }
+ return;
}
##############################################
@@ -4455,7 +4886,7 @@ Returns: HTML div with $content
sub head_subbox {
my ($content)=@_;
my $output =
- ''
+ '
'
.$content
.'
'
}
@@ -4465,7 +4896,9 @@ sub head_subbox {
=item * &CSTR_pageheader()
-Inputs: ./.
+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.
Returns: HTML div with CSTR path and recent box
To be included on Construction Space pages
@@ -4473,12 +4906,19 @@ Returns: HTML div with CSTR path and rec
=cut
sub CSTR_pageheader {
- # this is for resources; directories have customtitle, and crumbs
- # and select recent are created in lonpubdir.pm
- my ($uname,$thisdisfn)=
- ($env{'request.filename'} =~ m|^/home/([^/]+)/public_html/(.*)|);
- my $formaction='/priv/'.$uname.'/'.$thisdisfn;
- $formaction=~s/\/+/\//g;
+ my ($trailfile) = @_;
+ if ($trailfile eq '') {
+ $trailfile = $env{'request.filename'};
+ }
+
+# this is for resources; directories have customtitle, and crumbs
+# and select recent are created in lonpubdir.pm
+
+ my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
+ my ($udom,$uname,$thisdisfn)=
+ ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
+ my $formaction = "/priv/$udom/$uname/$thisdisfn";
+ $formaction =~ s{/+}{/}g;
my $parentpath = '';
my $lastitem = '';
@@ -4495,7 +4935,7 @@ sub CSTR_pageheader {
.'
'.&mt('Construction Space:').' '
.'
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
inherit_jsmath -> when creating popup window in a page,
should it have jsmath forced on by the
current page
bread_crumbs -> Array containing breadcrumbs
bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs
+ group -> includes the current group, if page is for a
+ specific group
=back
@@ -6693,32 +7478,12 @@ $args - additional optional args support
sub start_page {
my ($title,$head_extra,$args) = @_;
#&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
-#SD
-#I don't see why we copy certain elements of %$args to %head_args
-#head args is passed to headtag() and this routine only reads those
-#keys that are needed. There doesn't happen any writes or any processing
-#of other keys.
-#proposal: just pass $args to headtag instead of \%head_args and delete
-#marked lines
-#<- MARK
- my %head_args;
- foreach my $arg ('redirect','force_register','domain','function',
- 'bgcolor','frameset','no_nav_bar','only_body',
- 'no_auto_mt_title') {
- if (defined($args->{$arg})) {
- $head_args{$arg} = $args->{$arg};
- }
- }
-#MARK ->
$env{'internal.start_page'}++;
- my $result;
+ my ($result,@advtools);
if (! exists($args->{'skip_phases'}{'head'}) ) {
- $result .=
- &xml_begin() . &headtag($title,$head_extra,\%head_args);
-#replace prev line by
-# &xml_begin() . &headtag($title, $head_extra, $args);
+ $result .= &xml_begin() . &headtag($title, $head_extra, $args);
}
if (! exists($args->{'skip_phases'}{'body'}) ) {
@@ -6732,7 +7497,8 @@ sub start_page {
$args->{'function'}, $args->{'add_entries'},
$args->{'only_body'}, $args->{'domain'},
$args->{'force_register'}, $args->{'no_nav_bar'},
- $args->{'bgcolor'}, $args);
+ $args->{'bgcolor'}, $args->{'no_inline_link'},
+ $args, \@advtools);
}
}
@@ -6761,6 +7527,10 @@ sub start_page {
&Apache::lonhtmlcommon::add_breadcrumb($crumb);
}
}
+ # if @advtools array contains items add then to the breadcrumbs
+ if (@advtools > 0) {
+ &Apache::lonmenu::advtools_crumbs(@advtools);
+ }
#if bread_crumbs_component exists show it as headline else show only the breadcrumbs
if(exists($args->{'bread_crumbs_component'})){
@@ -6768,6 +7538,11 @@ sub start_page {
}else{
$result .= &Apache::lonhtmlcommon::breadcrumbs();
}
+ } 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;
}
@@ -6784,13 +7559,14 @@ sub end_page {
}
$result .= &Apache::lonxml::xmlend($target,$parser);
}
-
if ($args->{'frameset'}) {
$result .= '';
} else {
$result .= &endbodytag($args);
}
- $result .= "\n";
+ unless ($args->{'notbody'}) {
+ $result .= "\n";
+ }
if ($args->{'js_ready'}) {
$result = &js_ready($result);
@@ -6803,6 +7579,285 @@ sub end_page {
return $result;
}
+sub wishlist_window {
+ return(<<'ENDWISHLIST');
+
+ENDWISHLIST
+}
+
+sub modal_window {
+ return(<<'ENDMODAL');
+
+ENDMODAL
+}
+
+sub modal_link {
+ my ($link,$linktext,$width,$height,$target,$scrolling,$title)=@_;
+ unless ($width) { $width=480; }
+ unless ($height) { $height=400; }
+ unless ($scrolling) { $scrolling='yes'; }
+ my $target_attr;
+ if (defined($target)) {
+ $target_attr = 'target="'.$target.'"';
+ }
+ return <<"ENDLINK";
+
+ $linktext
+ENDLINK
+}
+
+sub modal_adhoc_script {
+ my ($funcname,$width,$height,$content)=@_;
+ return (<
+//
+
+ENDADHOC
+}
+
+sub modal_adhoc_inner {
+ my ($funcname,$width,$height,$content)=@_;
+ my $innerwidth=$width-20;
+ $content=&js_ready(
+ &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
+ &start_scrollbox($width.'px',$innerwidth.'px',$height.'px').
+ $content.
+ &end_scrollbox().
+ &end_page()
+ );
+ return &modal_adhoc_script($funcname,$width,$height,$content);
+}
+
+sub modal_adhoc_window {
+ my ($funcname,$width,$height,$content,$linktext)=@_;
+ return &modal_adhoc_inner($funcname,$width,$height,$content).
+ "".$linktext." ";
+}
+
+sub modal_adhoc_launch {
+ my ($funcname,$width,$height,$content)=@_;
+ return &modal_adhoc_inner($funcname,$width,$height,$content).(<
+//
+
+ENDLAUNCH
+}
+
+sub modal_adhoc_close {
+ return (<
+//
+
+ENDCLOSE
+}
+
+sub togglebox_script {
+ return(<
+//
+
+ENDTOGGLE
+}
+
+sub start_togglebox {
+ my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
+ unless ($heading) { $heading=''; } else { $heading.=' '; }
+ unless ($showtext) { $showtext=&mt('show'); }
+ unless ($hidetext) { $hidetext=&mt('hide'); }
+ unless ($headerbg) { $headerbg='#FFFFFF'; }
+ return &start_data_table().
+ &start_data_table_header_row().
+ ''.$heading.
+ '['.$showtext.' ] '.
+ &end_data_table_header_row().
+ '';
+}
+
+sub end_togglebox {
+ return ' '.&end_data_table();
+}
+
+sub LCprogressbar_script {
+ my ($id)=@_;
+ return(<
+//
+
+ENDPROGRESS
+}
+
+sub LCprogressbarUpdate_script {
+ return(<
+.ui-progressbar { position:relative; }
+.pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
+
+
+ENDPROGRESSUPDATE
+}
+
+my $LClastpercent;
+my $LCidcnt;
+my $LCcurrentid;
+
+sub LCprogressbar {
+ my ($r)=(@_);
+ $LClastpercent=0;
+ $LCidcnt++;
+ $LCcurrentid=$$.'_'.$LCidcnt;
+ my $starting=&mt('Starting');
+ my $content=(<
+ $starting
+
+ENDPROGBAR
+ &r_print($r,$content.&LCprogressbar_script($LCcurrentid));
+}
+
+sub LCprogressbarUpdate {
+ 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
+}
+
+sub LCprogressbarClose {
+ my ($r)=@_;
+ $LClastpercent=0;
+ &r_print($r,<
+//
+
+ENDCLOSE
+}
+
+sub r_print {
+ my ($r,$to_print)=@_;
+ if ($r) {
+ $r->print($to_print);
+ $r->rflush();
+ } else {
+ print($to_print);
+ }
+}
+
sub html_encode {
my ($result) = @_;
@@ -6810,6 +7865,7 @@ sub html_encode {
return $result;
}
+
sub js_ready {
my ($result) = @_;
@@ -6846,11 +7902,34 @@ sub validate_page {
}
}
+
+sub start_scrollbox {
+ my ($outerwidth,$width,$height,$id,$bgcolor)=@_;
+ unless ($outerwidth) { $outerwidth='520px'; }
+ unless ($width) { $width='500px'; }
+ unless ($height) { $height='200px'; }
+ my ($table_id,$div_id,$tdcol);
+ if ($id ne '') {
+ $table_id = " id='table_$id'";
+ $div_id = " id='div_$id'";
+ }
+ if ($bgcolor ne '') {
+ $tdcol = "background-color: $bgcolor;";
+ }
+ return <<"END";
+
+END
+}
+
+sub end_scrollbox {
+ return '
';
+}
+
sub simple_error_page {
my ($r,$title,$msg) = @_;
my $page =
&Apache::loncommon::start_page($title).
- &mt($msg).
+ ''.&mt($msg).'
'.
&Apache::loncommon::end_page();
if (ref($r)) {
$r->print($page);
@@ -6873,10 +7952,14 @@ sub simple_error_page {
}
sub start_data_table {
- my ($add_class) = @_;
+ my ($add_class,$id) = @_;
my $css_class = (join(' ','LC_data_table',$add_class));
+ my $table_id;
+ if (defined($id)) {
+ $table_id = ' id="'.$id.'"';
+ }
&start_data_table_count();
- return ''."\n";
+ return ''."\n";
}
sub end_data_table {
@@ -7003,7 +8086,7 @@ sub get_users_function {
$function='admin';
}
if (($env{'request.role'}=~/^(au|ca|aa)/) ||
- ($ENV{'REQUEST_URI'}=~/^(\/priv|\~)/)) {
+ ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
$function='author';
}
return $function;
@@ -7061,8 +8144,7 @@ role status: active, previous or future.
sub check_user_status {
my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
- my $extra = &Apache::lonnet::freeze_escape({'skipcheck' => 1});
- my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname,'.',undef,$extra);
+ my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
my @uroles = keys %userinfo;
my $srchstr;
my $active_chk = 'none';
@@ -7140,7 +8222,19 @@ sub get_sections {
my %sectioncount;
my $now = time;
- if (!defined($possible_roles) || (grep(/^st$/,@$possible_roles))) {
+ my $check_students = 1;
+ my $only_students = 0;
+ if (ref($possible_roles) eq 'ARRAY') {
+ if (grep(/^st$/,@{$possible_roles})) {
+ if (@{$possible_roles} == 1) {
+ $only_students = 1;
+ }
+ } else {
+ $check_students = 0;
+ }
+ }
+
+ 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();
@@ -7167,6 +8261,9 @@ sub get_sections {
}
}
}
+ if ($only_students) {
+ return %sectioncount;
+ }
my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
foreach my $user (sort(keys(%courseroles))) {
if ($user !~ /^(\w{2})/) { next; }
@@ -7314,7 +8411,7 @@ sub get_course_users {
active => 'Active',
future => 'Future',
);
- my %nothide;
+ my (%nothide,@possdoms);
if ($hidepriv) {
my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
@@ -7324,6 +8421,10 @@ sub get_course_users {
$nothide{$user} = 1;
}
}
+ my @possdoms = ($cdom);
+ if ($coursehash{'checkforpriv'}) {
+ push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
+ }
}
foreach my $person (sort(keys(%coursepersonnel))) {
my $match = 0;
@@ -7359,7 +8460,7 @@ sub get_course_users {
}
if ($uname ne '' && $udom ne '') {
if ($hidepriv) {
- if ((&Apache::lonnet::privileged($uname,$udom)) &&
+ if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
(!$nothide{$uname.':'.$udom})) {
next;
}
@@ -7645,7 +8746,7 @@ sub get_secgrprole_info {
}
sub user_picker {
- my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype) = @_;
+ my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context) = @_;
my $currdom = $dom;
my %curr_selected = (
srchin => 'dom',
@@ -7736,10 +8837,15 @@ sub user_picker {
$srchtypesel .= "\n \n";
my ($newuserscript,$new_user_create);
-
+ my $context_dom = $env{'request.role.domain'};
+ if ($context eq 'requestcrs') {
+ if ($env{'form.coursedom'} ne '') {
+ $context_dom = $env{'form.coursedom'};
+ }
+ }
if ($forcenewuser) {
if (ref($srch) eq 'HASH') {
- if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $env{'request.role.domain'}) {
+ if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
if ($cancreate) {
$new_user_create = ' &"').'" onclick="javascript:setSearch(\'1\','.$caller.');" />
';
} else {
@@ -7778,7 +8884,7 @@ function setSearch(createnew,callingForm
}
}
for (var i=0; i 0) {
- $output = ' '.&mt("$text{$check} with the following format(s) may only be used for verified users at [_1]:",$domdesc).' ';
+ $output = ' '.
+ &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
+ '',' ',$domdesc).
+ ' ';
foreach my $rule (@{$ruleorder}) {
if (ref($curr_rules) eq 'ARRAY') {
if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
@@ -8113,7 +9222,8 @@ sub get_standard_codeitems {
=item * sorted_slots()
-Sorts an array of slot names in order of slot start time (earliest first).
+Sorts an array of slot names in order of an optional sort key,
+default sort is by slot start time (earliest first).
Inputs:
@@ -8123,15 +9233,16 @@ slotsarr - Reference to array of unsort
slots - Reference to hash of hash, where outer hash keys are slot names.
+sortkey - Name of key in inner hash to be sorted on (e.g., starttime).
+
=back
Returns:
=over 4
-sorted - An array of slot names sorted by the start time of the slot.
-
-=back
+sorted - An array of slot names sorted by a specified sort key
+ (default sort key is start time of the slot).
=back
@@ -8139,13 +9250,16 @@ sorted - An array of slot names sorted
sub sorted_slots {
- my ($slotsarr,$slots) = @_;
+ my ($slotsarr,$slots,$sortkey) = @_;
+ if ($sortkey eq '') {
+ $sortkey = 'starttime';
+ }
my @sorted;
if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
@sorted =
sort {
if (ref($slots->{$a}) && ref($slots->{$b})) {
- return $slots->{$a}{'starttime'} <=> $slots->{$b}{'starttime'}
+ return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
}
if (ref($slots->{$a})) { return -1;}
if (ref($slots->{$b})) { return 1;}
@@ -8155,9 +9269,136 @@ sub sorted_slots {
return @sorted;
}
+=pod
+
+=item * get_future_slots()
+
+Inputs:
+
+=over 4
+
+cnum - course number
+
+cdom - course domain
+
+now - current UNIX time
+
+symb - optional symb
+
+=back
+
+Returns:
+
+=over 4
+
+sorted_reservable - ref to array of student_schedulable slots currently
+ reservable, ordered by end date of reservation period.
+
+reservable_now - ref to hash of student_schedulable slots currently
+ reservable.
+
+ Keys in inner hash are:
+ (a) symb: either blank or symb to which slot use is restricted.
+ (b) endreserve: end date of reservation period.
+
+sorted_future - ref to array of student_schedulable slots reservable in
+ the future, ordered by start date of reservation period.
+
+future_reservable - ref to hash of student_schedulable slots reservable
+ in the future.
+
+ Keys in inner hash are:
+ (a) symb: either blank or symb to which slot use is restricted.
+ (b) startreserve: start date of reservation period.
+
+=back
+
+=cut
+
+sub get_future_slots {
+ my ($cnum,$cdom,$now,$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}->{'starttime'} > $now) &&
+ ($slots{$slot}->{'endtime'} > $now)) {
+ if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
+ my $userallowed = 0;
+ if ($slots{$slot}->{'allowedsections'}) {
+ my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
+ if (!defined($env{'request.role.sec'})
+ && grep(/^No section assigned$/,@allowed_sec)) {
+ $userallowed=1;
+ } else {
+ if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
+ $userallowed=1;
+ }
+ }
+ unless ($userallowed) {
+ if (defined($env{'request.course.groups'})) {
+ my @groups = split(/:/,$env{'request.course.groups'});
+ foreach my $group (@groups) {
+ if (grep(/^\Q$group\E$/,@allowed_sec)) {
+ $userallowed=1;
+ last;
+ }
+ }
+ }
+ }
+ }
+ if ($slots{$slot}->{'allowedusers'}) {
+ my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
+ my $user = $env{'user.name'}.':'.$env{'user.domain'};
+ if (grep(/^\Q$user\E$/,@allowed_users)) {
+ $userallowed = 1;
+ }
+ }
+ next unless($userallowed);
+ }
+ my $startreserve = $slots{$slot}->{'startreserve'};
+ my $endreserve = $slots{$slot}->{'endreserve'};
+ my $symb = $slots{$slot}->{'symb'};
+ if (($startreserve < $now) &&
+ (!$endreserve || $endreserve > $now)) {
+ my $lastres = $endreserve;
+ if (!$lastres) {
+ $lastres = $slots{$slot}->{'starttime'};
+ }
+ $reservable_now{$slot} = {
+ symb => $symb,
+ endreserve => $lastres
+ };
+ } elsif (($startreserve > $now) &&
+ (!$endreserve || $endreserve > $startreserve)) {
+ $future_reservable{$slot} = {
+ symb => $symb,
+ startreserve => $startreserve
+ };
+ }
+ }
+ }
+ my @unsorted_reservable = keys(%reservable_now);
+ if (@unsorted_reservable > 0) {
+ @sorted_reservable =
+ &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
+ }
+ my @unsorted_future = keys(%future_reservable);
+ if (@unsorted_future > 0) {
+ @sorted_future =
+ &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
+ }
+ return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
+}
=pod
+=back
+
=head1 HTTP Helpers
=over 4
@@ -8296,144 +9537,601 @@ sub get_env_multiple {
sub ask_for_embedded_content {
my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
- my (%subdependencies,%dependencies,%newfiles);
- my $num = 0;
- my $upload_output;
- foreach my $embed_file (keys(%{$allfiles})) {
- unless ($embed_file =~ m{^\w+://} || $embed_file =~ m{^/}) {
- my ($relpath,$fname);
- if ($embed_file =~ m{/}) {
- my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
- $subdependencies{$path}{$fname} = 1;
- } else {
- $dependencies{$embed_file} = 1;
- }
+ my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
+ %currsubfile,%unused,$rem);
+ my $counter = 0;
+ my $numnew = 0;
+ my $numremref = 0;
+ my $numinvalid = 0;
+ my $numpathchg = 0;
+ my $numexisting = 0;
+ my $numunused = 0;
+ my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
+ $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path);
+ my $heading = &mt('Upload embedded files');
+ my $buttontext = &mt('Upload');
+
+ my ($navmap,$cdom,$cnum);
+ if ($env{'request.course.id'}) {
+ if ($actionurl eq '/adm/dependencies') {
+ $navmap = Apache::lonnavmaps::navmap->new();
}
+ $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
}
- my ($url,$udom,$uname,$getpropath);
- if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+ if (($actionurl eq '/adm/portfolio') ||
+ ($actionurl eq '/adm/coursegrp_portfolio')) {
my $current_path='/';
if ($env{'form.currentpath'}) {
$current_path = $env{'form.currentpath'};
}
if ($actionurl eq '/adm/coursegrp_portfolio') {
- $udom = $env{'course.'.$env{'request.course.id'}.'.domain'};
- $uname = $env{'course.'.$env{'request.course.id'}.'.num'};
+ $udom = $cdom;
+ $uname = $cnum;
$url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
} else {
$udom = $env{'user.domain'};
$uname = $env{'user.name'};
$url = '/userfiles/portfolio';
}
+ $toplevel = $url.'/';
$url .= $current_path;
$getpropath = 1;
- } elsif ($actionurl eq '/adm/upload') {
- ($uname,my $rest) = ($args->{'current_path'} =~ m{/priv/($match_username)/?(.*)$});
- $url = '/home/'.$uname.'/public_html';
+ } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
+ ($actionurl eq '/adm/imsimport')) {
+ my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
+ $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
+ $toplevel = $url;
if ($rest ne '') {
- $url .= '/'.$rest;
+ $url .= $rest;
+ }
+ } elsif ($actionurl eq '/adm/coursedocs') {
+ if (ref($args) eq 'HASH') {
+ $url = $args->{'docs_url'};
+ $toplevel = $url;
+ if ($args->{'context'} eq 'paste') {
+ ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
+ ($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') {
+ if ($env{'request.course.id'} ne '') {
+ if (ref($args) eq 'HASH') {
+ $url = $args->{'docs_url'};
+ $title = $args->{'docs_title'};
+ $toplevel = $url;
+ unless ($toplevel =~ m{^/}) {
+ $toplevel = "/$url";
+ }
+ ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
+ if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
+ $path = $1;
+ } else {
+ ($path) =
+ ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
+ }
+ $fileloc = &Apache::lonnet::filelocation('',$toplevel);
+ $fileloc =~ s{^/}{};
+ ($filename) = ($fileloc =~ m{.+/([^/]+)$});
+ $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
+ }
+ }
+ } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
+ $udom = $cdom;
+ $uname = $cnum;
+ $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
+ $toplevel = $url;
+ $path = $url;
+ $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
+ $fileloc =~ s{^/}{};
+ }
+ foreach my $file (keys(%{$allfiles})) {
+ my $embed_file;
+ if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
+ $embed_file = $1;
+ } else {
+ $embed_file = $file;
+ }
+ my $absolutepath;
+ if ($embed_file =~ m{^\w+://}) {
+ $newfiles{$embed_file} = 1;
+ $mapping{$embed_file} = $embed_file;
+ } else {
+ if ($embed_file =~ m{^/}) {
+ $absolutepath = $embed_file;
+ $embed_file =~ s{^(/+)}{};
+ }
+ if ($embed_file =~ m{/}) {
+ my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
+ $path = &check_for_traversal($path,$url,$toplevel);
+ my $item = $fname;
+ if ($path ne '') {
+ $item = $path.'/'.$fname;
+ $subdependencies{$path}{$fname} = 1;
+ } else {
+ $dependencies{$item} = 1;
+ }
+ if ($absolutepath) {
+ $mapping{$item} = $absolutepath;
+ } else {
+ $mapping{$item} = $embed_file;
+ }
+ } else {
+ $dependencies{$embed_file} = 1;
+ if ($absolutepath) {
+ $mapping{$embed_file} = $absolutepath;
+ } else {
+ $mapping{$embed_file} = $embed_file;
+ }
+ }
}
}
+ my $dirptr = 16384;
foreach my $path (keys(%subdependencies)) {
- my %currsubfile;
- if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
- my @subdir_list = &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
- foreach my $line (@subdir_list) {
- my ($file_name,$rest) = split(/\&/,$line,2);
- $currsubfile{$file_name} = 1;
+ $currsubfile{$path} = {};
+ 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') {
+ foreach my $line (@{$sublistref}) {
+ my ($file_name,$rest) = split(/\&/,$line,2);
+ $currsubfile{$path}{$file_name} = 1;
+ }
}
- } elsif ($actionurl eq '/adm/upload') {
+ } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
if (opendir(my $dir,$url.'/'.$path)) {
my @subdir_list = grep(!/^\./,readdir($dir));
- map {$currsubfile{$_} = 1;} @subdir_list;
+ map {$currsubfile{$path}{$_} = 1;} @subdir_list;
+ }
+ } elsif (($actionurl eq '/adm/dependencies') ||
+ (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
+ ($args->{'context'} eq 'paste')) ||
+ ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
+ if ($env{'request.course.id'} ne '') {
+ my $dir;
+ if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
+ $dir = $fileloc;
+ } else {
+ ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
+ }
+ if ($dir ne '') {
+ my ($sublistref,$listerror) =
+ &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
+ if (ref($sublistref) eq 'ARRAY') {
+ foreach my $line (@{$sublistref}) {
+ my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
+ undef,$mtime)=split(/\&/,$line,12);
+ unless (($testdir&$dirptr) ||
+ ($file_name =~ /^\.\.?$/)) {
+ $currsubfile{$path}{$file_name} = [$size,$mtime];
+ }
+ }
+ }
+ }
}
}
foreach my $file (keys(%{$subdependencies{$path}})) {
- unless ($currsubfile{$file}) {
- $newfiles{$path.'/'.$file} = 1;
+ if (exists($currsubfile{$path}{$file})) {
+ my $item = $path.'/'.$file;
+ unless ($mapping{$item} eq $item) {
+ $pathchanges{$item} = 1;
+ }
+ $existing{$item} = 1;
+ $numexisting ++;
+ } else {
+ $newfiles{$path.'/'.$file} = 1;
+ }
+ }
+ if ($actionurl eq '/adm/dependencies') {
+ foreach my $path (keys(%currsubfile)) {
+ if (ref($currsubfile{$path}) eq 'HASH') {
+ foreach my $file (keys(%{$currsubfile{$path}})) {
+ unless ($subdependencies{$path}{$file}) {
+ next if (($rem ne '') &&
+ (($env{"httpref.$rem"."$path/$file"} ne '') ||
+ (ref($navmap) &&
+ (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
+ (($file =~ /^(.*\.s?html?)\.bak$/i) &&
+ ($navmap->getResourceByUrl($rem."$path/$1")))))));
+ $unused{$path.'/'.$file} = 1;
+ }
+ }
+ }
}
}
}
- my (@dir_list,%currfile);
- if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
- my @dir_list = &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
- foreach my $line (@dir_list) {
- my ($file_name,$rest) = split(/\&/,$line,2);
- $currfile{$file_name} = 1;
+ my %currfile;
+ if (($actionurl eq '/adm/portfolio') ||
+ ($actionurl eq '/adm/coursegrp_portfolio')) {
+ my ($dirlistref,$listerror) =
+ &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
+ if (ref($dirlistref) eq 'ARRAY') {
+ foreach my $line (@{$dirlistref}) {
+ my ($file_name,$rest) = split(/\&/,$line,2);
+ $currfile{$file_name} = 1;
+ }
}
- } elsif ($actionurl eq '/adm/upload') {
+ } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
if (opendir(my $dir,$url)) {
- @dir_list = grep(!/^\./,readdir($dir));
+ my @dir_list = grep(!/^\./,readdir($dir));
map {$currfile{$_} = 1;} @dir_list;
}
+ } elsif (($actionurl eq '/adm/dependencies') ||
+ (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
+ ($args->{'context'} eq 'paste')) ||
+ ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
+ if ($env{'request.course.id'} ne '') {
+ my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
+ if ($dir ne '') {
+ my ($dirlistref,$listerror) =
+ &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
+ if (ref($dirlistref) eq 'ARRAY') {
+ foreach my $line (@{$dirlistref}) {
+ my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
+ $size,undef,$mtime)=split(/\&/,$line,12);
+ unless (($testdir&$dirptr) ||
+ ($file_name =~ /^\.\.?$/)) {
+ $currfile{$file_name} = [$size,$mtime];
+ }
+ }
+ }
+ }
+ }
}
foreach my $file (keys(%dependencies)) {
- unless ($currfile{$file}) {
+ if (exists($currfile{$file})) {
+ unless ($mapping{$file} eq $file) {
+ $pathchanges{$file} = 1;
+ }
+ $existing{$file} = 1;
+ $numexisting ++;
+ } else {
$newfiles{$file} = 1;
}
}
+ foreach my $file (keys(%currfile)) {
+ unless (($file eq $filename) ||
+ ($file eq $filename.'.bak') ||
+ ($dependencies{$file})) {
+ if ($actionurl eq '/adm/dependencies') {
+ unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
+ next if (($rem ne '') &&
+ (($env{"httpref.$rem".$file} ne '') ||
+ (ref($navmap) &&
+ (($navmap->getResourceByUrl($rem.$file) ne '') ||
+ (($file =~ /^(.*\.s?html?)\.bak$/i) &&
+ ($navmap->getResourceByUrl($rem.$1)))))));
+ }
+ }
+ $unused{$file} = 1;
+ }
+ }
+ 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") &&
+ (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
+ $counter = scalar(keys(%existing));
+ $numpathchg = scalar(keys(%pathchanges));
+ return ($output,$counter,$numpathchg,\%existing,\%mapping);
+ }
foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
+ if ($actionurl eq '/adm/dependencies') {
+ next if ($embed_file =~ m{^\w+://});
+ }
$upload_output .= &start_data_table_row().
- ''.$embed_file.' ';
- if ($args->{'ignore_remote_references'}
- && $embed_file =~ m{^\w+://}) {
- $upload_output.=''.&mt("URL points to other server.").' ';
+ ' '.
+ ''.$embed_file.' ';
+ unless ($mapping{$embed_file} eq $embed_file) {
+ $upload_output .= ''.
+ &mt('changed from: [_1]',$mapping{$embed_file}).' ';
+ }
+ $upload_output .= ' ';
+ if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) {
+ $upload_output.=''.
+ ''.
+ &mt("URL points to web address").' ';
+ $numremref++;
} elsif ($args->{'error_on_invalid_names'}
&& $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
-
- $upload_output.=''.&mt("Invalid characters").' ';
-
+ $upload_output.=' '.
+ &mt('Invalid characters').' ';
+ $numinvalid++;
} else {
- $upload_output .='
-
- ';
- my $attrib = join(':',@{$$allfiles{$embed_file}});
- $upload_output .=
- "\n\t\t".
- ' ';
- if (exists($$codebase{$embed_file})) {
- $upload_output .=
- "\n\t\t".
- ' ';
- }
+ $upload_output .= ''.
+ &embedded_file_element('upload_embedded',$counter,
+ $embed_file,\%mapping,
+ $allfiles,$codebase,'upload');
+ $counter ++;
+ $numnew ++;
}
$upload_output .= ' '.&Apache::loncommon::end_data_table_row()."\n";
- $num++;
}
- if ($num) {
- $upload_output = ' ';
+ &end_data_table()."\n";
+ }
+ if ($modify_output) {
+ $modify_output = &start_data_table().
+ &start_data_table_header_row().
+ ''.&mt('File').' '.
+ ''.&mt('Size (KB)').' '.
+ ''.&mt('Modified').' '.
+ ''.&mt('Upload replacement?').' '.
+ &end_data_table_header_row().
+ $modify_output.
+ &end_data_table()."\n";
+ }
+ if ($delete_output) {
+ $delete_output = &start_data_table().
+ &start_data_table_header_row().
+ ''.&mt('File').' '.
+ ''.&mt('Size (KB)').' '.
+ ''.&mt('Modified').' '.
+ ''.&mt('Delete?').' '.
+ &end_data_table_header_row().
+ $delete_output.
+ &end_data_table()."\n";
+ }
+ my $applies = 0;
+ if ($numremref) {
+ $applies ++;
+ }
+ if ($numinvalid) {
+ $applies ++;
+ }
+ if ($numexisting) {
+ $applies ++;
+ }
+ if ($counter || $numunused) {
+ $output = ''."\n";
+ } elsif ($numpathchg) {
+ my %pathchange = ();
+ $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
+ if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+ $output .= ''.&mt('or').'
';
+ }
+ }
+ return ($output,$counter,$numpathchg);
+}
+
+sub embedded_file_element {
+ my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
+ return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
+ (ref($codebase) eq 'HASH'));
+ my $output;
+ if (($context eq 'upload_embedded') && ($type ne 'delete')) {
+ $output = ' '."\n";
+ }
+ $output .= ' ';
+ unless (($context eq 'upload_embedded') &&
+ ($mapping->{$embed_file} eq $embed_file)) {
+ $output .='
+ ';
+ }
+ my $attrib;
+ if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
+ $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
+ }
+ $output .=
+ "\n\t\t".
+ ' ';
+ if (exists($codebase->{$mapping->{$embed_file}})) {
+ $output .=
+ "\n\t\t".
+ ' ';
+ }
+ return $output;
+}
+
+sub get_dependency_details {
+ my ($currfile,$currsubfile,$embed_file) = @_;
+ my ($size,$mtime,$showsize,$showmtime);
+ if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
+ if ($embed_file =~ m{/}) {
+ my ($path,$fname) = split(/\//,$embed_file);
+ if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
+ ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
+ }
+ } else {
+ if (ref($currfile->{$embed_file}) eq 'ARRAY') {
+ ($size,$mtime) = @{$currfile->{$embed_file}};
+ }
+ }
+ $showsize = $size/1024.0;
+ $showsize = sprintf("%.1f",$showsize);
+ if ($mtime > 0) {
+ $showmtime = &Apache::lonlocal::locallocaltime($mtime);
+ }
+ }
+ return ($showsize,$showmtime);
+}
+
+sub ask_embedded_js {
+ return <<"END";
+
+
+END
}
sub upload_embedded {
my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
- $current_disk_usage) = @_;
- my $output;
+ $current_disk_usage,$hiddenstate,$actionurl) = @_;
+ my (%pathchange,$output,$modifyform,$footer,$returnflag);
for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
my $orig_uploaded_filename =
$env{'form.embedded_item_'.$i.'.filename'};
-
- $env{'form.embedded_orig_'.$i} =
- &unescape($env{'form.embedded_orig_'.$i});
+ foreach my $type ('orig','ref','attrib','codebase') {
+ if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
+ $env{'form.embedded_'.$type.'_'.$i} =
+ &unescape($env{'form.embedded_'.$type.'_'.$i});
+ }
+ }
my ($path,$fname) =
($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
# no path, whole string is fname
if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
-
- $path = $env{'form.currentpath'}.$path;
$fname = &Apache::lonnet::clean_filename($fname);
# See if there is anything left
next if ($fname eq '');
@@ -8445,7 +10143,8 @@ sub upload_embedded {
if ($group ne '') {
$port_path = "groups/$group/$port_path";
}
- ($state,$msg) = &check_for_upload($path,$fname,$group,'embedded_item_'.$i,
+ ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
+ $fname,$group,'embedded_item_'.$i,
$dir_root,$port_path,$disk_quota,
$current_disk_usage,$uname,$udom);
if ($state eq 'will_exceed_quota'
@@ -8463,28 +10162,30 @@ sub upload_embedded {
# Check if extension is valid
if (($fname =~ /\.(\w+)$/) &&
(&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
- $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1);
+ $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1).' ';
next;
} elsif (($fname =~ /\.(\w+)$/) &&
(!defined(&Apache::loncommon::fileembstyle($1)))) {
- $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1);
+ $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).' ';
next;
} elsif ($fname=~/\.(\d+)\.(\w+)$/) {
- $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2);
+ $output .= &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).' ';
next;
}
-
$env{'form.embedded_item_'.$i.'.filename'}=$fname;
+ my $subdir = $path;
+ $subdir =~ s{/+$}{};
if ($context eq 'portfolio') {
my $result;
if ($state eq 'existingfile') {
$result=
&Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
- $dirpath.$path,);
+ $dirpath.$env{'form.currentpath'}.$subdir);
} else {
$result=
&Apache::lonnet::userfileupload('embedded_item_'.$i,'',
- $dirpath.$path);
+ $dirpath.
+ $env{'form.currentpath'}.$subdir);
if ($result !~ m|^/uploaded/|) {
$output .= ''
.&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
@@ -8492,8 +10193,26 @@ sub upload_embedded {
.' ';
next;
} else {
- $output .= ''.&mt('Uploaded [_1]',''.
- $path.$fname.' ').'
';
+ $output .= &mt('Uploaded [_1]',''.
+ $path.$fname.' ').' ';
+ }
+ }
+ } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
+ my $extendedsubdir = $dirpath.'/'.$subdir;
+ $extendedsubdir =~ s{/+$}{};
+ my $result =
+ &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
+ if ($result !~ m|^/uploaded/|) {
+ $output .= ''
+ .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
+ ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
+ .' ';
+ next;
+ } else {
+ $output .= &mt('Uploaded [_1]',''.
+ $path.$fname.' ').' ';
+ if ($context eq 'syllabus') {
+ &Apache::lonnet::make_public_indefinitely($result);
}
}
} else {
@@ -8502,12 +10221,12 @@ sub upload_embedded {
my $fullpath = $dir_root.$dirpath.'/'.$path;
my $dest = $fullpath.$fname;
my $url = $url_root.$dirpath.'/'.$path.$fname;
- my @parts=split(/\//,$fullpath);
+ my @parts=split(/\//,"$dirpath/$path");
my $count;
my $filepath = $dir_root;
- for ($count=4;$count<=$#parts;$count++) {
- $filepath .= "/$parts[$count]";
- if ((-e $filepath)!=1) {
+ foreach my $subdir (@parts) {
+ $filepath .= "/$subdir";
+ if (!-e $filepath) {
mkdir($filepath,0770);
}
}
@@ -8515,30 +10234,294 @@ sub upload_embedded {
if (!open($fh,'>'.$dest)) {
&Apache::lonnet::logthis('Failed to create '.$dest);
$output .= ''.
- &mt('An error occurred while trying to upload [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
+ &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
+ $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
' ';
} else {
if (!print $fh $env{'form.embedded_item_'.$i}) {
&Apache::lonnet::logthis('Failed to write to '.$dest);
$output .= ''.
- &mt('An error occurred while writing the file [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
+ &mt('An error occurred while writing the file [_1] for embedded element [_2].',
+ $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
' ';
} else {
- if ($context eq 'testbank') {
- $output .= &mt('Embedded file uploaded successfully:').
- ' '.
- $orig_uploaded_filename.' ';
- } else {
- $output .= ''.
- &mt('View embedded file: [_1]',''.
- $orig_uploaded_filename.' ').' ';
+ $output .= &mt('Uploaded [_1]',''.
+ $url.' ').' ';
+ unless ($context eq 'testbank') {
+ $footer .= &mt('View embedded file: [_1]',
+ ''.$fname.' ').' ';
}
}
close($fh);
}
}
+ if ($env{'form.embedded_ref_'.$i}) {
+ $pathchange{$i} = 1;
+ }
+ }
+ if ($output) {
+ $output = ''.$output.'
';
+ }
+ $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
+ $returnflag = 'ok';
+ my $numpathchgs = scalar(keys(%pathchange));
+ if ($numpathchgs > 0) {
+ if ($context eq 'portfolio') {
+ $output .= ''.&mt('or').'
';
+ } elsif ($context eq 'testbank') {
+ $output .= ''.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
+ '',' ').'
';
+ $returnflag = 'modify_orightml';
+ }
+ }
+ return ($output.$footer,$returnflag,$numpathchgs);
+}
+
+sub modify_html_form {
+ my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
+ my $end = 0;
+ my $modifyform;
+ if ($context eq 'upload_embedded') {
+ return unless (ref($pathchange) eq 'HASH');
+ if ($env{'form.number_embedded_items'}) {
+ $end += $env{'form.number_embedded_items'};
+ }
+ if ($env{'form.number_pathchange_items'}) {
+ $end += $env{'form.number_pathchange_items'};
+ }
+ if ($end) {
+ for (my $i=0; $i<$end; $i++) {
+ if ($i < $env{'form.number_embedded_items'}) {
+ next unless($pathchange->{$i});
+ }
+ $modifyform .=
+ &start_data_table_row().
+ ' '.
+ ''.$env{'form.embedded_ref_'.$i}.
+ ' '.
+ ' '.
+ ' '.
+ ''.$env{'form.embedded_orig_'.$i}.
+ ' '.
+ &end_data_table_row();
+ }
+ }
+ } else {
+ $modifyform = $pathchgtable;
+ if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
+ $hiddenstate .= ' ';
+ } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
+ $hiddenstate .= ' ';
+ }
+ }
+ if ($modifyform) {
+ if ($actionurl eq '/adm/dependencies') {
+ $hiddenstate .= ' ';
+ }
+ return ''.&mt('Changes in content of HTML file required').' '."\n".
+ ''.&mt('Changes need to be made to the reference(s) used for one or more of the dependencies, if your HTML file is to work correctly:').'
'."\n".
+ ''.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').' '."\n".
+ ''.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').' '."\n".
+ ' '."\n".''.
+ &mt('LON-CAPA can make the required changes to your HTML file.').'
'."\n".
+ ''."\n";
+ }
+ return;
+}
+
+sub modify_html_refs {
+ my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
+ my $container;
+ if ($context eq 'portfolio') {
+ $container = $env{'form.container'};
+ } elsif ($context eq 'coursedoc') {
+ $container = $env{'form.primaryurl'};
+ } elsif ($context eq 'manage_dependencies') {
+ (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
+ $container = "/$container";
+ } elsif ($context eq 'syllabus') {
+ $container = $url;
+ } else {
+ $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
+ }
+ my (%allfiles,%codebase,$output,$content);
+ my @changes = &get_env_multiple('form.namechange');
+ unless ((@changes > 0) || ($context eq 'syllabus')) {
+ if (wantarray) {
+ return ('',0,0);
+ } else {
+ return;
+ }
+ }
+ if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
+ ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
+ unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
+ if (wantarray) {
+ return ('',0,0);
+ } else {
+ return;
+ }
+ }
+ $content = &Apache::lonnet::getfile($container);
+ if ($content eq '-1') {
+ if (wantarray) {
+ return ('',0,0);
+ } else {
+ return;
+ }
+ }
+ } else {
+ unless ($container =~ /^\Q$dir_root\E/) {
+ if (wantarray) {
+ return ('',0,0);
+ } else {
+ return;
+ }
+ }
+ if (open(my $fh,"<$container")) {
+ $content = join('', <$fh>);
+ close($fh);
+ } else {
+ if (wantarray) {
+ return ('',0,0);
+ } else {
+ return;
+ }
+ }
+ }
+ my ($count,$codebasecount) = (0,0);
+ my $mm = new File::MMagic;
+ my $mime_type = $mm->checktype_contents($content);
+ if ($mime_type eq 'text/html') {
+ my $parse_result =
+ &Apache::lonnet::extract_embedded_items($container,\%allfiles,
+ \%codebase,\$content);
+ if ($parse_result eq 'ok') {
+ foreach my $i (@changes) {
+ my $orig = &unescape($env{'form.embedded_orig_'.$i});
+ my $ref = &unescape($env{'form.embedded_ref_'.$i});
+ if ($allfiles{$ref}) {
+ my $newname = $orig;
+ my ($attrib_regexp,$codebase);
+ $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
+ if ($attrib_regexp =~ /:/) {
+ $attrib_regexp =~ s/\:/|/g;
+ }
+ if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
+ my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
+ $count += $numchg;
+ $allfiles{$newname} = $allfiles{$ref};
+ }
+ if ($env{'form.embedded_codebase_'.$i} ne '') {
+ $codebase = &unescape($env{'form.embedded_codebase_'.$i});
+ my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
+ $codebasecount ++;
+ }
+ }
+ }
+ my $skiprewrites;
+ if ($count || $codebasecount) {
+ my $saveresult;
+ if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
+ ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
+ my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
+ if ($url eq $container) {
+ my ($fname) = ($container =~ m{/([^/]+)$});
+ $output = ''.&mt('Updated [quant,_1,reference] in [_2].',
+ $count,''.
+ $fname.' ').'
';
+ } else {
+ $output = ''.
+ &mt('Error: update failed for: [_1].',
+ ''.
+ $container.' ').'
';
+ }
+ if ($context eq 'syllabus') {
+ unless ($saveresult eq 'ok') {
+ $skiprewrites = 1;
+ }
+ }
+ } else {
+ if (open(my $fh,">$container")) {
+ print $fh $content;
+ close($fh);
+ $output = ''.&mt('Updated [quant,_1,reference] in [_2].',
+ $count,''.
+ $container.' ').'
';
+ } else {
+ $output = ''.
+ &mt('Error: could not update [_1].',
+ ''.
+ $container.' ').'
';
+ }
+ }
+ }
+ if (($context eq 'syllabus') && (!$skiprewrites)) {
+ my ($actionurl,$state);
+ $actionurl = "/public/$udom/$uname/syllabus";
+ my ($ignore,$num,$numpathchanges,$existing,$mapping) =
+ &ask_for_embedded_content($actionurl,$state,\%allfiles,
+ \%codebase,
+ {'context' => 'rewrites',
+ 'ignore_remote_references' => 1,});
+ if (ref($mapping) eq 'HASH') {
+ my $rewrites = 0;
+ foreach my $key (keys(%{$mapping})) {
+ next if ($key =~ m{^https?://});
+ my $ref = $mapping->{$key};
+ my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
+ my $attrib;
+ if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
+ $attrib = join('|',@{$allfiles{$mapping->{$key}}});
+ }
+ if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
+ my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
+ $rewrites += $numchg;
+ }
+ }
+ if ($rewrites) {
+ my $saveresult;
+ my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
+ if ($url eq $container) {
+ my ($fname) = ($container =~ m{/([^/]+)$});
+ $output .= ''.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
+ $count,''.
+ $fname.' ').'
';
+ } else {
+ $output .= ''.
+ &mt('Error: could not update links in [_1].',
+ ''.
+ $container.' ').'
';
+
+ }
+ }
+ }
+ }
+ } else {
+ &logthis('Failed to parse '.$container.
+ ' to modify references: '.$parse_result);
+ }
+ }
+ if (wantarray) {
+ return ($output,$count,$codebasecount);
+ } else {
+ return $output;
}
- return $output;
}
sub check_for_existing {
@@ -8566,38 +10549,65 @@ sub check_for_upload {
&mt('Unable to upload [_1]. (size = [_2] bytes)',
''.$fname.' ',
$filesize).' '.
- &mt('Either the file you uploaded was empty, or your web browser was unable to read its contents.').' ';
+ &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').' '.
'';
return ('zero_bytes',$msg);
}
$filesize = $filesize/1000; #express in k (1024?)
my $getpropath = 1;
- my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,
- $getpropath);
+ my ($dirlistref,$listerror) =
+ &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
my $found_file = 0;
my $locked_file = 0;
- foreach my $line (@dir_list) {
- my ($file_name,$rest)=split(/\&/,$line,2);
- if ($file_name eq $fname){
- $file_name = $path.$file_name;
- if ($group ne '') {
- $file_name = $group.$file_name;
- }
- $found_file = 1;
- if (&Apache::lonnet::is_locked($file_name,$udom,$uname) eq 'true') {
- $locked_file = 1;
- } else {
- my @info = split(/\&/,$rest);
- my $currsize = $info[6]/1000;
- if ($currsize < $filesize) {
- my $extra = $filesize - $currsize;
- if (($current_disk_usage + $extra) > $disk_quota) {
- my $msg = ''.
- &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded if existing (smaller) file with same name (size = [_3] kilobytes) is replaced.',
- ''.$fname.' ',$filesize,$currsize).' '.
- ' '.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
- $disk_quota,$current_disk_usage);
- return ('will_exceed_quota',$msg);
+ my @lockers;
+ my $navmap;
+ if ($env{'request.course.id'}) {
+ $navmap = Apache::lonnavmaps::navmap->new();
+ }
+ if (ref($dirlistref) eq 'ARRAY') {
+ foreach my $line (@{$dirlistref}) {
+ my ($file_name,$rest)=split(/\&/,$line,2);
+ if ($file_name eq $fname){
+ $file_name = $path.$file_name;
+ if ($group ne '') {
+ $file_name = $group.$file_name;
+ }
+ $found_file = 1;
+ if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
+ foreach my $lock (@lockers) {
+ if (ref($lock) eq 'ARRAY') {
+ my ($symb,$crsid) = @{$lock};
+ if ($crsid eq $env{'request.course.id'}) {
+ if (ref($navmap)) {
+ my $res = $navmap->getBySymb($symb);
+ foreach my $part (@{$res->parts()}) {
+ my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
+ unless (($slot_status == $res->RESERVED) ||
+ ($slot_status == $res->RESERVED_LOCATION)) {
+ $locked_file = 1;
+ }
+ }
+ } else {
+ $locked_file = 1;
+ }
+ } else {
+ $locked_file = 1;
+ }
+ }
+ }
+ } else {
+ my @info = split(/\&/,$rest);
+ my $currsize = $info[6]/1000;
+ if ($currsize < $filesize) {
+ my $extra = $filesize - $currsize;
+ if (($current_disk_usage + $extra) > $disk_quota) {
+ my $msg = ''.
+ &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded if existing (smaller) file with same name (size = [_3] kilobytes) is replaced.',
+ ''.$fname.' ',$filesize,$currsize).' '.
+ ' '.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
+ $disk_quota,$current_disk_usage);
+ return ('will_exceed_quota',$msg);
+ }
}
}
}
@@ -8624,6 +10634,1321 @@ sub check_for_upload {
}
}
+sub check_for_traversal {
+ my ($path,$url,$toplevel) = @_;
+ my @parts=split(/\//,$path);
+ my $cleanpath;
+ my $fullpath = $url;
+ for (my $i=0;$i<@parts;$i++) {
+ next if ($parts[$i] eq '.');
+ if ($parts[$i] eq '..') {
+ $fullpath =~ s{([^/]+/)$}{};
+ } else {
+ $fullpath .= $parts[$i].'/';
+ }
+ }
+ if ($fullpath =~ /^\Q$url\E(.*)$/) {
+ $cleanpath = $1;
+ } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
+ my $curr_toprel = $1;
+ my @parts = split(/\//,$curr_toprel);
+ my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
+ my @urlparts = split(/\//,$url_toprel);
+ my $doubledots;
+ my $startdiff = -1;
+ for (my $i=0; $i<@urlparts; $i++) {
+ if ($startdiff == -1) {
+ unless ($urlparts[$i] eq $parts[$i]) {
+ $startdiff = $i;
+ $doubledots .= '../';
+ }
+ } else {
+ $doubledots .= '../';
+ }
+ }
+ if ($startdiff > -1) {
+ $cleanpath = $doubledots;
+ for (my $i=$startdiff; $i<@parts; $i++) {
+ $cleanpath .= $parts[$i].'/';
+ }
+ }
+ }
+ $cleanpath =~ s{(/)$}{};
+ return $cleanpath;
+}
+
+sub is_archive_file {
+ my ($mimetype) = @_;
+ if (($mimetype eq 'application/octet-stream') ||
+ ($mimetype eq 'application/x-stuffit') ||
+ ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
+ return 1;
+ }
+ return;
+}
+
+sub decompress_form {
+ my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
+ my %lt = &Apache::lonlocal::texthash (
+ this => 'This file is an archive file.',
+ camt => 'This file is a Camtasia archive file.',
+ itsc => 'Its contents are as follows:',
+ youm => 'You may wish to extract its contents.',
+ extr => 'Extract contents',
+ auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
+ proa => 'Process automatically?',
+ yes => 'Yes',
+ no => 'No',
+ fold => 'Title for folder containing movie',
+ movi => 'Title for page containing embedded movie',
+ );
+ my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
+ my ($is_camtasia,$topdir,%toplevel,@paths);
+ my $info = &list_archive_contents($fileloc,\@paths);
+ if (@paths) {
+ foreach my $path (@paths) {
+ $path =~ s{^/}{};
+ if ($path =~ m{^([^/]+)/$}) {
+ $topdir = $1;
+ }
+ if ($path =~ m{^([^/]+)/}) {
+ $toplevel{$1} = $path;
+ } else {
+ $toplevel{$path} = $path;
+ }
+ }
+ }
+ if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
+ my @camtasia = ("$topdir/","$topdir/index.html",
+ "$topdir/media/",
+ "$topdir/media/$topdir.mp4",
+ "$topdir/media/FirstFrame.png",
+ "$topdir/media/player.swf",
+ "$topdir/media/swfobject.js",
+ "$topdir/media/expressInstall.swf");
+ my @diffs = &compare_arrays(\@paths,\@camtasia);
+ if (@diffs == 0) {
+ $is_camtasia = 1;
+ }
+ }
+ my $output;
+ if ($is_camtasia) {
+ $output = <<"ENDCAM";
+
+$lt{'camt'}
+ENDCAM
+ } else {
+ $output = ''.$lt{'this'};
+ if ($info eq '') {
+ $output .= ' '.$lt{'youm'}.'
'."\n";
+ } else {
+ $output .= ' '.$lt{'itsc'}.''."\n".
+ '';
+ }
+ }
+ $output .= '
+$noextract
+END
+ return $output;
+}
+
+sub decompression_utility {
+ my ($program) = @_;
+ my @utilities = ('tar','gunzip','bunzip2','unzip');
+ my $location;
+ if (grep(/^\Q$program\E$/,@utilities)) {
+ foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
+ '/usr/sbin/') {
+ if (-x $dir.$program) {
+ $location = $dir.$program;
+ last;
+ }
+ }
+ }
+ return $location;
+}
+
+sub list_archive_contents {
+ my ($file,$pathsref) = @_;
+ my (@cmd,$output);
+ my $needsregexp;
+ if ($file =~ /\.zip$/) {
+ @cmd = (&decompression_utility('unzip'),"-l");
+ $needsregexp = 1;
+ } elsif (($file =~ m/\.tar\.gz$/) ||
+ ($file =~ /\.tgz$/)) {
+ @cmd = (&decompression_utility('tar'),"-ztf");
+ } elsif ($file =~ /\.tar\.bz2$/) {
+ @cmd = (&decompression_utility('tar'),"-jtf");
+ } elsif ($file =~ m|\.tar$|) {
+ @cmd = (&decompression_utility('tar'),"-tf");
+ }
+ if (@cmd) {
+ undef($!);
+ undef($@);
+ if (open(my $fh,"-|", @cmd, $file)) {
+ while (my $line = <$fh>) {
+ $output .= $line;
+ chomp($line);
+ my $item;
+ if ($needsregexp) {
+ ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/);
+ } else {
+ $item = $line;
+ }
+ if ($item ne '') {
+ unless (grep(/^\Q$item\E$/,@{$pathsref})) {
+ push(@{$pathsref},$item);
+ }
+ }
+ }
+ close($fh);
+ }
+ }
+ return $output;
+}
+
+sub decompress_uploaded_file {
+ my ($file,$dir) = @_;
+ &Apache::lonnet::appenv({'cgi.file' => $file});
+ &Apache::lonnet::appenv({'cgi.dir' => $dir});
+ my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
+ my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
+ my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
+ &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
+ my $decompressed = $env{'cgi.decompressed'};
+ &Apache::lonnet::delenv('cgi.file');
+ &Apache::lonnet::delenv('cgi.dir');
+ &Apache::lonnet::delenv('cgi.decompressed');
+ return ($decompressed,$result);
+}
+
+sub process_decompression {
+ my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
+ my ($dir,$error,$warning,$output);
+ if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/) {
+ $error = &mt('Filename not a supported archive file type.').
+ ' '.&mt('Filename should end with one of: [_1].',
+ '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
+ } else {
+ my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
+ if ($docuhome eq 'no_host') {
+ $error = &mt('Could not determine home server for course.');
+ } else {
+ my @ids=&Apache::lonnet::current_machine_ids();
+ my $currdir = "$dir_root/$destination";
+ if (grep(/^\Q$docuhome\E$/,@ids)) {
+ $dir = &LONCAPA::propath($docudom,$docuname).
+ "$dir_root/$destination";
+ } else {
+ $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
+ "$dir_root/$docudom/$docuname/$destination";
+ unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
+ $error = &mt('Archive file not found.');
+ }
+ }
+ my (@to_overwrite,@to_skip);
+ if ($env{'form.archive_overwrite_total'} > 0) {
+ my $total = $env{'form.archive_overwrite_total'};
+ for (my $i=0; $i<$total; $i++) {
+ if ($env{'form.archive_overwrite_'.$i} == 1) {
+ push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
+ } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
+ push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
+ }
+ }
+ }
+ my $numskip = scalar(@to_skip);
+ 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 > 0) {
+ my $tempdir = time.'_'.$$.int(rand(10000));
+ mkdir("$dir/$tempdir",0755);
+ 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");
+ }
+ }
+ }
+ system("mv $dir/$tempdir/* $dir");
+ rmdir("$dir/$tempdir");
+ } else {
+ ($decompressed,$display) =
+ &decompress_uploaded_file($file,$dir);
+ }
+ if ($decompressed eq 'ok') {
+ $output = ''.
+ &mt('Files extracted successfully from archive.').
+ '
'."\n";
+ my ($warning,$result,@contents);
+ my ($newdirlistref,$newlisterror) =
+ &Apache::lonnet::dirlist($currdir,$docudom,
+ $docuname,1);
+ my (%is_dir,%changes,@newitems);
+ my $dirptr = 16384;
+ if (ref($newdirlistref) eq 'ARRAY') {
+ foreach my $dir_line (@{$newdirlistref}) {
+ my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
+ unless (($item =~ /^\.+$/) || ($item eq $file) ||
+ ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) {
+ push(@newitems,$item);
+ if ($dirptr&$testdir) {
+ $is_dir{$item} = 1;
+ }
+ $changes{$item} = 1;
+ }
+ }
+ }
+ if (keys(%changes) > 0) {
+ foreach my $item (sort(@newitems)) {
+ if ($changes{$item}) {
+ push(@contents,$item);
+ }
+ }
+ }
+ if (@contents > 0) {
+ my $wantform;
+ unless ($env{'form.autoextract_camtasia'}) {
+ $wantform = 1;
+ }
+ my (%children,%parent,%dirorder,%titles);
+ my ($count,$datatable) = &get_extracted($docudom,$docuname,
+ $currdir,\%is_dir,
+ \%children,\%parent,
+ \@contents,\%dirorder,
+ \%titles,$wantform);
+ if ($datatable ne '') {
+ $output .= &archive_options_form('decompressed',$datatable,
+ $count,$hiddenelem);
+ my $startcount = 6;
+ $output .= &archive_javascript($startcount,$count,
+ \%titles,\%children);
+ }
+ if ($env{'form.autoextract_camtasia'}) {
+ my %displayed;
+ my $total = 1;
+ $env{'form.archive_directory'} = [];
+ foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
+ my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
+ $path =~ s{/$}{};
+ my $item;
+ if ($path ne '') {
+ $item = "$path/$titles{$i}";
+ } else {
+ $item = $titles{$i};
+ }
+ $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
+ if ($item eq $contents[0]) {
+ push(@{$env{'form.archive_directory'}},$i);
+ $env{'form.archive_'.$i} = 'display';
+ $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
+ $displayed{'folder'} = $i;
+ } elsif ($item eq "$contents[0]/index.html") {
+ $env{'form.archive_'.$i} = 'display';
+ $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
+ $displayed{'web'} = $i;
+ } else {
+ if ($item eq "$contents[0]/media") {
+ push(@{$env{'form.archive_directory'}},$i);
+ }
+ $env{'form.archive_'.$i} = 'dependency';
+ }
+ $total ++;
+ }
+ for (my $i=1; $i<$total; $i++) {
+ next if ($i == $displayed{'web'});
+ next if ($i == $displayed{'folder'});
+ $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
+ }
+ $env{'form.phase'} = 'decompress_cleanup';
+ $env{'form.archivedelete'} = 1;
+ $env{'form.archive_count'} = $total-1;
+ $output .=
+ &process_extracted_files('coursedocs',$docudom,
+ $docuname,$destination,
+ $dir_root,$hiddenelem);
+ }
+ } else {
+ $warning = &mt('No new items extracted from archive file.');
+ }
+ } else {
+ $output = $display;
+ $error = &mt('An error occurred during extraction from the archive file.');
+ }
+ }
+ }
+ }
+ if ($error) {
+ $output .= ''.&mt('Not extracted.').' '.
+ $error.'
'."\n";
+ }
+ if ($warning) {
+ $output .= ''.$warning.'
'."\n";
+ }
+ return $output;
+}
+
+sub get_extracted {
+ my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
+ $titles,$wantform) = @_;
+ my $count = 0;
+ my $depth = 0;
+ my $datatable;
+ my @hierarchy;
+ return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
+ (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
+ (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
+ foreach my $item (@{$contents}) {
+ $count ++;
+ @{$dirorder->{$count}} = @hierarchy;
+ $titles->{$count} = $item;
+ &archive_hierarchy($depth,$count,$parent,$children);
+ if ($wantform) {
+ $datatable .= &archive_row($is_dir->{$item},$item,
+ $currdir,$depth,$count);
+ }
+ if ($is_dir->{$item}) {
+ $depth ++;
+ push(@hierarchy,$count);
+ $parent->{$depth} = $count;
+ $datatable .=
+ &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
+ \$depth,\$count,\@hierarchy,$dirorder,
+ $children,$parent,$titles,$wantform);
+ $depth --;
+ pop(@hierarchy);
+ }
+ }
+ return ($count,$datatable);
+}
+
+sub recurse_extracted_archive {
+ my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
+ $children,$parent,$titles,$wantform) = @_;
+ my $result='';
+ unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
+ (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
+ (ref($dirorder) eq 'HASH')) {
+ return $result;
+ }
+ my $dirptr = 16384;
+ my ($newdirlistref,$newlisterror) =
+ &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
+ if (ref($newdirlistref) eq 'ARRAY') {
+ foreach my $dir_line (@{$newdirlistref}) {
+ my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
+ unless ($item =~ /^\.+$/) {
+ $$count ++;
+ @{$dirorder->{$$count}} = @{$hierarchy};
+ $titles->{$$count} = $item;
+ &archive_hierarchy($$depth,$$count,$parent,$children);
+
+ my $is_dir;
+ if ($dirptr&$testdir) {
+ $is_dir = 1;
+ }
+ if ($wantform) {
+ $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
+ }
+ if ($is_dir) {
+ $$depth ++;
+ push(@{$hierarchy},$$count);
+ $parent->{$$depth} = $$count;
+ $result .=
+ &recurse_extracted_archive("$currdir/$item",$docudom,
+ $docuname,$depth,$count,
+ $hierarchy,$dirorder,$children,
+ $parent,$titles,$wantform);
+ $$depth --;
+ pop(@{$hierarchy});
+ }
+ }
+ }
+ }
+ return $result;
+}
+
+sub archive_hierarchy {
+ my ($depth,$count,$parent,$children) =@_;
+ if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
+ if (exists($parent->{$depth})) {
+ $children->{$parent->{$depth}} .= $count.':';
+ }
+ }
+ return;
+}
+
+sub archive_row {
+ my ($is_dir,$item,$currdir,$depth,$count) = @_;
+ my ($name) = ($item =~ m{([^/]+)$});
+ my %choices = &Apache::lonlocal::texthash (
+ 'display' => 'Add as file',
+ 'dependency' => 'Include as dependency',
+ 'discard' => 'Discard',
+ );
+ if ($is_dir) {
+ $choices{'display'} = &mt('Add as folder');
+ }
+ my $output = &start_data_table_row().''.$count.' '."\n";
+ my $offset = 0;
+ foreach my $action ('display','dependency','discard') {
+ $offset ++;
+ if ($action ne 'display') {
+ $offset ++;
+ }
+ $output .= ''.
+ ' ';
+ if ($action eq 'dependency') {
+ $output .= ''."\n".
+ &mt('Used by:').' '."\n".
+ ' '."\n".
+ ' '."\n".
+ '
';
+ } elsif ($action eq 'display') {
+ $output .= ''."\n".
+ &mt('Title:').' '."\n".
+ '
';
+ }
+ $output .= ' ';
+ }
+ $output .= ' &').'" />'.(' ' x 2);
+ for (my $i=0; $i<$depth; $i++) {
+ $output .= (' ' x2)."\n";
+ }
+ if ($is_dir) {
+ $output .= ' '."\n".
+ ' '."\n";
+ } else {
+ $output .= ' '."\n";
+ }
+ $output .= ' '.$name.' '."\n".
+ &end_data_table_row();
+ return $output;
+}
+
+sub archive_options_form {
+ my ($form,$display,$count,$hiddenelem) = @_;
+ my %lt = &Apache::lonlocal::texthash(
+ perm => 'Permanently remove archive file?',
+ hows => 'How should each extracted item be incorporated in the course?',
+ cont => 'Content actions for all',
+ addf => 'Add as folder/file',
+ incd => 'Include as dependency for a displayed file',
+ disc => 'Discard',
+ no => 'No',
+ yes => 'Yes',
+ save => 'Save',
+ );
+ my $output = <<"END";
+';
+}
+
+sub archive_javascript {
+ my ($startcount,$numitems,$titles,$children) = @_;
+ return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
+ my $maintitle = $env{'form.comment'};
+ my $scripttag = <
+// 0) {
+ var startelement = $startcount + ((count-1) * 7);
+ for (var j=1; j<6; j++) {
+ if ((j != 2) && (j != 4)) {
+ var item = startelement + j;
+ if (form.elements[item].type == 'radio') {
+ if (form.elements[item].checked) {
+ containerCheck(form,count,j);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+numitems = $numitems
+var titles = new Array(numitems);
+var parents = new Array(numitems);
+for (var i=0; i $b } (keys(%{$children}))) {
+ my @contents = split(/:/,$children->{$container});
+ for (my $i=0; $i<@contents; $i ++) {
+ $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
+ }
+ }
+
+ foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
+ $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
+ }
+
+ $scripttag .= < 0) {
+ dependencyCheck(form,count,offset);
+ var item = (offset+$startcount)+7*(count-1);
+ form.elements[item].checked = true;
+ if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
+ if (parents[count].length > 0) {
+ for (var j=0; j 0) {
+ var chosen = (offset+$startcount)+7*(count-1);
+ var depitem = $startcount + ((count-1) * 7) + 4;
+ var currtype = form.elements[depitem].type;
+ if (form.elements[chosen].value == 'dependency') {
+ document.getElementById('arc_depon_'+count).style.display='block';
+ form.elements[depitem].options.length = 0;
+ form.elements[depitem].options[0] = new Option('Select','',true,true);
+ for (var i=1; i<=numitems; i++) {
+ if (i == count) {
+ continue;
+ }
+ var startelement = $startcount + (i-1) * 7;
+ for (var j=1; j<6; j++) {
+ if ((j != 2) && (j!= 4)) {
+ var item = startelement + j;
+ if (form.elements[item].type == 'radio') {
+ if (form.elements[item].checked) {
+ if (form.elements[item].value == 'display') {
+ var n = form.elements[depitem].options.length;
+ form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ document.getElementById('arc_depon_'+count).style.display='none';
+ form.elements[depitem].options.length = 0;
+ form.elements[depitem].options[0] = new Option('Select','',true,true);
+ }
+ titleCheck(form,count,offset);
+ }
+}
+
+function propagateSelect(form,count,offset) {
+ if (count > 0) {
+ var item = (1+offset+$startcount)+7*(count-1);
+ var picked = form.elements[item].options[form.elements[item].selectedIndex].value;
+ if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
+ if (parents[count].length > 0) {
+ for (var j=0; j 0) {
+ var item = (offset+$startcount)+7*(count-1);
+ if (form.elements[item].type == 'radio') {
+ if (form.elements[item].value == 'dependency') {
+ if (form.elements[item+1].type == 'select-one') {
+ for (var i=0; i 0) {
+ for (var j=0; j 0) {
+ var chosen = (offset+$startcount)+7*(count-1);
+ var depitem = $startcount + ((count-1) * 7) + 2;
+ var currtype = form.elements[depitem].type;
+ if (form.elements[chosen].value == 'display') {
+ document.getElementById('arc_title_'+count).style.display='block';
+ if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
+ document.getElementById('archive_title_'+count).value=maintitle;
+ }
+ } else {
+ document.getElementById('arc_title_'+count).style.display='none';
+ if (currtype == 'text') {
+ document.getElementById('archive_title_'+count).value='';
+ }
+ }
+ }
+ return;
+}
+
+// ]]>
+
+END
+ return $scripttag;
+}
+
+sub process_extracted_files {
+ my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
+ my $numitems = $env{'form.archive_count'};
+ return unless ($numitems);
+ my @ids=&Apache::lonnet::current_machine_ids();
+ my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
+ %folders,%containers,%mapinner,%prompttofetch);
+ my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
+ if (grep(/^\Q$docuhome\E$/,@ids)) {
+ $prefix = &LONCAPA::propath($docudom,$docuname);
+ $pathtocheck = "$dir_root/$destination";
+ $dir = $dir_root;
+ $ishome = 1;
+ } else {
+ $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
+ $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
+ $dir = "$dir_root/$docudom/$docuname";
+ }
+ my $currdir = "$dir_root/$destination";
+ (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
+ if ($env{'form.folderpath'}) {
+ my @items = split('&',$env{'form.folderpath'});
+ $folders{'0'} = $items[-2];
+ if ($env{'form.folderpath'} =~ /\:1$/) {
+ $containers{'0'}='page';
+ } else {
+ $containers{'0'}='sequence';
+ }
+ }
+ my @archdirs = &get_env_multiple('form.archive_directory');
+ if ($numitems) {
+ for (my $i=1; $i<=$numitems; $i++) {
+ my $path = $env{'form.archive_content_'.$i};
+ if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
+ my $item = $1;
+ $toplevelitems{$item} = $i;
+ if (grep(/^\Q$i\E$/,@archdirs)) {
+ $is_dir{$item} = 1;
+ }
+ }
+ }
+ }
+ my ($output,%children,%parent,%titles,%dirorder,$result);
+ if (keys(%toplevelitems) > 0) {
+ my @contents = sort(keys(%toplevelitems));
+ (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
+ \%parent,\@contents,\%dirorder,\%titles);
+ }
+ my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
+ if ($numitems) {
+ for (my $i=1; $i<=$numitems; $i++) {
+ next if ($env{'form.archive_'.$i} eq 'dependency');
+ my $path = $env{'form.archive_content_'.$i};
+ if ($path =~ /^\Q$pathtocheck\E/) {
+ if ($env{'form.archive_'.$i} eq 'discard') {
+ if ($prefix ne '' && $path ne '') {
+ if (-e $prefix.$path) {
+ if ((@archdirs > 0) &&
+ (grep(/^\Q$i\E$/,@archdirs))) {
+ $todeletedir{$prefix.$path} = 1;
+ } else {
+ $todelete{$prefix.$path} = 1;
+ }
+ }
+ }
+ } elsif ($env{'form.archive_'.$i} eq 'display') {
+ my ($docstitle,$title,$url,$outer);
+ ($title) = ($path =~ m{/([^/]+)$});
+ $docstitle = $env{'form.archive_title_'.$i};
+ if ($docstitle eq '') {
+ $docstitle = $title;
+ }
+ $outer = 0;
+ if (ref($dirorder{$i}) eq 'ARRAY') {
+ if (@{$dirorder{$i}} > 0) {
+ foreach my $item (reverse(@{$dirorder{$i}})) {
+ if ($env{'form.archive_'.$item} eq 'display') {
+ $outer = $item;
+ last;
+ }
+ }
+ }
+ }
+ my ($errtext,$fatal) =
+ &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
+ '/'.$folders{$outer}.'.'.
+ $containers{$outer});
+ next if ($fatal);
+ if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
+ if ($context eq 'coursedocs') {
+ $mapinner{$i} = time;
+ $folders{$i} = 'default_'.$mapinner{$i};
+ $containers{$i} = 'sequence';
+ my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
+ $folders{$i}.'.'.$containers{$i};
+ my $newidx = &LONCAPA::map::getresidx();
+ $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);
+ $newseqid{$i} = $newidx;
+ unless ($errtext) {
+ $result .= ''.&mt('Folder: [_1] added to course',$docstitle).' '."\n";
+ }
+ }
+ } else {
+ if ($context eq 'coursedocs') {
+ my $newidx=&LONCAPA::map::getresidx();
+ my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
+ $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
+ $title;
+ 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',$docstitle).' '."\n";
+ }
+ }
+ }
+ }
+ }
+ } else {
+ $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).' ';
+ }
+ }
+ for (my $i=1; $i<=$numitems; $i++) {
+ next unless ($env{'form.archive_'.$i} eq 'dependency');
+ my $path = $env{'form.archive_content_'.$i};
+ if ($path =~ /^\Q$pathtocheck\E/) {
+ my ($title) = ($path =~ m{/([^/]+)$});
+ $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
+ if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
+ if (ref($dirorder{$i}) eq 'ARRAY') {
+ my ($itemidx,$fullpath,$relpath);
+ if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
+ my $container = $dirorder{$referrer{$i}}->[-1];
+ for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
+ if ($dirorder{$i}->[$j] eq $container) {
+ $itemidx = $j;
+ }
+ }
+ }
+ if ($itemidx eq '') {
+ $itemidx = 0;
+ }
+ if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
+ if ($mapinner{$referrer{$i}}) {
+ $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
+ for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
+ if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
+ unless (defined($newseqid{$dirorder{$i}->[$j]})) {
+ $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
+ $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
+ if (!-e $fullpath) {
+ mkdir($fullpath,0755);
+ }
+ }
+ } else {
+ last;
+ }
+ }
+ }
+ } elsif ($newdest{$referrer{$i}}) {
+ $fullpath = $newdest{$referrer{$i}};
+ for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
+ if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
+ $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
+ last;
+ } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
+ unless (defined($newseqid{$dirorder{$i}->[$j]})) {
+ $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
+ $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
+ if (!-e $fullpath) {
+ mkdir($fullpath,0755);
+ }
+ }
+ } else {
+ last;
+ }
+ }
+ }
+ if ($fullpath ne '') {
+ if (-e "$prefix$path") {
+ system("mv $prefix$path $fullpath/$title");
+ }
+ if (-e "$fullpath/$title") {
+ my $showpath;
+ if ($relpath ne '') {
+ $showpath = "$relpath/$title";
+ } else {
+ $showpath = "/$title";
+ }
+ $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.',
+ $path,$env{'form.archive_content_'.$referrer{$i}}).' ';
+ }
+ } else {
+ $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).' ';
+ }
+ }
+ if (keys(%todelete)) {
+ foreach my $key (keys(%todelete)) {
+ unlink($key);
+ }
+ }
+ if (keys(%todeletedir)) {
+ foreach my $key (keys(%todeletedir)) {
+ rmdir($key);
+ }
+ }
+ foreach my $dir (sort(keys(%is_dir))) {
+ if (($pathtocheck ne '') && ($dir ne '')) {
+ &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
+ }
+ }
+ if ($result ne '') {
+ $output .= ''."\n".
+ $result."\n".
+ ' ';
+ }
+ unless ($ishome) {
+ my $replicationfail;
+ foreach my $item (keys(%prompttofetch)) {
+ my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
+ unless ($fetchresult eq 'ok') {
+ $replicationfail .= ''.$item.' '."\n";
+ }
+ }
+ if ($replicationfail) {
+ $output .= ''.
+ &mt('Course home server failed to retrieve:').'
'.
+ $replicationfail.
+ ' ';
+ }
+ }
+ } else {
+ $warning = &mt('No items found in archive.');
+ }
+ if ($error) {
+ $output .= ''.&mt('Not extracted.').' '.
+ $error.'
'."\n";
+ }
+ if ($warning) {
+ $output .= ''.$warning.'
'."\n";
+ }
+ return $output;
+}
+
+sub cleanup_empty_dirs {
+ my ($path) = @_;
+ if (($path ne '') && (-d $path)) {
+ if (opendir(my $dirh,$path)) {
+ my @dircontents = grep(!/^\./,readdir($dirh));
+ my $numitems = 0;
+ foreach my $item (@dircontents) {
+ if (-d "$path/$item") {
+ &cleanup_empty_dirs("$path/$item");
+ if (-e "$path/$item") {
+ $numitems ++;
+ }
+ } else {
+ $numitems ++;
+ }
+ }
+ if ($numitems == 0) {
+ rmdir($path);
+ }
+ closedir($dirh);
+ }
+ }
+ return;
+}
+
+=pod
+
+=item &get_folder_hierarchy()
+
+Provides hierarchy of names of folders/sub-folders containing the current
+item,
+
+Inputs: 3
+ - $navmap - navmaps object
+
+ - $map - url for map (either the trigger itself, or map containing
+ the resource, which is the trigger).
+
+ - $showitem - 1 => show title for map itself; 0 => do not show.
+
+Outputs: 1 @pathitems - array of folder/subfolder names.
+
+=cut
+
+sub get_folder_hierarchy {
+ my ($navmap,$map,$showitem) = @_;
+ my @pathitems;
+ if (ref($navmap)) {
+ my $mapres = $navmap->getResourceByUrl($map);
+ if (ref($mapres)) {
+ my $pcslist = $mapres->map_hierarchy();
+ if ($pcslist ne '') {
+ my @pcs = split(/,/,$pcslist);
+ foreach my $pc (@pcs) {
+ if ($pc == 1) {
+ push(@pathitems,&mt('Main Content'));
+ } else {
+ my $res = $navmap->getByMapPc($pc);
+ if (ref($res)) {
+ my $title = $res->compTitle();
+ $title =~ s/\W+/_/g;
+ if ($title ne '') {
+ push(@pathitems,$title);
+ }
+ }
+ }
+ }
+ }
+ if ($showitem) {
+ if ($mapres->{ID} eq '0.0') {
+ push(@pathitems,&mt('Main Content'));
+ } else {
+ my $maptitle = $mapres->compTitle();
+ $maptitle =~ s/\W+/_/g;
+ if ($maptitle ne '') {
+ push(@pathitems,$maptitle);
+ }
+ }
+ }
+ }
+ }
+ return @pathitems;
+}
+
+=pod
+
+=item * &get_turnedin_filepath()
+
+Determines path in a user's portfolio file for storage of files uploaded
+to a specific essayresponse or dropbox item.
+
+Inputs: 3 required + 1 optional.
+$symb is symb for resource, $uname and $udom are for current user (required).
+$caller is optional (can be "submission", if routine is called when storing
+an upoaded file when "Submit Answer" button was pressed).
+
+Returns array containing $path and $multiresp.
+$path is path in portfolio. $multiresp is 1 if this resource contains more
+than one file upload item. Callers of routine should append partid as a
+subdirectory to $path in cases where $multiresp is 1.
+
+Called by: homework/essayresponse.pm and homework/structuretags.pm
+
+=cut
+
+sub get_turnedin_filepath {
+ my ($symb,$uname,$udom,$caller) = @_;
+ my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
+ my $turnindir;
+ my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
+ $turnindir = $userhash{'turnindir'};
+ my ($path,$multiresp);
+ if ($turnindir eq '') {
+ if ($caller eq 'submission') {
+ $turnindir = &mt('turned in');
+ $turnindir =~ s/\W+/_/g;
+ my %newhash = (
+ 'turnindir' => $turnindir,
+ );
+ &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
+ }
+ }
+ if ($turnindir ne '') {
+ $path = '/'.$turnindir.'/';
+ my ($multipart,$turnin,@pathitems);
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (defined($navmap)) {
+ my $mapres = $navmap->getResourceByUrl($map);
+ if (ref($mapres)) {
+ my $pcslist = $mapres->map_hierarchy();
+ if ($pcslist ne '') {
+ foreach my $pc (split(/,/,$pcslist)) {
+ my $res = $navmap->getByMapPc($pc);
+ if (ref($res)) {
+ my $title = $res->compTitle();
+ $title =~ s/\W+/_/g;
+ if ($title ne '') {
+ push(@pathitems,$title);
+ }
+ }
+ }
+ }
+ my $maptitle = $mapres->compTitle();
+ $maptitle =~ s/\W+/_/g;
+ if ($maptitle ne '') {
+ push(@pathitems,$maptitle);
+ }
+ unless ($env{'request.state'} eq 'construct') {
+ my $res = $navmap->getBySymb($symb);
+ if (ref($res)) {
+ my $partlist = $res->parts();
+ my $totaluploads = 0;
+ if (ref($partlist) eq 'ARRAY') {
+ foreach my $part (@{$partlist}) {
+ my @types = $res->responseType($part);
+ my @ids = $res->responseIds($part);
+ for (my $i=0; $i < scalar(@ids); $i++) {
+ if ($types[$i] eq 'essay') {
+ my $partid = $part.'_'.$ids[$i];
+ if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
+ $totaluploads ++;
+ }
+ }
+ }
+ }
+ if ($totaluploads > 1) {
+ $multiresp = 1;
+ }
+ }
+ }
+ }
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ my $restitle=&Apache::lonnet::gettitle($symb);
+ $restitle =~ s/\W+/_/g;
+ if ($restitle eq '') {
+ $restitle = ($resurl =~ m{/[^/]+$});
+ if ($restitle eq '') {
+ $restitle = time;
+ }
+ }
+ push(@pathitems,$restitle);
+ $path .= join('/',@pathitems);
+ }
+ return ($path,$multiresp);
+}
=pod
@@ -9991,7 +13316,7 @@ sub commit_customrole {
}
sub commit_standardrole {
- my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
+ my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
my ($output,$logmsg,$linefeed);
if ($context eq 'auto') {
$linefeed = "\n";
@@ -10000,7 +13325,7 @@ sub commit_standardrole {
}
if ($three eq 'st') {
my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
- $one,$two,$sec,$context);
+ $one,$two,$sec,$context,$credits);
if (($result =~ /^error/) || ($result eq 'not_in_class') ||
($result eq 'unknown_course') || ($result eq 'refused')) {
$output = $logmsg.' '.&mt('Error: ').$result."\n";
@@ -10031,7 +13356,8 @@ sub commit_standardrole {
}
sub commit_studentrole {
- my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context) = @_;
+ my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
+ $credits) = @_;
my ($result,$linefeed,$oldsecurl,$newsecurl);
if ($context eq 'auto') {
$linefeed = "\n";
@@ -10078,7 +13404,11 @@ sub commit_studentrole {
}
}
if (($expire_role_result eq 'ok') || ($secchange == 0)) {
- $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context);
+ $modify_section_result =
+ &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
+ undef,undef,undef,$sec,
+ $end,$start,'','',$cid,
+ '',$context,$credits);
if ($modify_section_result =~ /^ok/) {
if ($secchange == 1) {
if ($sec eq '') {
@@ -10109,7 +13439,7 @@ sub commit_studentrole {
$result = $modify_section_result;
} elsif ($secchange == 1) {
if ($oldsec eq '') {
- $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_3] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
+ $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_2] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
} else {
$$logmsg .= &mt('Error when attempting to expire existing role for [_1] in section [_2] in course [_3] -error: ',$uname,$oldsec,$cid).' '.$expire_role_result.$linefeed;
}
@@ -10135,6 +13465,26 @@ sub commit_studentrole {
return $result;
}
+sub show_role_extent {
+ my ($scope,$context,$role) = @_;
+ $scope =~ s{^/}{};
+ my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
+ push(@courseroles,'co');
+ my @authorroles = &Apache::lonuserutils::roles_by_context('author');
+ if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
+ $scope =~ s{/}{_};
+ return ''.$env{'course.'.$scope.'.description'}.' ';
+ } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
+ my ($audom,$auname) = split(/\//,$scope);
+ return &mt('[_1] Author Space',''.
+ &Apache::loncommon::plainname($auname,$audom).' ');
+ } else {
+ $scope =~ s{/$}{};
+ return &mt('Domain: [_1]',''.
+ &Apache::lonnet::domain($scope,'description').' ');
+ }
+}
+
############################################################
############################################################
@@ -10298,6 +13648,7 @@ sub construct_course {
'pch.users.denied',
'plc.users.denied',
'hidefromcat',
+ 'checkforpriv',
'categories'],
$$crsudom,$$crsunum);
}
@@ -10327,6 +13678,9 @@ sub construct_course {
} else {
$cenv{'internal.courseowner'} = $args->{'curruser'};
}
+ if ($args->{'defaultcredits'}) {
+ $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
+ }
my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
if ($args->{'crssections'}) {
$cenv{'internal.sectionnums'} = '';
@@ -10351,6 +13705,11 @@ sub construct_course {
# do not hide course coordinator from staff listing,
# even if privileged
$cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
+# add course coordinator's domain to domains to check for privileged users
+# if different to course domain
+ if ($$crsudom ne $args->{'ccdomain'}) {
+ $cenv{'checkforpriv'} = $args->{'ccdomain'};
+ }
# add crosslistings
if ($args->{'crsxlist'}) {
$cenv{'internal.crosslistings'}='';
@@ -10663,7 +14022,7 @@ sub init_user_environment {
# See if old ID present, if so, remove
- my ($filename,$cookie,$userroles);
+ my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
my $now=time;
if ($public) {
@@ -10701,7 +14060,8 @@ sub init_user_environment {
# Initialize roles
- $userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
+ ($userroles,$firstaccenv,$timerintenv) =
+ &Apache::lonnet::rolesinit($domain,$username,$authhost);
}
# ------------------------------------ Check browser type and MathML capability
@@ -10757,9 +14117,12 @@ sub init_user_environment {
}
my %is_adv = ( is_adv => $env{'user.adv'} );
- my %domdef = &Apache::lonnet::get_domain_defaults($domain);
+ my %domdef;
+ unless ($domain eq 'public') {
+ %domdef = &Apache::lonnet::get_domain_defaults($domain);
+ }
- foreach my $tool ('aboutme','blog','portfolio') {
+ foreach my $tool ('aboutme','blog','webdav','portfolio') {
$userenv{'availabletools.'.$tool} =
&Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
undef,\%userenv,\%domdef,\%is_adv);
@@ -10772,13 +14135,33 @@ sub init_user_environment {
\%userenv,\%domdef,\%is_adv);
}
+ $userenv{'canrequest.author'} =
+ &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
+ 'reload','requestauthor',
+ \%userenv,\%domdef,\%is_adv);
+ my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
+ $domain,$username);
+ my $reqstatus = $reqauthor{'author_status'};
+ if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {
+ if (ref($reqauthor{'author'}) eq 'HASH') {
+ $userenv{'requestauthorqueued'} = $reqstatus.':'.
+ $reqauthor{'author'}{'timestamp'};
+ }
+ }
+
$env{'user.environment'} = "$lonids/$cookie.id";
-
+
if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
&GDBM_WRCREAT(),0640)) {
&_add_to_env(\%disk_env,\%initial_env);
&_add_to_env(\%disk_env,\%userenv,'environment.');
&_add_to_env(\%disk_env,$userroles);
+ if (ref($firstaccenv) eq 'HASH') {
+ &_add_to_env(\%disk_env,$firstaccenv);
+ }
+ if (ref($timerintenv) eq 'HASH') {
+ &_add_to_env(\%disk_env,$timerintenv);
+ }
if (ref($args->{'extra_env'})) {
&_add_to_env(\%disk_env,$args->{'extra_env'});
}
@@ -10814,7 +14197,9 @@ sub get_symb {
my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
if ($symb eq '') {
if (!$silent) {
- $request->print("Unable to handle ambiguous references:$url:.");
+ if (ref($request)) {
+ $request->print("Unable to handle ambiguous references:$url:.");
+ }
return ();
}
}
@@ -10848,6 +14233,348 @@ sub clean_symb {
return ($symb,$enc);
}
+sub build_release_hashes {
+ my ($checkparms,$checkresponsetypes,$checkcrstypes,$anonsurvey,$randomizetry) = @_;
+ return unless((ref($checkparms) eq 'HASH') && (ref($checkresponsetypes) eq 'HASH') &&
+ (ref($checkcrstypes) eq 'HASH') && (ref($anonsurvey) eq 'HASH') &&
+ (ref($randomizetry) eq 'HASH'));
+ foreach my $key (keys(%Apache::lonnet::needsrelease)) {
+ my ($item,$name,$value) = split(/:/,$key);
+ if ($item eq 'parameter') {
+ if (ref($checkparms->{$name}) eq 'ARRAY') {
+ unless(grep(/^\Q$name\E$/,@{$checkparms->{$name}})) {
+ push(@{$checkparms->{$name}},$value);
+ }
+ } else {
+ push(@{$checkparms->{$name}},$value);
+ }
+ } elsif ($item eq 'resourcetag') {
+ if ($name eq 'responsetype') {
+ $checkresponsetypes->{$value} = $Apache::lonnet::needsrelease{$key}
+ }
+ } elsif ($item eq 'course') {
+ if ($name eq 'crstype') {
+ $checkcrstypes->{$value} = $Apache::lonnet::needsrelease{$key};
+ }
+ }
+ }
+ ($anonsurvey->{major},$anonsurvey->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:anonsurvey'});
+ ($randomizetry->{major},$randomizetry->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:randomizetry'});
+ return;
+}
+
+sub update_content_constraints {
+ 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;
+ foreach my $key (keys(%Apache::lonnet::needsrelease)) {
+ my ($item,$name,$value) = split(/:/,$key);
+ if ($item eq 'resourcetag') {
+ if ($name eq 'responsetype') {
+ $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
+ }
+ }
+ }
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (defined($navmap)) {
+ my %allresponses;
+ foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
+ my %responses = $res->responseTypes();
+ foreach my $key (keys(%responses)) {
+ next unless(exists($checkresponsetypes{$key}));
+ $allresponses{$key} += $responses{$key};
+ }
+ }
+ foreach my $key (keys(%allresponses)) {
+ my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
+ if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
+ ($reqdmajor,$reqdminor) = ($major,$minor);
+ }
+ }
+ undef($navmap);
+ }
+ unless (($reqdmajor eq '') && ($reqdminor eq '')) {
+ &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
+ }
+ return;
+}
+
+sub allmaps_incourse {
+ my ($cdom,$cnum,$chome,$cid) = @_;
+ if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
+ $cid = $env{'request.course.id'};
+ $cdom = $env{'course.'.$cid.'.domain'};
+ $cnum = $env{'course.'.$cid.'.num'};
+ $chome = $env{'course.'.$cid.'.home'};
+ }
+ my %allmaps = ();
+ my $lastchange =
+ &Apache::lonnet::get_coursechange($cdom,$cnum);
+ if ($lastchange > $env{'request.course.tied'}) {
+ my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
+ unless ($ferr) {
+ &update_content_constraints($cdom,$cnum,$chome,$cid);
+ }
+ }
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (defined($navmap)) {
+ foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
+ $allmaps{$res->src()} = 1;
+ }
+ }
+ return \%allmaps;
+}
+
+sub parse_supplemental_title {
+ my ($title) = @_;
+
+ my ($foldertitle,$renametitle);
+ if ($title =~ /&&&/) {
+ $title = &HTML::Entites::decode($title);
+ }
+ if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
+ $renametitle=$4;
+ my ($time,$uname,$udom) = ($1,$2,$3);
+ $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
+ my $name = &plainname($uname,$udom);
+ $name = &HTML::Entities::encode($name,'"<>&\'');
+ $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
+ $title=''.&Apache::lonlocal::locallocaltime($time).' '.
+ $name.': '.$foldertitle;
+ }
+ if (wantarray) {
+ return ($title,$foldertitle,$renametitle);
+ }
+ return $title;
+}
+
+sub symb_to_docspath {
+ my ($symb) = @_;
+ return unless ($symb);
+ my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
+ if ($resurl=~/\.(sequence|page)$/) {
+ $mapurl=$resurl;
+ } elsif ($resurl eq 'adm/navmaps') {
+ $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
+ }
+ my $mapresobj;
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (ref($navmap)) {
+ $mapresobj = $navmap->getResourceByUrl($mapurl);
+ }
+ $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
+ my $type=$2;
+ my $path;
+ if (ref($mapresobj)) {
+ my $pcslist = $mapresobj->map_hierarchy();
+ if ($pcslist ne '') {
+ foreach my $pc (split(/,/,$pcslist)) {
+ next if ($pc <= 1);
+ my $res = $navmap->getByMapPc($pc);
+ if (ref($res)) {
+ my $thisurl = $res->src();
+ $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
+ my $thistitle = $res->title();
+ $path .= '&'.
+ &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
+ &Apache::lonhtmlcommon::entity_encode($thistitle).
+ ':'.$res->randompick().
+ ':'.$res->randomout().
+ ':'.$res->encrypted().
+ ':'.$res->randomorder().
+ ':'.$res->is_page();
+ }
+ }
+ }
+ $path =~ s/^\&//;
+ my $maptitle = $mapresobj->title();
+ if ($mapurl eq 'default') {
+ $maptitle = 'Main Content';
+ }
+ $path .= (($path ne '')? '&' : '').
+ &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
+ &Apache::lonhtmlcommon::entity_encode($maptitle).
+ ':'.$mapresobj->randompick().
+ ':'.$mapresobj->randomout().
+ ':'.$mapresobj->encrypted().
+ ':'.$mapresobj->randomorder().
+ ':'.$mapresobj->is_page();
+ } else {
+ my $maptitle = &Apache::lonnet::gettitle($mapurl);
+ my $ispage = (($type eq 'page')? 1 : '');
+ if ($mapurl eq 'default') {
+ $maptitle = 'Main Content';
+ }
+ $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
+ &Apache::lonhtmlcommon::entity_encode($maptitle).':::::'.$ispage;
+ }
+ unless ($mapurl eq 'default') {
+ $path = 'default&'.
+ &Apache::lonhtmlcommon::entity_encode('Main Content').
+ ':::::&'.$path;
+ }
+ return $path;
+}
+
+sub captcha_display {
+ my ($context,$lonhost) = @_;
+ my ($output,$error);
+ my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
+ if ($captcha eq 'original') {
+ $output = &create_captcha();
+ unless ($output) {
+ $error = 'captcha';
+ }
+ } elsif ($captcha eq 'recaptcha') {
+ $output = &create_recaptcha($pubkey);
+ unless ($output) {
+ $error = 'recaptcha';
+ }
+ }
+ return ($output,$error);
+}
+
+sub captcha_response {
+ my ($context,$lonhost) = @_;
+ my ($captcha_chk,$captcha_error);
+ my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
+ if ($captcha eq 'original') {
+ ($captcha_chk,$captcha_error) = &check_captcha();
+ } elsif ($captcha eq 'recaptcha') {
+ $captcha_chk = &check_recaptcha($privkey);
+ } else {
+ $captcha_chk = 1;
+ }
+ return ($captcha_chk,$captcha_error);
+}
+
+sub get_captcha_config {
+ my ($context,$lonhost) = @_;
+ my ($captcha,$pubkey,$privkey,$hashtocheck);
+ my $hostname = &Apache::lonnet::hostname($lonhost);
+ my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
+ my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
+ if ($context eq 'usercreation') {
+ my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
+ if (ref($domconfig{$context}) eq 'HASH') {
+ $hashtocheck = $domconfig{$context}{'cancreate'};
+ if (ref($hashtocheck) eq 'HASH') {
+ if ($hashtocheck->{'captcha'} eq 'recaptcha') {
+ if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
+ $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
+ $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
+ }
+ if ($privkey && $pubkey) {
+ $captcha = 'recaptcha';
+ } else {
+ $captcha = 'original';
+ }
+ } elsif ($hashtocheck->{'captcha'} ne 'notused') {
+ $captcha = 'original';
+ }
+ }
+ } else {
+ $captcha = 'captcha';
+ }
+ } elsif ($context eq 'login') {
+ my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
+ if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
+ $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
+ $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
+ if ($privkey && $pubkey) {
+ $captcha = 'recaptcha';
+ } else {
+ $captcha = 'original';
+ }
+ } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
+ $captcha = 'original';
+ }
+ }
+ return ($captcha,$pubkey,$privkey);
+}
+
+sub create_captcha {
+ my %captcha_params = &captcha_settings();
+ my ($output,$maxtries,$tries) = ('',10,0);
+ while ($tries < $maxtries) {
+ $tries ++;
+ my $captcha = Authen::Captcha->new (
+ output_folder => $captcha_params{'output_dir'},
+ data_folder => $captcha_params{'db_dir'},
+ );
+ my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
+
+ if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
+ $output = ' '."\n".
+ &mt('Type in the letters/numbers shown below').' '.
+ ' '.
+ ' ';
+ last;
+ }
+ }
+ return $output;
+}
+
+sub captcha_settings {
+ my %captcha_params = (
+ output_dir => $Apache::lonnet::perlvar{'lonCaptchaDir'},
+ www_output_dir => "/captchaspool",
+ db_dir => $Apache::lonnet::perlvar{'lonCaptchaDb'},
+ numchars => '5',
+ );
+ return %captcha_params;
+}
+
+sub check_captcha {
+ my ($captcha_chk,$captcha_error);
+ my $code = $env{'form.code'};
+ my $md5sum = $env{'form.crypt'};
+ my %captcha_params = &captcha_settings();
+ my $captcha = Authen::Captcha->new(
+ output_folder => $captcha_params{'output_dir'},
+ data_folder => $captcha_params{'db_dir'},
+ );
+ $captcha_chk = $captcha->check_code($code,$md5sum);
+ my %captcha_hash = (
+ 0 => 'Code not checked (file error)',
+ -1 => 'Failed: code expired',
+ -2 => 'Failed: invalid code (not in database)',
+ -3 => 'Failed: invalid code (code does not match crypt)',
+ );
+ if ($captcha_chk != 1) {
+ $captcha_error = $captcha_hash{$captcha_chk}
+ }
+ return ($captcha_chk,$captcha_error);
+}
+
+sub create_recaptcha {
+ my ($pubkey) = @_;
+ my $captcha = Captcha::reCAPTCHA->new;
+ return $captcha->get_options_setter({theme => 'white'})."\n".
+ $captcha->get_html($pubkey).
+ &mt('If either word is hard to read, [_1] will replace them.',
+ ' ').
+ ' ';
+}
+
+sub check_recaptcha {
+ my ($privkey) = @_;
+ my $captcha_chk;
+ my $captcha = Captcha::reCAPTCHA->new;
+ my $captcha_result =
+ $captcha->check_answer(
+ $privkey,
+ $ENV{'REMOTE_ADDR'},
+ $env{'form.recaptcha_challenge_field'},
+ $env{'form.recaptcha_response_field'},
+ );
+ if ($captcha_result->{is_valid}) {
+ $captcha_chk = 1;
+ }
+ return $captcha_chk;
+}
+
=pod
=back
';
+ }
$endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
if ( exists( $env{'internal.head.redirect'} ) ) {
if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
@@ -4807,7 +5328,7 @@ sub standard_css {
my $mono = 'monospace';
my $data_table_head = $sidebg;
my $data_table_light = '#FAFAFA';
- my $data_table_dark = '#F0F0F0';
+ my $data_table_dark = '#E0E0E0';
my $data_table_darker = '#CCCCCC';
my $data_table_highlight = '#FFFF00';
my $mail_new = '#FFBB77';
@@ -4847,7 +5368,6 @@ body {
a:focus,
a:focus img {
color: red;
- background: yellow;
}
form, .inline {
@@ -4862,6 +5382,14 @@ form, .inline {
vertical-align:middle;
}
+.LC_floatleft {
+ float: left;
+}
+
+.LC_floatright {
+ float: right;
+}
+
.LC_400Box {
width:400px;
}
@@ -4894,12 +5422,18 @@ form, .inline {
text-decoration:none;
}
+.LC_setting {
+ text-decoration:underline;
+}
+
.LC_error {
color: red;
- font-size: larger;
}
-.LC_warning,
+.LC_warning {
+ color: darkorange;
+}
+
.LC_diff_removed {
color: red;
}
@@ -4938,35 +5472,36 @@ div.LC_confirm_box .LC_success img {
}
.LC_discussion {
- background: $tabbg;
+ background: $data_table_dark;
border: 1px solid black;
margin: 2px;
}
-.LC_disc_action_links_bar {
- background: $tabbg;
- border: none;
- margin: 4px;
-}
-
.LC_disc_action_left {
+ background: $sidebg;
text-align: left;
+ padding: 4px;
+ margin: 2px;
}
.LC_disc_action_right {
+ background: $sidebg;
text-align: right;
+ padding: 4px;
+ margin: 2px;
}
.LC_disc_new_item {
background: white;
border: 2px solid red;
- margin: 2px;
+ margin: 4px;
+ padding: 4px;
}
.LC_disc_old_item {
background: white;
- border: 1px solid black;
- margin: 2px;
+ margin: 4px;
+ padding: 4px;
}
table.LC_pastsubmission {
@@ -5058,14 +5593,16 @@ td.LC_table_cell_checkbox {
overflow: hidden;
margin: 0;
padding: 0;
+ text-align: left;
}
-#LC_head_subbox {
+.LC_head_subbox, .LC_actionbox {
clear:both;
background: #F8F8F8; /* $sidebg; */
border: 1px solid $sidebg;
- margin: 0 0 10px 0;
+ margin: 0 0 10px 0;
padding: 3px;
+ text-align: left;
}
.LC_fontsize_medium {
@@ -5086,8 +5623,9 @@ td.LC_table_cell_checkbox {
vertical-align: middle;
}
-li.LC_menubuttons_inline_text img,a {
+li.LC_menubuttons_inline_text img {
cursor:pointer;
+ text-decoration: none;
}
.LC_menubuttons_link {
@@ -5135,14 +5673,6 @@ table.LC_nested {
width: 100%;
}
-.ui-accordion,
-.ui-accordion table.LC_data_table,
-.ui-accordion table.LC_nested_outer{
- border: 0px;
- border-spacing: 0px;
- margin: 3px;
-}
-
table.LC_data_table tr th,
table.LC_calendar tr th,
table.LC_prior_tries tr th,
@@ -5203,7 +5733,8 @@ table.LC_nested tr.LC_empty_row td {
padding: 8px;
}
-table.LC_data_table tr.LC_empty_row td {
+table.LC_data_table tr.LC_empty_row td,
+table.LC_data_table tr.LC_footer_row td {
background-color: $sidebg;
}
@@ -5258,22 +5789,6 @@ table.LC_nested tr td.LC_right_item {
text-align: right;
}
-.ui-accordion table.LC_nested tr.LC_odd_row td.LC_left_item,
-.ui-accordion table.LC_nested tr.LC_even_row td.LC_left_item {
- text-align: right;
- width: 40%;
- padding-right:10px;
- vertical-align: top;
- padding: 5px;
-}
-
-.ui-accordion table.LC_nested tr.LC_odd_row td.LC_right_item,
-.ui-accordion table.LC_nested tr.LC_even_row td.LC_right_item {
- text-align: left;
- width: 60%;
- padding: 2px 4px;
-}
-
table.LC_nested tr.LC_odd_row td {
background-color: #EEEEEE;
}
@@ -5405,6 +5920,11 @@ span.LC_current_location {
background: $pgbg;
}
+span.LC_current_nav_location {
+ font-weight:bold;
+ background: $sidebg;
+}
+
span.LC_parm_menu_item {
font-size: larger;
}
@@ -5776,7 +6296,6 @@ div.LC_docs_entry_move {
table.LC_data_table tr > td.LC_docs_entry_commands,
table.LC_data_table tr > td.LC_docs_entry_parameter {
- background: #DDDDDD;
font-size: x-small;
}
@@ -5906,6 +6425,7 @@ div.LC_edit_problem_footer {
font-weight: normal;
font-size: medium;
margin: 2px;
+ background-color: $sidebg;
}
div.LC_edit_problem_header,
@@ -5922,6 +6442,7 @@ div.LC_edit_problem_header_title {
font-size: larger;
background: $tabbg;
padding: 3px;
+ margin: 0 0 5px 0;
}
table.LC_edit_problem_header_title {
@@ -5939,6 +6460,11 @@ div.LC_edit_problem_saves {
padding-bottom: 5px;
}
+.LC_edit_opt {
+ padding-left: 1em;
+ white-space: nowrap;
+}
+
img.stift {
border-width: 0;
vertical-align: middle;
@@ -5953,13 +6479,13 @@ div.LC_createcourse {
}
.LC_dccid {
+ float: right;
margin: 0.2em 0 0 0;
padding: 0;
font-size: 90%;
display:none;
}
-a:hover,
ol.LC_primary_menu a:hover,
ol#LC_MenuBreadcrumbs a:hover,
ol#LC_PathBreadcrumbs a:hover,
@@ -6033,6 +6559,7 @@ fieldset > legend {
#LC_nav_bar {
float: left;
+ background-color: $pgbg_or_bgcolor;
margin: 0 0 2px 0;
}
@@ -6041,6 +6568,7 @@ fieldset > legend {
padding: 0;
font-weight: bold;
text-align: center;
+ background-color: $pgbg_or_bgcolor;
}
#LC_nav_bar em {
@@ -6049,8 +6577,9 @@ fieldset > legend {
}
ol.LC_primary_menu {
- float: right;
margin: 0;
+ padding: 0;
+ background-color: $pgbg_or_bgcolor;
}
ol#LC_PathBreadcrumbs {
@@ -6058,14 +6587,55 @@ ol#LC_PathBreadcrumbs {
}
ol.LC_primary_menu li {
- display: inline;
- padding: 5px 5px 0 10px;
+ color: RGB(80, 80, 80);
+ vertical-align: middle;
+ text-align: left;
+ list-style: none;
+ float: left;
+}
+
+ol.LC_primary_menu li a {
+ display: block;
+ margin: 0;
+ padding: 0 5px 0 10px;
+ text-decoration: none;
+}
+
+ol.LC_primary_menu li ul {
+ display: none;
+ width: 10em;
+ background-color: $data_table_light;
+}
+
+ol.LC_primary_menu li:hover ul, ol.LC_primary_menu li.hover ul {
+ display: block;
+ position: absolute;
+ margin: 0;
+ padding: 0;
+ z-index: 2;
+}
+
+ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
+ font-size: 90%;
vertical-align: top;
+ float: none;
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+}
+
+ol.LC_primary_menu li:hover li a, ol.LC_primary_menu li.hover li a {
+ background-color:$data_table_light;
+}
+
+ol.LC_primary_menu li li a:hover {
+ color:$button_hover;
+ background-color:$data_table_dark;
}
ol.LC_primary_menu li img {
vertical-align: bottom;
height: 1.1em;
+ margin: 0.2em 0 0 0;
}
ol.LC_primary_menu a {
@@ -6103,22 +6673,61 @@ ol.LC_docs_parameters li.LC_docs_paramet
}
ul#LC_secondary_menu {
- clear: both;
+ clear: right;
color: $fontmenu;
background: $tabbg;
list-style: none;
padding: 0;
margin: 0;
width: 100%;
+ text-align: left;
+ float: left;
}
ul#LC_secondary_menu li {
font-weight: bold;
line-height: 1.8em;
- padding: 0 0.8em;
border-right: 1px solid black;
- display: inline;
vertical-align: middle;
+ float: left;
+}
+
+ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
+ background-color: $data_table_light;
+}
+
+ul#LC_secondary_menu li a {
+ padding: 0 0.8em;
+}
+
+ul#LC_secondary_menu li ul {
+ display: none;
+}
+
+ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
+ display: block;
+ position: absolute;
+ margin: 0;
+ padding: 0;
+ list-style:none;
+ float: none;
+ background-color: $data_table_light;
+ z-index: 2;
+ margin-left: -1px;
+}
+
+ul#LC_secondary_menu li ul li {
+ font-size: 90%;
+ vertical-align: top;
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+ background-color: $data_table_light;
+ list-style:none;
+ float: none;
+}
+
+ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
+ background-color: $data_table_dark;
}
ul.LC_TabContent {
@@ -6126,7 +6735,7 @@ ul.LC_TabContent {
background: $sidebg;
border-bottom: solid 1px $lg_border_color;
list-style:none;
- margin: 0 -10px;
+ margin: -1px -10px 0 -10px;
padding: 0;
}
@@ -6149,7 +6758,7 @@ ul.LC_TabContent li {
padding: 0 16px 0 10px;
background-color:$tabbg;
border-bottom:solid 1px $lg_border_color;
- border-right: solid 1px $font;
+ border-left: solid 1px $font;
}
ul.LC_TabContent .right {
@@ -6189,6 +6798,12 @@ ul.LC_TabContent li.active a {
background:#FFFFFF;
outline: none;
}
+
+ul.LC_TabContent li.goback {
+ float: left;
+ border-left: none;
+}
+
#maincoursedoc {
clear:both;
}
@@ -6243,17 +6858,15 @@ ul.LC_TabContentBigger li.active b {
background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
color:$font;
border: 0;
- cursor:default;
}
ul.LC_CourseBreadcrumbs {
background: $sidebg;
- line-height: 32px;
+ height: 2em;
padding-left: 10px;
- margin: 0 0 10px 0;
+ margin: 0;
list-style-position: inside;
-
}
ol#LC_MenuBreadcrumbs,
@@ -6295,6 +6908,11 @@ ol#LC_PathBreadcrumbs li a {
padding: 0 10px 10px 10px;
}
+.LC_DocsBox {
+ border: solid 1px $lg_border_color;
+ padding: 0 0 10px 10px;
+}
+
.LC_AboutMe_Image {
float:left;
margin-right:10px;
@@ -6435,6 +7053,10 @@ a#LC_content_toolbar_changefolder_toggle
background-image:url(/res/adm/pages/open-all-folders.gif);
}
+a#LC_content_toolbar_edittoplevel {
+ background-image:url(/res/adm/pages/edittoplevel.gif);
+}
+
ul#LC_toolbar li a:hover {
background-position: bottom center;
}
@@ -6445,6 +7067,7 @@ ul#LC_toolbar {
list-style:none;
position:relative;
background-color:white;
+ overflow: auto;
}
ul#LC_toolbar li {
@@ -6454,6 +7077,7 @@ ul#LC_toolbar li {
float: left;
display:inline;
vertical-align:middle;
+ white-space: nowrap;
}
@@ -6499,16 +7123,78 @@ ul.LC_funclist li {
line-height: 150%;
}
-.ui-accordion .LC_advanced_toggle {
- float: right;
- font-size: 90%;
- padding: 0px 4px
-}
-
.LC_hidden {
display: none;
}
+.LCmodal-overlay {
+ position:fixed;
+ top:0;
+ right:0;
+ bottom:0;
+ left:0;
+ height:100%;
+ width:100%;
+ margin:0;
+ padding:0;
+ background:#999;
+ opacity:.75;
+ filter: alpha(opacity=75);
+ -moz-opacity: 0.75;
+ z-index:101;
+}
+
+* html .LCmodal-overlay {
+ position: absolute;
+ height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
+}
+
+.LCmodal-window {
+ position:fixed;
+ top:50%;
+ left:50%;
+ margin:0;
+ padding:0;
+ z-index:102;
+ }
+
+* html .LCmodal-window {
+ position:absolute;
+}
+
+.LCclose-window {
+ position:absolute;
+ width:32px;
+ height:32px;
+ right:8px;
+ top:8px;
+ background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
+ text-indent:-99999px;
+ overflow:hidden;
+ cursor:pointer;
+}
+
+/*
+ styles used by TTH when "Default set of options to pass to tth/m
+ when converting TeX" in course settings has been set
+
+ option passed: -t
+
+*/
+
+td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
+td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
+td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
+td div.norm {line-height:normal;}
+
+/*
+ option passed -y3
+*/
+
+span.roman {font-family: serif; font-style: normal; font-weight: normal;}
+span.overacc2 {position: relative; left: .8em; top: -1.2ex;}
+span.overacc1 {position: relative; left: .6em; top: -1.2ex;}
+
END
}
@@ -6557,18 +7243,36 @@ sub headtag {
'
'.
&font_settings();
+ my $inhibitprint = &print_suppression();
+
if (!$args->{'frameset'}) {
$result .= &Apache::lonhtmlcommon::htmlareaheaders();
}
- if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
- $result .= Apache::lonxml::display_title();
+ if ($args->{'force_register'}) {
+ $result .= &Apache::lonmenu::registerurl(1);
}
if (!$args->{'no_nav_bar'}
&& !$args->{'only_body'}
&& !$args->{'frameset'}) {
$result .= &help_menu_js();
+ $result.=&modal_window();
+ $result.=&togglebox_script();
+ $result.=&wishlist_window();
+ $result.=&LCprogressbarUpdate_script();
+ } else {
+ if ($args->{'add_modal'}) {
+ $result.=&modal_window();
+ }
+ if ($args->{'add_wishlist'}) {
+ $result.=&wishlist_window();
+ }
+ if ($args->{'add_togglebox'}) {
+ $result.=&togglebox_script();
+ }
+ if ($args->{'add_progressbar'}) {
+ $result.=&LCprogressbarUpdate_script();
+ }
}
-
if (ref($args->{'redirect'})) {
my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
$url = &Apache::lonenc::check_encrypt($url);
@@ -6586,6 +7290,7 @@ ADDMETA
if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
$result .= '