--- loncom/interface/londocs.pm 2002/07/31 13:50:38 1.3
+++ loncom/interface/londocs.pm 2025/01/04 21:16:45 1.718
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Documents
#
-# $Id: londocs.pm,v 1.3 2002/07/31 13:50:38 www Exp $
+# $Id: londocs.pm,v 1.718 2025/01/04 21:16:45 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -29,36 +29,10821 @@
package Apache::londocs;
use strict;
-use Apache::Constants qw(:common);
+use Apache::Constants qw(:common :http);
+use Apache::imsexport;
+use Apache::lonnet;
+use Apache::loncommon;
+use Apache::lonhtmlcommon;
+use LONCAPA::map();
+use Apache::lonratedt();
+use Apache::lonxml;
+use Apache::lonclonecourse;
+use Apache::lonnavmaps;
+use Apache::lonnavdisplay();
+use Apache::lonextresedit();
+use Apache::lontemplate();
+use Apache::lonsimplepage();
+use Apache::lonhomework();
+use Apache::lonpublisher();
+use Apache::loncourserespicker();
+use HTML::Entities;
+use HTML::TokeParser;
+use HTML::LCParser;
+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;
+
+my %hash;
+
+my $hashtied;
+my %alreadyseen=();
+
+my $hadchanges;
+my $suppchanges;
+
+
+my %help=();
+
+
+sub mapread {
+ my ($coursenum,$coursedom,$map)=@_;
+ return
+ &LONCAPA::map::mapread('/uploaded/'.$coursedom.'/'.$coursenum.'/'.
+ $map);
+}
+
+sub storemap {
+ my ($coursenum,$coursedom,$map,$contentchg)=@_;
+ my $report;
+ if (($contentchg) && ($map =~ /^default/)) {
+ $report = 1;
+ }
+ my ($outtext,$errtext)=
+ &LONCAPA::map::storemap('/uploaded/'.$coursedom.'/'.$coursenum.'/'.
+ $map,1,$report);
+ if ($errtext) { return ($errtext,2); }
+
+ if ($map =~ /^default/) {
+ $hadchanges=1;
+ } elsif ($contentchg) {
+ $suppchanges=1;
+ }
+ return ($errtext,0);
+}
+
+
+
+sub authorhosts {
+ my %outhash=();
+ my $home=0;
+ my $other=0;
+ my @ids=&Apache::lonnet::current_machine_ids();
+ foreach my $key (keys(%env)) {
+ if ($key=~/^user\.role\.(au|ca)\.(.+)$/) {
+ my $role=$1;
+ my $realm=$2;
+ my ($start,$end)=split(/\./,$env{$key});
+ if (($start) && ($start>time)) { next; }
+ if (($end) && (time>$end)) { next; }
+ my ($ca,$cd);
+ if ($1 eq 'au') {
+ $ca=$env{'user.name'};
+ $cd=$env{'user.domain'};
+ } else {
+ ($cd,$ca)=($realm=~/^\/($match_domain)\/($match_username)$/);
+ }
+ my $allowed=0;
+ my $myhome=&Apache::lonnet::homeserver($ca,$cd);
+ foreach my $id (@ids) {
+ if ($id eq $myhome) {
+ $allowed=1;
+ last;
+ }
+ }
+ if ($allowed) {
+ $home++;
+ $outhash{'home_'.$ca.':'.$cd}=1;
+ } else {
+ $outhash{'otherhome_'.$ca.':'.$cd}=$myhome;
+ $other++;
+ }
+ }
+ }
+ return ($home,$other,%outhash);
+}
-sub handler {
- my $r = shift;
- $r->content_type('text/html');
- $r->send_http_header;
- return OK if $r->header_only;
- my $url;
+sub clean {
+ my ($title)=@_;
+ $title=~s/[^\w\/\!\$\%\^\*\-\_\=\+\;\:\,\\\|\`\~]+/\_/gs;
+ return $title;
+}
- if ($ENV{'form.uploaddoc.filename'}) {
- $url=&Apache::lonnet::userfileupload('uploaddoc');
+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;
+ }
}
- $r->print(< '.&mt('No author or co-author roles on this server.').' '.&mt('Selected Authoring Space is not on this server.').' '.&mt('No author or co-author roles on this server.').' '.
+ &mt('You do not have permission to copy files and/or directories from Course Authoring Space.').
+ ' '.&mt('Selected Authoring Space is not on this server.').' '.&mt('After removal of disallowed characters target sub-directory name was blank.').' '.&mt('After replacement of non-alphanumeric characters with _ in target sub-directory name, nothing but underscores was left.').' '.&mt('No files or directories selected for copying').' '.&mt('Copy to: [_1]',
+ ''.$desturl.'/'.$subdir.'').
+ ' '.&mt('Target directory: [_1] does not exist, and could not be created.',
+ ''.$desturl.'/'.$subdir.'/'.$dir.'').
+ ' '.&mt('Target directory: [_1] does not exist, and could not be created.',
+ ''.$desturl.'/'.$subdir.'/'.$path.'').
+ ' '.&mt('Target file: [_1] already exists -- not overwriting.',
+ ''.$desturl.'/'.$subdir.'/'.$file.'').
+ ' '.&mt('Target directory: [_1] name is already in a use for a file -- not overwriting.',
+ ''.$desturl.'/'.$subdir.'/'.$file.'').
+ ' '.&mt('No files or sub-directories copied').' '.&mt('Created the following directories in [_1]:',''.$desturl.'/'.$subdir.'').
+ ''.&mt('Copying Files').'
');
+ my $title=$env{'form.authorfolder'};
+ $title=&clean($title);
+ my ($navmap,$errormsg) =
+ &Apache::loncourserespicker::get_navmap_object($crstype,'dumpdocs');
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my (%maps,%resources,%titles);
+ if (!ref($navmap)) {
+ $r->print($errormsg.
+ &endContentScreen());
+ return '';
+ } else {
+ &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles,
+ 'dumpdocs',$cdom,$cnum);
+ }
+ my @todump = &Apache::loncommon::get_env_multiple('form.archive');
+ my (%tocopy,%replacehash,%lookup,%deps,%display,%result,%depresult,%simpleproblems,%simplepages,
+ %newcontent,%has_simpleprobs);
+ foreach my $item (sort {$a <=> $b} (@todump)) {
+ my $name = $env{'form.namefor_'.$item};
+ if ($resources{$item}) {
+ my ($map,$id,$res) = &Apache::lonnet::decode_symb($resources{$item});
+ if ($res =~ m{^uploaded/$cdom/$cnum/\E((?:docs|supplemental)/.+)$}) {
+ $tocopy{$1} = $name;
+ $display{$item} = $1;
+ $lookup{$1} = $item;
+ } elsif ($res eq 'lib/templates/simpleproblem.problem') {
+ $simpleproblems{$item} = {
+ symb => $resources{$item},
+ name => $name,
+ };
+ $display{$item} = 'simpleproblem_'.$name;
+ if ($map =~ m{^\Quploaded/$cdom/$cnum/\E(.+)$}) {
+ $has_simpleprobs{$1}{$id} = $item;
+ }
+ } elsif ($res =~ m{^adm/$match_domain/$match_username/(\d+)/smppg}) {
+ my $marker = $1;
+ my $db_name = &Apache::lonsimplepage::get_db_name($res,$marker,$cdom,$cnum);
+ $simplepages{$item} = {
+ res => $res,
+ title => $titles{$item},
+ db => $db_name,
+ marker => $marker,
+ symb => $resources{$item},
+ name => $name,
+ };
+ $display{$item} = '/'.$res;
+ }
+ } elsif ($maps{$item}) {
+ if ($maps{$item} =~ m{^\Quploaded/$cdom/$cnum/\E((?:default|supplemental)_\d+\.(?:sequence|page))$}) {
+ $tocopy{$1} = $name;
+ $display{$item} = $1;
+ $lookup{$1} = $item;
+ }
+ } else {
+ next;
+ }
+ }
+ my $crs='/uploaded/'.$env{'request.course.id'}.'/';
+ $crs=~s/\_/\//g;
+ my $mm = new File::MMagic;
+ my $prefix = "/uploaded/$cdom/$cnum/";
+ %replacehash = %tocopy;
+ foreach my $item (sort(keys(%simpleproblems))) {
+ my $content = &Apache::imsexport::simpleproblem($simpleproblems{$item}{'symb'});
+ $newcontent{$display{$item}} = $content;
+ }
+ my $gateway = Apache::lonhtmlgateway->new('web');
+ foreach my $item (sort(keys(%simplepages))) {
+ if (ref($simplepages{$item}) eq 'HASH') {
+ my $pagetitle = $simplepages{$item}{'title'};
+ my %fields = &Apache::lonnet::dump($simplepages{$item}{'db'},$cdom,$cnum);
+ my %contents;
+ foreach my $field (keys(%fields)) {
+ if ($field =~ /^(?:aaa|bbb|ccc)_(\w+)$/) {
+ my $name = $1;
+ my $msg = $fields{$field};
+ if ($name eq 'webreferences') {
+ if ($msg =~ m{^https?://}) {
+ $contents{$name} = ''.$msg.'';
+ }
+ } else {
+ $msg = &Encode::decode('utf8',$msg);
+ $msg = $gateway->process_outgoing_html($msg,1);
+ $contents{$name} = $msg;
+ }
+ } elsif ($field eq 'uploaded.photourl') {
+ my $marker = $simplepages{$item}{marker};
+ if ($fields{$field} =~ m{^\Q$prefix\E(simplepage/$marker/.+)$}) {
+ my $filepath = $1;
+ my ($relpath,$fname) = ($filepath =~ m{^(.+/)([^/]+)$});
+ if ($fname ne '') {
+ $fname=~s/\.(\w+)$//;
+ my $ext=$1;
+ $fname = &clean($fname);
+ $fname.='.'.$ext;
+ $contents{image} = '';
+ $replacehash{$filepath} = $relpath.$fname;
+ $deps{$item}{$filepath} = 1;
+ }
+ }
+ }
+ }
+ $replacehash{'/'.$simplepages{$item}{'res'}} = $simplepages{$item}{'name'};
+ $lookup{'/'.$simplepages{$item}{'res'}} = $item;
+ my $content = '
-Course Documents
-
-$url
+';
+ if ($contents{title}) {
+ $content .= "\n".''.$contents{title}.'
';
+ }
+ if ($contents{image}) {
+ $content .= "\n".$contents{image};
+ }
+ if ($contents{content}) {
+ $content .= '
+'.&mt('Content').'
'.
+$contents{content}.'
+'.&mt('Web References').'
'.
+$contents{webreferences}.'
+
'.$item.' => '.$newfilename.': ';
+ } else {
+ $depresult .= ''.$depresult.'
');
+ }
+ }
+ } else {
+ my ($navmap,$errormsg) =
+ &Apache::loncourserespicker::get_navmap_object($crstype,'dumpdocs');
+ if (!ref($navmap)) {
+ $r->print($errormsg);
+ } else {
+ my $title=$origcrsdata{'description'};
+ $title=~s/[\/\s]+/\_/gs;
+ $title=&clean($title);
+ my $formname = 'dumpdoc';
+ my $preamble = &authorspace_selector($r,$formname,$home,$title,%outhash).
+ ''."\n";
+ my %uploadedfiles;
+ &tiehash();
+ foreach my $file (&Apache::lonclonecourse::crsdirlist($origcrsid,'userfiles')) {
+ my ($ext)=($file=~/\.(\w+)$/);
+# FIXME Check supplemental here
+ my $title=$hash{'title_'.$hash{
+ 'ids_/uploaded/'.$origcrsdata{'domain'}.'/'.$origcrsdata{'num'}.'/'.$file}};
+ if (!$title) {
+ $title=$file;
+ } else {
+ $title=~s|/|_|g;
+ }
+ $title=~s/\.(\w+)$//;
+ $title=&clean($title);
+ $title.='.'.$ext;
+# $r->print("\n"
+ $uploadedfiles{$file} = $title;
+ }
+ &untiehash();
+ $r->print(&Apache::loncourserespicker::create_picker($navmap,'dumpdocs',$formname,$crstype,undef,
+ undef,undef,$preamble,$home,\%uploadedfiles));
+ }
+ }
+ $r->print(&endContentScreen());
+}
+
+sub authorspace_selector {
+ my ($r,$formname,$home,$title,%outhash) = @_;
+ $r->print(' '.&mt('Copying Files and/or Sub-directories').'
');
+ if ($readonly) {
+ $r->print('
'."\n".
+ ''.&mt('Target directory: [_1] does not exist, and could not be created.',
+ ''.$desturl.'/'.$subdir.'').
+ '
'.&mt('Copied the following files to [_1]:',''.$desturl.'/'.$subdir.''). + '
'."\n". + ''.&mt('You may also need to copy the following missing dependencies for files copied to [_1]:', + ''.$desturl.'/'.$subdir.''). + '
'."\n". + ''.&mt('No currently existing files or directories in Course Authoring Space selected for copying').'
'); + $r->print(&endContentScreen()); + return ''; + } + } else { + my $chkname = 'copytouser'; + my $context = 'crsauthored'; + my (%subdirs,%files,@dirs_by_depth,@files_by_depth,%parent,%children,%hierarchy,@checked_maps); + &Apache::lonnet::recursedirs($is_course_home,1,undef,$exclude,0,0,$srcurl,'',\%subdirs,\%files,1); + foreach my $key (keys(%subdirs)) { + next if (($key eq '/') || ($key eq '')); + my @items = split(/\//,$key); + my $dir = pop(@items); + my $depth = scalar(@items); + my $path; + if (!$depth) { + $path = '/'; + } else { + $path = join('/',@items); + } + $dirs_by_depth[$depth]{$path}{$dir} = 1; + } + foreach my $path (keys(%files)) { + next if ($path eq ''); + my $depth; + if ($path eq '/') { + $depth = 0; + } else { + $depth = scalar(split(/\//,$path)); + } + if (ref($files{$path}) eq 'HASH') { + foreach my $file (keys(%{$files{$path}})) { + $files_by_depth[$depth]{$path}{$file} = $files{$path}{$file}; + } + } + } + my ($info,$display,$onsubmit,$togglebuttons,$disabled); + my (%resdirs,%resfiles); + &Apache::lonnet::recursedirs($is_course_home,1,undef,$res_exclude,0,0,$resurl,'',\%resdirs,\%resfiles); + my $numpub = 0; + if (keys(%resfiles)) { + foreach my $dir (keys(%resfiles)) { + if (ref($resfiles{$dir}) eq 'HASH') { + foreach my $file (keys(%{$resfiles{$dir}})) { + if (exists($files{$dir}{$file})) { + $numpub ++; + } + } + } + } + } + if ($readonly) { + $disabled = ' disabled="disabled"'; + } + if ($disabled) { + $togglebuttons = '