--- loncom/interface/londocs.pm 2016/06/06 17:40:48 1.604
+++ loncom/interface/londocs.pm 2022/10/29 18:13:28 1.688
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Documents
#
-# $Id: londocs.pm,v 1.604 2016/06/06 17:40:48 raeburn Exp $
+# $Id: londocs.pm,v 1.688 2022/10/29 18:13:28 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -43,12 +43,18 @@ use Apache::lonnavdisplay();
use Apache::lonextresedit();
use Apache::lontemplate();
use Apache::lonsimplepage();
+use Apache::lonhomework();
+use Apache::lonpublisher();
+use Apache::lonparmset();
+use Apache::loncourserespicker();
use HTML::Entities;
use HTML::TokeParser;
use GDBM_File;
use File::MMagic;
+use File::Copy;
use Apache::lonlocal;
use Cwd;
+use UUID::Tiny ':std';
use LONCAPA qw(:DEFAULT :match);
my $iconpath;
@@ -85,7 +91,7 @@ sub storemap {
if ($map =~ /^default/) {
$hadchanges=1;
- } else {
+ } elsif ($contentchg) {
$suppchanges=1;
}
return ($errtext,0);
@@ -139,7 +145,101 @@ sub clean {
return $title;
}
+sub default_folderpath {
+ my ($coursenum,$coursedom,$navmapref) = @_;
+ return unless ($coursenum && $coursedom && ref($navmapref));
+# Check if entire course is hidden and/or encrypted
+ my ($hiddenmap,$encryptmap,$folderpath,$hiddentop);
+ my $toplevel = "uploaded/$coursedom/$coursenum/default.sequence";
+ unless (ref($$navmapref)) {
+ $$navmapref = Apache::lonnavmaps::navmap->new();
+ }
+ if (ref($$navmapref)) {
+ if (lc($$navmapref->get_mapparam(undef,$toplevel,"0.hiddenresource")) eq 'yes') {
+ my $filterFunc = sub { my $res = shift; return (!$res->randomout() && !$res->is_map()) };
+ my @resources = $$navmapref->retrieveResources($toplevel,$filterFunc,1,1);
+ unless (@resources) {
+ $hiddenmap = 1;
+ unless ($env{'request.role.adv'}) {
+ $hiddentop = 1;
+ if ($env{'form.folder'}) {
+ undef($env{'form.folder'});
+ }
+ }
+ }
+ }
+ if (lc($$navmapref->get_mapparam(undef,$toplevel,"0.encrypturl")) eq 'yes') {
+ $encryptmap = 1;
+ }
+ }
+ unless ($hiddentop) {
+ $folderpath='default&'.&escape(&mt('Main Content')).
+ '::'.$hiddenmap.':'.$encryptmap.'::';
+ }
+ if (wantarray) {
+ return ($folderpath,$hiddentop);
+ } else {
+ return $folderpath;
+ }
+}
+sub validate_supppath {
+ my ($coursenum,$coursedom) = @_;
+ my $backto;
+ if ($env{'form.supppath'} ne '') {
+ my @items = split(/\&/,$env{'form.supppath'});
+ my ($badpath,$got_supp,$supppath,%supphidden,%suppids);
+ for (my $i=0; $i<@items; $i++) {
+ my $odd = $i%2;
+ if ((!$odd) && ($items[$i] !~ /^supplemental(|_\d+)$/)) {
+ $badpath = 1;
+ last;
+ } elsif ($odd) {
+ my $suffix;
+ my $idx = $i-1;
+ if ($items[$i] =~ /^([^:]*)::(|1):::$/) {
+ $backto .= '&'.$1;
+ } elsif ($items[$idx] eq 'supplemental') {
+ $backto .= '&'.$items[$i];
+ } else {
+ $backto .= '&'.$items[$i];
+ my $is_hidden;
+ unless ($got_supp) {
+ my ($supplemental) = &Apache::loncommon::get_supplemental($coursenum,$coursedom);
+ if (ref($supplemental) eq 'HASH') {
+ if (ref($supplemental->{'hidden'}) eq 'HASH') {
+ %supphidden = %{$supplemental->{'hidden'}};
+ }
+ if (ref($supplemental->{'ids'}) eq 'HASH') {
+ %suppids = %{$supplemental->{'ids'}};
+ }
+ }
+ $got_supp = 1;
+ }
+ if (ref($suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}) eq 'ARRAY') {
+ my $mapid = $suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}->[0];
+ if ($supphidden{$mapid}) {
+ $is_hidden = 1;
+ }
+ }
+ $suffix = '::'.$is_hidden.':::';
+ }
+ $supppath .= '&'.$items[$i].$suffix;
+ } else {
+ $supppath .= '&'.$items[$i];
+ $backto .= '&'.$items[$i];
+ }
+ }
+ if ($badpath) {
+ delete($env{'form.supppath'});
+ } else {
+ $supppath =~ s/^\&//;
+ $backto =~ s/^\&//;
+ $env{'form.supppath'} = $supppath;
+ }
+ }
+ return $backto;
+}
sub dumpcourse {
my ($r) = @_;
@@ -564,9 +664,9 @@ sub recurse_html {
} else {
$relfile = $dependency;
$depurl = $currurlpath;
- $depurl =~ s{[^/]+$}{};
+ $depurl =~ s{[^/]+$}{};
$depurl .= $dependency;
- ($newcontainer) = ($depurl =~ m{^\Q$prefix\E(.+)$});
+ ($newcontainer) = ($depurl =~ m{^\Q$prefix\E(.+)$});
}
next if ($relfile eq '');
my $newname = $replacehash->{$container};
@@ -615,11 +715,12 @@ sub group_import {
}
}
if ($url) {
- if ($url =~ m{^(/adm/$coursedom/$coursenum/(\d+)/exttool)s?\:?(.*)$}) {
+ if ($url =~ m{^(/adm/$coursedom/$coursenum/(\d+)/ext\.tool)\:?(.*)$}) {
$url = $1;
my $marker = $2;
my $info = $3;
my ($toolid,%toolhash,%toolsettings);
+ my @extras = ('linktext','explanation','crslabel','crstitle','crsappend');
my @toolinfo = split(/:/,$info);
if ($residx) {
%toolsettings=&Apache::lonnet::dump('exttool_'.$marker,$coursedom,$coursenum);
@@ -629,28 +730,42 @@ sub group_import {
}
$toolid =~ s/\D//g;
($toolhash{'target'},$toolhash{'width'},$toolhash{'height'},
- $toolhash{'crslabel'},$toolhash{'crstitle'}) = @toolinfo;
- $toolhash{'crslabel'} = &unescape($toolhash{'crslabel'});
- $toolhash{'crstitle'} = &unescape($toolhash{'crstitle'});
+ $toolhash{'linktext'},$toolhash{'explanation'},$toolhash{'crslabel'},
+ $toolhash{'crstitle'},$toolhash{'crsappend'},$toolhash{'gradable'}) = @toolinfo;
+ foreach my $item (@extras) {
+ $toolhash{$item} = &unescape($toolhash{$item});
+ }
+ if ($folder =~ /^supplemental/) {
+ delete($toolhash{'gradable'});
+ } else {
+ $toolhash{'gradable'} =~ s/\D+//g;
+ }
if (ref($ltitoolsref) eq 'HASH') {
- my @deleted;
if (ref($ltitoolsref->{$toolid}) eq 'HASH') {
- if ($ltitoolsref->{$toolid}->{'url'} =~ m{^https://}) {
- $url =~ s/exttool$/exttools/;
- }
+ my @deleted;
$toolhash{'id'} = $toolid;
- if (($toolhash{'target'} eq 'iframe') || ($toolhash{'target'} eq 'window')) {
+ if (($toolhash{'target'} eq 'iframe') || ($toolhash{'target'} eq 'tab') ||
+ ($toolhash{'target'} eq 'window')) {
if ($toolhash{'target'} eq 'window') {
foreach my $item ('width','height') {
$toolhash{$item} =~ s/^\s+//;
$toolhash{$item} =~ s/\s+$//;
+ if ($toolhash{$item} =~ /\D/) {
+ delete($toolhash{$item});
+ if ($residx) {
+ if ($toolsettings{$item}) {
+ push(@deleted,$item);
+ }
+ }
+ }
}
}
} elsif ($residx) {
$toolhash{'target'} = $toolsettings{'target'};
if ($toolhash{'target'} eq 'window') {
- $toolhash{'width'} = $toolsettings{'width'};
- $toolhash{'height'} = $toolsettings{'height'};
+ foreach my $item ('width','height') {
+ $toolhash{$item} = $toolsettings{$item};
+ }
}
} elsif (ref($ltitoolsref->{$toolid}->{'display'}) eq 'HASH') {
$toolhash{'target'} = $ltitoolsref->{$toolid}->{'display'}->{'target'};
@@ -660,40 +775,93 @@ sub group_import {
}
}
if ($toolhash{'target'} eq 'iframe') {
- delete($toolhash{'width'});
- delete($toolhash{'height'});
- if ($residx) {
- if ($toolsettings{'width'}) {
- push(@deleted,'width');
+ foreach my $item ('width','height','linktext','explanation') {
+ delete($toolhash{$item});
+ if ($residx) {
+ if ($toolsettings{$item}) {
+ push(@deleted,$item);
+ }
}
- if ($toolsettings{'height'}) {
- push(@deleted,'height');
+ }
+ } elsif ($toolhash{'target'} eq 'tab') {
+ foreach my $item ('width','height') {
+ delete($toolhash{$item});
+ if ($residx) {
+ if ($toolsettings{$item}) {
+ push(@deleted,$item);
+ }
}
}
}
if (ref($ltitoolsref->{$toolid}->{'crsconf'}) eq 'HASH') {
- foreach my $item ('label','title') {
+ foreach my $item ('label','title','linktext','explanation') {
+ my $crsitem;
+ if (($item eq 'label') || ($item eq 'title')) {
+ $crsitem = 'crs'.$item;
+ } else {
+ $crsitem = $item;
+ }
if ($ltitoolsref->{$toolid}->{'crsconf'}->{$item}) {
- $toolhash{'crs'.$item} =~ s/^\s+//;
- $toolhash{'crs'.$item} =~ s/\s+$//;
- if ($toolhash{'crs'.$item} eq '') {
- delete($toolhash{'crs'.$item});
+ $toolhash{$crsitem} =~ s/^\s+//;
+ $toolhash{$crsitem} =~ s/\s+$//;
+ if ($toolhash{$crsitem} eq '') {
+ delete($toolhash{$crsitem});
}
} else {
- delete($toolhash{'crs'.$item});
+ delete($toolhash{$crsitem});
}
- if (($residx) && (exists($toolsettings{'crs'.$item}))) {
- unless (exists($toolhash{'crs'.$item})) {
- push(@deleted,'crs'.$item);
+ if (($residx) && (exists($toolsettings{$crsitem}))) {
+ unless (exists($toolhash{$crsitem})) {
+ push(@deleted,$crsitem);
}
}
}
}
+ if ($toolhash{'passback'}) {
+ my $gradesecret = UUID::Tiny::create_uuid_as_string(UUID_V4);
+ $toolhash{'gradesecret'} = $gradesecret;
+ $toolhash{'gradesecretdate'} = time;
+ }
+ if ($toolhash{'roster'}) {
+ my $rostersecret = UUID::Tiny::create_uuid_as_string(UUID_V4);
+ $toolhash{'rostersecret'} = $rostersecret;
+ $toolhash{'rostersecretdate'} = time;
+ }
+ my $changegradable;
+ if (($residx) && ($folder =~ /^default/)) {
+ if ($toolsettings{'gradable'}) {
+ unless (($toolhash{'gradable'}) || (defined($LONCAPA::map::zombies[$residx]))) {
+ push(@deleted,'gradable');
+ $changegradable = 1;
+ }
+ } elsif ($toolhash{'gradable'}) {
+ $changegradable = 1;
+ }
+ if (($caller eq 'londocs') && (defined($LONCAPA::map::zombies[$residx]))) {
+ $changegradable = 1;
+ if ($toolsettings{'gradable'}) {
+ $toolhash{'gradable'} = 1;
+ }
+ }
+ }
my $putres = &Apache::lonnet::put('exttool_'.$marker,\%toolhash,$coursedom,$coursenum);
if ($putres eq 'ok') {
if (@deleted) {
&Apache::lonnet::del('exttool_'.$marker,\@deleted,$coursedom,$coursenum);
- }
+ }
+ if (($changegradable) && ($folder =~ /^default/)) {
+ my $val;
+ if ($toolhash{'gradable'}) {
+ $val = 'yes';
+ } else {
+ $val = 'no';
+ }
+ &LONCAPA::map::storeparameter($residx,'parameter_0_gradable',$val,
+ 'string_yesno');
+ &remember_parms($residx,'gradable','set',$val);
+ }
+ } else {
+ return (&mt('Failed to save update to external tool.'),1);
}
}
}
@@ -709,8 +877,8 @@ sub group_import {
$donechk = 1;
}
if ($url =~ m{^/uploaded/\Q$coursedom\E/\Q$coursenum\E/(default_\d+\.)(page|sequence)$}) {
- &contained_map_check($url,$folder,\%removefrommap,\%removeparam,
- \%addedmaps,\%hierarchy,\%titles,$allmaps);
+ &contained_map_check($url,$folder,$coursenum,$coursedom,\%removefrommap,
+ \%removeparam,\%addedmaps,\%hierarchy,\%titles,$allmaps);
$importuploaded = 1;
} elsif ($url =~ m{^/res/.+\.(page|sequence)$}) {
next if ($allmaps->{$url});
@@ -723,26 +891,27 @@ sub group_import {
}
my $ext = 'false';
if ($url=~m{^http://} || $url=~m{^https://}) { $ext = 'true'; }
- $name = &LONCAPA::map::qtunescape($name);
- if ($name eq '') {
- $name = &LONCAPA::map::qtunescape(&mt('Web Page'));
- }
if ($url =~ m{^/uploaded/$coursedom/$coursenum/((?:docs|supplemental)/(?:default|\d+))/new\.html$}) {
my $filepath = $1;
- my $fname = $name;
- if ($fname =~ /^\W+$/) {
+ my $fname;
+ if ($name eq '') {
+ $name = &mt('Web Page');
$fname = 'web';
} else {
- $fname =~ s/\W/_/g;
- }
- if (length($fname) > 15) {
- $fname = substr($fname,0,14);
+ $fname = $name;
+ $fname=&Apache::lonnet::clean_filename($fname);
+ if ($fname eq '') {
+ $fname = 'web';
+ } elsif (length($fname) > 15) {
+ $fname = substr($fname,0,14);
+ }
}
+ my $title = &Apache::loncommon::cleanup_html($name);
my $initialtext = &mt('Replace with your own content.');
my $newhtml = <
$lt{'title'}:
-
+
$pathitem
@@ -5286,11 +6263,17 @@ IMSFORM
$checkbox
-
+
FUFORM
+ my $mapimportjs;
+ if ($canedit) {
+ $mapimportjs = "javascript:openbrowser('mapimportform','importmap','sequence,page','');";
+ } else {
+ $mapimportjs = "javascript:alert('".&js_escape($lt{'er'})."');";
+ }
my $importpubform=(<
- $lt{'selm'}
-
+ onfocus="this.blur();$mapimportjs" $disabled />
+ $lt{'selm'}
+
SEDFFORM
+ my $importcrsresform;
+ my ($numdirs,$pickfile) =
+ &Apache::loncommon::import_crsauthor_form('crsresimportform','coursepath','coursefile',
+ "resize_scrollbox('contentscroll','1','0');",
+ undef,'res');
+ if ($pickfile) {
+ $importcrsresform=(<
'.$lt{'dire'},
+ $defrole,'authorrole','authorpath',
+ \%select_menus,\@order,'toggleCrsResTitle();',
+ '','priv').'
';
+ $showtitle = 'none';
+ } else {
+ my $is_home;
+ $showtitle = 'inline';
+ if (grep(/^\Q$crshome\E$/,@ids)) {
+ $is_home = 1;
+ $pickdir .= '';
+ my $toppath="/priv/$coursedom/$coursenum'}";
+ my %subdirs;
+ &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs);
+ $numcrsdirs = keys(%subdirs);
+ if ($numcrsdirs) {
+ $pickdir .= &mt('Directory: ').'';
+ } else {
+ $pickdir .= ''."\n";
+ }
+ }
+ }
+
+ my %seltemplate_menus;
+ my @files = &Apache::lonhomework::get_template_list('problem');
+ my @noexamplelink = ('blank.problem','blank.library','script.library');
+ my $currentcategory = '';
+ my @ordered = ('');
+ my %templatehelp;
+ my $defcategory = '';
+ my @catorder = ($defcategory);
+ $seltemplate_menus{$defcategory}->{'order'} = [''];
+ $seltemplate_menus{$defcategory}->{'text'} = '';
+ foreach my $file (@files) {
+ if (ref($file) eq 'ARRAY') {
+ my ($path,$title,$category,$help) = @{$file};
+ next if ($title !~ /\S/);
+ if (&js_escape($category) ne $currentcategory) {
+ $currentcategory = &js_escape($category);
+ push(@catorder,&js_escape($currentcategory));
+ $seltemplate_menus{$currentcategory}->{'text'} = $category;
+ $seltemplate_menus{$currentcategory}->{'default'} = '';
+ $seltemplate_menus{$currentcategory}->{'select2'}->{''} = '';
+ push(@{$seltemplate_menus{$currentcategory}->{'order'}},'');
+ }
+ if ($path) {
+ $seltemplate_menus{$currentcategory}->{'select2'}->{&js_escape($path)} = $title;
+ push(@{$seltemplate_menus{$currentcategory}->{'order'}},&js_escape($path));
+ if ($help) {
+ $templatehelp{$path} = $help;
+ }
+ }
+ }
+ }
+
+ my $templates = $lt{'cate'}.' '.
+ &Apache::loncommon::linked_select_forms('courseresform','
'.$lt{'tmpl'}.' ',
+ $defcategory,'tempcategory','template',
+ \%seltemplate_menus,\@catorder,
+ "resize_scrollbox('contentscroll','1','0');",
+ "toggleExampleText();",'template').'
';
+ my $templatepreview = ''.
+ ''.&mt('Example').'';
+ my $crsresform=(<
+ $lt{'uste'} + + +
+ + + + + + + + +RESFORM my $specialdocumentsform; my @specialdocumentsforma; @@ -5539,7 +6785,7 @@ NFFORM $pathitem - $lt{'syll'} + $lt{'syll'} $help{'Syllabus'} @@ -5547,32 +6793,40 @@ NSYLFORM my $newgroupfileform=(<'.$error.'
'); } if ($hadchanges) { - &mark_hash_old(); - } + unless (&is_hash_old()) { + &mark_hash_old(); + } + } &changewarning($r,''); } @@ -5632,7 +6891,7 @@ unless ($container eq 'page') { unless ($supplementalflag) { $folder='supplemental'; } - if ($folder =~ /^supplemental$/ && + if (($folder eq 'supplemental') && (($env{'form.folderpath'} =~ /^default\&/) || ($env{'form.folderpath'} eq ''))) { $env{'form.folderpath'} = &supplemental_base(); } elsif ($allowed) { @@ -5650,7 +6909,7 @@ unless ($container eq 'page') { SNSFORM @@ -5704,7 +6965,7 @@ SNSFORM $pathitem - $lt{'mypi'} + $lt{'mypi'} $help{'My Personal Information Page'} SNAMFORM @@ -5731,22 +6992,22 @@ SWEBFORM my @specialdocs = ( - {'' + {'' =>$supnewsylform}, - {'' + {'' =>$supnewaboutmeform}, {''=>$supwebpageform}, ); -my @supimportdoc = ( - {'' - =>$supextform}); + my @supexternal = ( + {'' + =>$supextform}); if (keys(%ltitools)) { - push(@supimportdoc, - {'' + push(@supexternal, + {'' =>$supexttoolform}); } - push(@supimportdoc, + my @supimportdoc = ( {'' =>$supupdocform}, ); @@ -5754,32 +7015,27 @@ my @supimportdoc = ( $supupdocform = &create_form_ul(&create_list_elements(@supimportdoc)); my %suporderhash = ( '00' => ['Supnewfolder', $supnewfolderform], - 'ee' => ['Upload',$supupdocform], + 'dd' => ['Upload',$supupdocform], + 'ee' => ['External',&create_form_ul(&create_list_elements(@supexternal))], 'ff' => ['Other',&create_form_ul(&create_list_elements(@specialdocs))] ); if ($supplementalflag) { - my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, - $supplementalflag,\%suporderhash,$iconpath,$pathitem,\%ltitools); - if ($error) { - $r->print(''.$error.'
'); - } else { - if ($suppchanges) { - my %servers = &Apache::lonnet::internet_dom_servers($coursedom); - my @ids=&Apache::lonnet::current_machine_ids(); - foreach my $server (keys(%servers)) { - next if (grep(/^\Q$server\E$/,@ids)); - my $hashid=$coursenum.':'.$coursedom; - my $cachekey = &escape('suppcount').':'.&escape($hashid); - &Apache::lonnet::remote_devalidate_cache($server,[$cachekey]); - } - &Apache::lonnet::get_numsuppfiles($coursenum,$coursedom,1); - undef($suppchanges); - } - } + $suppchanges = 0; + my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, + $supplementalflag,\%suporderhash,$iconpath,$pathitem, + \%ltitools,$canedit,$hostname); + if ($error) { + $r->print(''.$error.'
'); + } + if ($suppchanges) { + &Apache::lonnet::update_supp_caches($coursedom,$coursenum); + undef($suppchanges); + } } } elsif ($supplementalflag) { my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, - $supplementalflag,'',$iconpath,$pathitem); + $supplementalflag,'',$iconpath,$pathitem,'',$canedit, + $hostname); if ($error) { $r->print(''.$error.'
'); } @@ -5806,13 +7062,16 @@ my %suporderhash = ( &entryline(0,&mt("Click to download or use your browser's Save Link function"),$showdoc).''); } } - $r->print(&Apache::loncommon::end_page()); + unless ($noendpage) { + $r->print(&Apache::loncommon::end_page()); + } return OK; } sub embedded_form_elems { my ($phase,$primaryurl,$newidx) = @_; my $folderpath = &HTML::Entities::encode($env{'form.folderpath'},'<>&"'); + $newidx =~s /\D+//g; return <