+$nicescroll_js
+
+
+
END
}
@@ -7938,11 +7975,98 @@ sub end_scrollbox {
return ' |
';
}
+sub nicescroll_javascript {
+ my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
+ my %options;
+ if (ref($cursor) eq 'HASH') {
+ %options = %{$cursor};
+ }
+ unless ($options{'railalign'} =~ /^left|right$/) {
+ $options{'railalign'} = 'left';
+ }
+ unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
+ my $function = &get_users_function();
+ $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
+ unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
+ $options{'cursorcolor'} = '#00F';
+ }
+ }
+ if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
+ unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
+ $options{'cursoropacity'}='1.0';
+ }
+ } else {
+ $options{'cursoropacity'}='1.0';
+ }
+ if ($options{'cursorfixedheight'} eq 'none') {
+ delete($options{'cursorfixedheight'});
+ } else {
+ unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
+ }
+ unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
+ delete($options{'railoffset'});
+ }
+ my @niceoptions;
+ while (my($key,$value) = each(%options)) {
+ if ($value =~ /^\{.+\}$/) {
+ push(@niceoptions,$key.':'.$value);
+ } else {
+ push(@niceoptions,$key.':"'.$value.'"');
+ }
+ }
+ my $nicescroll_js = '
+$(document).ready(
+ function() {
+ $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
+ }
+);
+';
+ if ($framecheck) {
+ $nicescroll_js .= '
+function expand_div(caller) {
+ if (top === self) {
+ document.getElementById("'.$id.'").style.width = "auto";
+ document.getElementById("'.$id.'").style.height = "auto";
+ } else {
+ try {
+ if (parent.frames) {
+ if (parent.frames.length > 1) {
+ var framesrc = parent.frames[1].location.href;
+ var currsrc = framesrc.replace(/\#.*$/,"");
+ if ((caller == "search") || (currsrc == "'.$location.'")) {
+ document.getElementById("'.$id.'").style.width = "auto";
+ document.getElementById("'.$id.'").style.height = "auto";
+ }
+ }
+ }
+ } catch (e) {
+ return;
+ }
+ }
+ return;
+}
+';
+ }
+ if ($needjsready) {
+ $nicescroll_js = '
+\n";
+ } else {
+ $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
+ }
+ return $nicescroll_js;
+}
+
sub simple_error_page {
- my ($r,$title,$msg) = @_;
+ my ($r,$title,$msg,$args) = @_;
+ if (ref($args) eq 'HASH') {
+ if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
+ } else {
+ $msg = &mt($msg);
+ }
+
my $page =
&Apache::loncommon::start_page($title).
- '
'.&mt($msg).'
'.
+ '
'.$msg.'
'.
&Apache::loncommon::end_page();
if (ref($r)) {
$r->print($page);
@@ -8568,7 +8692,9 @@ Incoming parameters:
1. user's username
2. user's domain
3. quota name - portfolio, author, or course
- (if no quota name provided, defaults to portfolio).
+ (if no quota name provided, defaults to portfolio).
+4. crstype - official, unofficial or community, if quota name is
+ course
Returns:
1. Disk quota (in Mb) assigned to student.
@@ -8590,7 +8716,7 @@ defined for the user's institutional sta
sub get_user_quota {
- my ($uname,$udom,$quotaname) = @_;
+ my ($uname,$udom,$quotaname,$crstype) = @_;
my ($quota,$quotatype,$settingstatus,$defquota);
if (!defined($udom)) {
$udom = $env{'user.domain'};
@@ -8641,7 +8767,12 @@ sub get_user_quota {
if ($quota eq '' || wantarray) {
if ($quotaname eq 'course') {
my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
- $defquota = $domdefs{'uploadquota'};
+ if (($crstype eq 'official') || ($crstype eq 'unofficial') || ($crstype eq 'community')) {
+ $defquota = $domdefs{$crstype.'quota'};
+ }
+ if ($defquota eq '') {
+ $defquota = 500;
+ }
} else {
($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
}
@@ -8670,6 +8801,7 @@ Retrieves default quota assigned for sto
given an (optional) user's institutional status.
Incoming parameters:
+
1. domain
2. (Optional) institutional status(es). This is a : separated list of
status types (e.g., faculty, staff, student etc.)
@@ -8680,6 +8812,7 @@ Incoming parameters:
(if no quota name provided, defaults to portfolio).
Returns:
+
1. Default disk quota (in Mb) for user portfolios in the domain.
2. (Optional) institutional type which determined the value of the
default quota.
@@ -8693,8 +8826,6 @@ If the user's status includes multiple t
the largest default quota which applies to the user determines the
default quota returned.
-=back
-
=cut
###############################################
@@ -8743,6 +8874,11 @@ sub default_quota {
$defquota = $quotahash{'quotas'}{'default'};
}
$settingstatus = 'default';
+ if ($defquota eq '') {
+ if ($quotaname eq 'author') {
+ $defquota = 500;
+ }
+ }
}
} else {
$settingstatus = 'default';
@@ -8759,6 +8895,63 @@ sub default_quota {
}
}
+###############################################
+
+=pod
+
+=item * &excess_filesize_warning()
+
+Returns warning message if upload of file to authoring space, or copying
+of existing file within authoring space will cause quota for the authoring
+space to be exceeded.
+
+Same, if upload of a file directly to a course/community via Course Editor
+will cause quota for uploaded content for the course to be exceeded.
+
+Inputs: 6
+1. username or coursenum
+2. domain
+3. context ('author' or 'course')
+4. filename of file for which action is being requested
+5. filesize (kB) of file
+6. action being taken: copy or upload.
+
+Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
+ otherwise return null.
+
+=back
+
+=cut
+
+sub excess_filesize_warning {
+ my ($uname,$udom,$context,$filename,$filesize,$action) = @_;
+ my $current_disk_usage = 0;
+ my $disk_quota = &get_user_quota($uname,$udom,$context); #expressed in MB
+ if ($context eq 'author') {
+ my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
+ $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
+ } else {
+ foreach my $subdir ('docs','supplemental') {
+ $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
+ }
+ }
+ $disk_quota = int($disk_quota * 1000);
+ if (($current_disk_usage + $filesize) > $disk_quota) {
+ return '
'.
+ &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
+ ''.$filename.'',$filesize).''.
+ '
'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
+ $disk_quota,$current_disk_usage).
+ '
';
+ }
+ return;
+}
+
+###############################################
+
+
+
+
sub get_secgrprole_info {
my ($cdom,$cnum,$needroles,$type) = @_;
my %sections_count = &get_sections($cdom,$cnum);
@@ -9598,11 +9791,10 @@ sub ask_for_embedded_content {
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);
+ $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
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();
@@ -9686,17 +9878,18 @@ sub ask_for_embedded_content {
} else {
$embed_file = $file;
}
- my $absolutepath;
+ my ($absolutepath,$cleaned_file);
if ($embed_file =~ m{^\w+://}) {
- $newfiles{$embed_file} = 1;
- $mapping{$embed_file} = $embed_file;
+ $cleaned_file = $embed_file;
+ $newfiles{$cleaned_file} = 1;
+ $mapping{$cleaned_file} = $embed_file;
} else {
+ $cleaned_file = &clean_path($embed_file);
if ($embed_file =~ m{^/}) {
$absolutepath = $embed_file;
- $embed_file =~ s{^(/+)}{};
}
- if ($embed_file =~ m{/}) {
- my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$});
+ if ($cleaned_file =~ m{/}) {
+ my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
$path = &check_for_traversal($path,$url,$toplevel);
my $item = $fname;
if ($path ne '') {
@@ -9713,9 +9906,9 @@ sub ask_for_embedded_content {
} else {
$dependencies{$embed_file} = 1;
if ($absolutepath) {
- $mapping{$embed_file} = $absolutepath;
+ $mapping{$cleaned_file} = $absolutepath;
} else {
- $mapping{$embed_file} = $embed_file;
+ $mapping{$cleaned_file} = $embed_file;
}
}
}
@@ -10089,6 +10282,46 @@ sub ask_for_embedded_content {
return ($output,$counter,$numpathchg);
}
+=pod
+
+=item * clean_path($name)
+
+Performs clean-up of directories, subdirectories and filename in an
+embedded object, referenced in an HTML file which is being uploaded
+to a course or portfolio, where
+"Upload embedded images/multimedia files if HTML file" checkbox was
+checked.
+
+Clean-up is similar to replacements in lonnet::clean_filename()
+except each / between sub-directory and next level is preserved.
+
+=cut
+
+sub clean_path {
+ my ($embed_file) = @_;
+ $embed_file =~s{^/+}{};
+ my @contents;
+ if ($embed_file =~ m{/}) {
+ @contents = split(/\//,$embed_file);
+ } else {
+ @contents = ($embed_file);
+ }
+ my $lastidx = scalar(@contents)-1;
+ for (my $i=0; $i<=$lastidx; $i++) {
+ $contents[$i]=~s{\\}{/}g;
+ $contents[$i]=~s/\s+/\_/g;
+ $contents[$i]=~s{[^/\w\.\-]}{}g;
+ if ($i == $lastidx) {
+ $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
+ }
+ }
+ if ($lastidx > 0) {
+ return join('/',@contents);
+ } else {
+ return $contents[0];
+ }
+}
+
sub embedded_file_element {
my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
@@ -10213,7 +10446,8 @@ 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 internal use.',$1)
+ .' '.&mt('Rename the file with a different extension and re-upload.').'
';
next;
} elsif (($fname =~ /\.(\w+)$/) &&
(!defined(&Apache::loncommon::fileembstyle($1)))) {
@@ -10477,6 +10711,7 @@ sub modify_html_refs {
my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
$count += $numchg;
$allfiles{$newname} = $allfiles{$ref};
+ delete($allfiles{$ref});
}
if ($env{'form.embedded_codebase_'.$i} ne '') {
$codebase = &unescape($env{'form.embedded_codebase_'.$i});
@@ -11947,6 +12182,9 @@ sub get_turnedin_filepath {
my $title = $res->compTitle();
$title =~ s/\W+/_/g;
if ($title ne '') {
+ if (($pc > 1) && (length($title) > 12)) {
+ $title = substr($title,0,12);
+ }
push(@pathitems,$title);
}
}
@@ -11955,6 +12193,9 @@ sub get_turnedin_filepath {
my $maptitle = $mapres->compTitle();
$maptitle =~ s/\W+/_/g;
if ($maptitle ne '') {
+ if (length($maptitle) > 12) {
+ $maptitle = substr($maptitle,0,12);
+ }
push(@pathitems,$maptitle);
}
unless ($env{'request.state'} eq 'construct') {
@@ -11995,6 +12236,9 @@ sub get_turnedin_filepath {
$restitle = time;
}
}
+ if (length($restitle) > 12) {
+ $restitle = substr($restitle,0,12);
+ }
push(@pathitems,$restitle);
$path .= join('/',@pathitems);
}
@@ -12932,16 +13176,20 @@ sub restore_settings {
=item * &build_recipient_list()
-Build recipient lists for five types of e-mail:
+Build recipient lists for following types of e-mail:
(a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
-(d) Help requests, (e) Course requests needing approval, generated by
-lonerrorhandler.pm, CHECKRPMS, loncron, lonsupportreq.pm and
-loncoursequeueadmin.pm respectively.
+(d) Help requests, (e) Course requests needing approval, (f) loncapa
+module change checking, student/employee ID conflict checks, as
+generated by lonerrorhandler.pm, CHECKRPMS, loncron,
+lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
Inputs:
defmail (scalar - email address of default recipient),
-mailing type (scalar - errormail, packagesmail, or helpdeskmail),
+mailing type (scalar: errormail, packagesmail, helpdeskmail,
+requestsmail, updatesmail, or idconflictsmail).
+
defdom (domain for which to retrieve configuration settings),
+
origmail (scalar - email address of recipient from loncapa.conf,
i.e., predates configuration by DC via domainprefs.pm
@@ -13319,7 +13567,7 @@ sub assign_category_rows {
if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
my $numchildren = @{$cats->[$depth]{$parent}};
my $css_class = $itemcount%2?' class="LC_odd_row"':'';
- $text .= '
';
+ $text .= '';
for (my $j=0; $j<$numchildren; $j++) {
$name = $cats->[$depth]{$parent}[$j];
$item = &escape($name).':'.&escape($parent).':'.$depth;
@@ -14117,7 +14365,7 @@ sub init_user_environment {
# ------------------------------------ Check browser type and MathML capability
my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
- $clientunicode,$clientos) = &decode_user_agent($r);
+ $clientunicode,$clientos,$clientmobile,$clientinfo) = &decode_user_agent($r);
# ------------------------------------------------------------- Get environment
@@ -14148,6 +14396,8 @@ sub init_user_environment {
"browser.mathml" => $clientmathml,
"browser.unicode" => $clientunicode,
"browser.os" => $clientos,
+ "browser.mobile" => $clientmobile,
+ "browser.info" => $clientinfo,
"server.domain" => $Apache::lonnet::perlvar{'lonDefDomain'},
"request.course.fn" => '',
"request.course.uri" => '',
@@ -14167,6 +14417,12 @@ sub init_user_environment {
$env{'browser.interface'}=$form->{'interface'};
}
+ if ($form->{'iptoken'}) {
+ my $lonhost = $r->dir_config('lonHostID');
+ $initial_env{"user.noloadbalance"} = $lonhost;
+ $env{'user.noloadbalance'} = $lonhost;
+ }
+
my %is_adv = ( is_adv => $env{'user.adv'} );
my %domdef;
unless ($domain eq 'public') {
@@ -14400,6 +14656,30 @@ sub parse_supplemental_title {
return $title;
}
+sub recurse_supplemental {
+ my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_;
+ if ($suppmap) {
+ my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
+ if ($fatal) {
+ $errors ++;
+ } else {
+ if ($#LONCAPA::map::resources > 0) {
+ foreach my $res (@LONCAPA::map::resources) {
+ my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
+ if (($src ne '') && ($status eq 'res')) {
+ if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
+ ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors);
+ } else {
+ $numfiles ++;
+ }
+ }
+ }
+ }
+ }
+ }
+ return ($numfiles,$errors);
+}
+
sub symb_to_docspath {
my ($symb) = @_;
return unless ($symb);
@@ -14429,7 +14709,7 @@ sub symb_to_docspath {
my $thistitle = $res->title();
$path .= '&'.
&Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
- &Apache::lonhtmlcommon::entity_encode($thistitle).
+ &escape($thistitle).
':'.$res->randompick().
':'.$res->randomout().
':'.$res->encrypted().
@@ -14445,7 +14725,7 @@ sub symb_to_docspath {
}
$path .= (($path ne '')? '&' : '').
&Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
- &Apache::lonhtmlcommon::entity_encode($maptitle).
+ &escape($maptitle).
':'.$mapresobj->randompick().
':'.$mapresobj->randomout().
':'.$mapresobj->encrypted().
@@ -14458,11 +14738,11 @@ sub symb_to_docspath {
$maptitle = 'Main Content';
}
$path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
- &Apache::lonhtmlcommon::entity_encode($maptitle).':::::'.$ispage;
+ &escape($maptitle).':::::'.$ispage;
}
unless ($mapurl eq 'default') {
$path = 'default&'.
- &Apache::lonhtmlcommon::entity_encode('Main Content').
+ &escape('Main Content').
':::::&'.$path;
}
return $path;
@@ -14601,9 +14881,13 @@ sub check_captcha {
sub create_recaptcha {
my ($pubkey) = @_;
+ my $use_ssl;
+ if ($ENV{'SERVER_PORT'} == 443) {
+ $use_ssl = 1;
+ }
my $captcha = Captcha::reCAPTCHA->new;
return $captcha->get_options_setter({theme => 'white'})."\n".
- $captcha->get_html($pubkey).
+ $captcha->get_html($pubkey,undef,$use_ssl).
&mt('If either word is hard to read, [_1] will replace them.',
' ').
'
';
@@ -14626,6 +14910,28 @@ sub check_recaptcha {
return $captcha_chk;
}
+sub cleanup_html {
+ my ($incoming) = @_;
+ my $outgoing;
+ if ($incoming ne '') {
+ $outgoing = $incoming;
+ $outgoing =~ s/;/;/g;
+ $outgoing =~ s/\#/#/g;
+ $outgoing =~ s/\&/&/g;
+ $outgoing =~ s/</g;
+ $outgoing =~ s/>/>/g;
+ $outgoing =~ s/\(/(/g;
+ $outgoing =~ s/\)/)/g;
+ $outgoing =~ s/"/"/g;
+ $outgoing =~ s/'/'/g;
+ $outgoing =~ s/\$/$/g;
+ $outgoing =~ s{/}{/}g;
+ $outgoing =~ s/=/=/g;
+ $outgoing =~ s/\\/\/g
+ }
+ return $outgoing;
+}
+
=pod
=back
| |