--- loncom/interface/londocs.pm 2002/08/01 15:26:23 1.6
+++ loncom/interface/londocs.pm 2017/12/29 15:55:21 1.648
@@ -1,7 +1,7 @@
# The LearningOnline Network
# Documents
#
-# $Id: londocs.pm,v 1.6 2002/08/01 15:26:23 www Exp $
+# $Id: londocs.pm,v 1.648 2017/12/29 15:55:21 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -29,98 +29,8914 @@
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::lonparmset();
+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);
-sub handler {
- my $r = shift;
- $r->content_type('text/html');
- $r->send_http_header;
- return OK if $r->header_only;
+my $iconpath;
+
+my %hash;
+
+my $hashtied;
+my %alreadyseen=();
-# does this user have privileges to post, etc?
- my $allowed=&Apache::lonnet::allowed('srm',$ENV{'request.course.id'});
+my $hadchanges;
+my $suppchanges;
- if ($allowed) {
- &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
- ['remove'])
+
+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); }
-# get course data
- my $coursenum=$ENV{'course.'.$ENV{'request.course.id'}.'.num'};
- my $coursedom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'};
+ if ($map =~ /^default/) {
+ $hadchanges=1;
+ } else {
+ $suppchanges=1;
+ }
+ return ($errtext,0);
+}
-# upload a file
- if (($ENV{'form.uploaddoc.filename'}) && ($allowed)) {
- my $id=time.'_'.$ENV{'user.name'}.'_'.$ENV{'user.domain'};
-# this is for a course, not a user, so set coursedoc flag
-# probably the only place in the system where this should be "1"
- my $url=&Apache::lonnet::userfileupload('uploaddoc',1);
- if ($url=~/^error\:/) {
- } else {
- my $comment=$ENV{'form.comment'};
- $comment=~s/\\<\;/g;
- $comment=~s/\>/\>\;/g;
- &Apache::lonnet::put('coursedocs',
- { $id.'.url' => $url,
- $id.'.comment' => $comment },
- $coursedom,$coursenum);
- }
- }
-
-# delete a file
- if ($ENV{'form.remove'}=~/$ENV{'user.name'}\_$ENV{'user.domain'}$/) {
- my $id=$ENV{'form.remove'};
- &Apache::lonnet::del('coursedocs',
- [$id.'.url',$id.'.comment'],
- $coursedom,$coursenum);
- }
-# print screen
- $r->print(<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);
+ my @ids=&Apache::lonnet::current_machine_ids();
+ 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 clean {
+ my ($title)=@_;
+ $title=~s/[^\w\/\!\$\%\^\*\-\_\=\+\;\:\,\\\|\`\~]+/\_/gs;
+ 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 dumpcourse {
+ my ($r) = @_;
+ my $crstype = &Apache::loncommon::course_type();
+ my ($starthash,$js);
+ unless (($env{'form.authorspace'}) && ($env{'form.authorfolder'}=~/\w/)) {
+ $js = <<"ENDJS";
+
+ENDJS
+ $starthash = {
+ add_entries => {'onload' => "hide_searching();"},
+ };
+ }
+ $r->print(&Apache::loncommon::start_page('Copy '.$crstype.' Content to Authoring Space',$js,$starthash)."\n".
+ &Apache::lonhtmlcommon::breadcrumbs('Copy '.$crstype.' Content to Authoring Space')."\n");
+ $r->print(&startContentScreen('tools'));
+ my ($home,$other,%outhash)=&authorhosts();
+ unless ($home) {
+ $r->print(&endContentScreen());
+ return '';
+ }
+ my $origcrsid=$env{'request.course.id'};
+ my %origcrsdata=&Apache::lonnet::coursedescription($origcrsid);
+ if (($env{'form.authorspace'}) && ($env{'form.authorfolder'}=~/\w/)) {
+# Do the dumping
+ unless ($outhash{'home_'.$env{'form.authorspace'}}) {
+ $r->print(&endContentScreen());
+ return '';
+ }
+ my ($ca,$cd)=split(/\:/,$env{'form.authorspace'});
+ $r->print(''.&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 = '
+
+
+'.$pagetitle.'
+
+';
+ if ($contents{title}) {
+ $content .= "\n".''.$contents{title}.' ';
+ }
+ if ($contents{image}) {
+ $content .= "\n".$contents{image};
+ }
+ if ($contents{content}) {
+ $content .= '
+
+
'.&mt('Content').' '.
+$contents{content}.'
+
';
+ }
+ if ($contents{webreferences}) {
+ $content .= '
+
+
'.&mt('Web References').' '.
+$contents{webreferences}.'
+';
+ }
+ $content .= '
+
+
+';
+ $newcontent{'/'.$simplepages{$item}{res}} = $content;
+ }
+ }
+ foreach my $item (keys(%tocopy)) {
+ unless ($item=~/\.(sequence|page)$/) {
+ my $currurlpath = $prefix.$item;
+ my $currdirpath = &Apache::lonnet::filelocation('',$currurlpath);
+ &recurse_html($mm,$prefix,$currdirpath,$currurlpath,$item,$lookup{$item},\%replacehash,\%deps);
+ }
+ }
+ foreach my $num (sort {$a <=> $b} (@todump)) {
+ my $src = $display{$num};
+ next if ($src eq '');
+ my @needcopy = ();
+ if ($replacehash{$src}) {
+ push(@needcopy,$src);
+ if (ref($deps{$num}) eq 'HASH') {
+ foreach my $dep (sort(keys(%{$deps{$num}}))) {
+ if ($replacehash{$dep}) {
+ push(@needcopy,$dep);
+ }
+ }
+ }
+ } elsif ($src =~ /^simpleproblem_/) {
+ push(@needcopy,$src);
+ }
+ next if (@needcopy == 0);
+ my ($result,$depresult);
+ for (my $i=0; $i<@needcopy; $i++) {
+ my $item = $needcopy[$i];
+ my $newfilename;
+ if ($simpleproblems{$num}) {
+ $newfilename=$title.'/'.$simpleproblems{$num}{'name'};
+ } else {
+ $newfilename=$title.'/'.$replacehash{$item};
+ }
+ $newfilename=~s/\.(\w+)$//;
+ my $ext=$1;
+ $newfilename=&clean($newfilename);
+ $newfilename.='.'.$ext;
+ my ($newrelpath) = ($newfilename =~ m{^\Q$title/\E(.+)$});
+ if ($newrelpath ne $replacehash{$item}) {
+ $replacehash{$item} = $newrelpath;
+ }
+ my @dirs=split(/\//,$newfilename);
+ my $path=$r->dir_config('lonDocRoot')."/priv/$cd/$ca";
+ my $makepath=$path;
+ my $fail;
+ my $origin;
+ for (my $i=0;$i<$#dirs;$i++) {
+ $makepath.='/'.$dirs[$i];
+ unless (-e $makepath) {
+ unless(mkdir($makepath,0755)) {
+ $fail = &mt('Directory creation failed.');
+ }
+ }
+ }
+ if ($i == 0) {
+ $result = ''.$item.' => '.$newfilename.' : ';
+ } else {
+ $depresult .= ''.$item.' => '.$newfilename.' '.
+ ''.
+ &mt('(dependency)').' : ';
+ }
+ if (-e $path.'/'.$newfilename) {
+ $fail = &mt('Destination already exists -- not overwriting.');
+ } else {
+ if (my $fh=Apache::File->new('>'.$path.'/'.$newfilename)) {
+ if (($item =~ m{^/adm/$match_domain/$match_username/\d+/smppg}) ||
+ ($item =~ /^simpleproblem_/)) {
+ print $fh $newcontent{$item};
+ } else {
+ my $fileloc = &Apache::lonnet::filelocation('',$prefix.$item);
+ if (-e $fileloc) {
+ if ($item=~/\.(sequence|page|html|htm|xml|xhtml)$/) {
+ if ((($1 eq 'sequence') || ($1 eq 'page')) &&
+ (ref($has_simpleprobs{$item}) eq 'HASH')) {
+ my %changes = %{$has_simpleprobs{$item}};
+ my $content = &Apache::lonclonecourse::rewritefile(
+ &Apache::lonclonecourse::readfile($env{'request.course.id'},$item),
+ (%replacehash,$crs => '')
+ );
+ my $updatedcontent = '';
+ my $parser = HTML::TokeParser->new(\$content);
+ $parser->attr_encoded(1);
+ while (my $token = $parser->get_token) {
+ if ($token->[0] eq 'S') {
+ if (($token->[1] eq 'resource') &&
+ ($token->[2]->{'src'} eq '/res/lib/templates/simpleproblem.problem') &&
+ ($changes{$token->[2]->{'id'}})) {
+ my $id = $token->[2]->{'id'};
+ $updatedcontent .= '<'.$token->[1];
+ foreach my $attrib (@{$token->[3]}) {
+ next unless ($attrib =~ /^(src|type|title|id)$/);
+ if ($attrib eq 'src') {
+ my ($file) = ($display{$changes{$id}} =~ /^\Qsimpleproblem_\E(.+)$/);
+ if ($file) {
+ $updatedcontent .= ' '.$attrib.'="'.$file.'"';
+ } else {
+ $updatedcontent .= ' '.$attrib.'="'.$token->[2]->{$attrib}.'"';
+ }
+ } else {
+ $updatedcontent .= ' '.$attrib.'="'.$token->[2]->{$attrib}.'"';
+ }
+ }
+ $updatedcontent .= ' />'."\n";
+ } else {
+ $updatedcontent .= $token->[4]."\n";
+ }
+ } else {
+ $updatedcontent .= $token->[2];
+ }
+ }
+ print $fh $updatedcontent;
+ } else {
+ print $fh &Apache::lonclonecourse::rewritefile(
+ &Apache::lonclonecourse::readfile($env{'request.course.id'},$item),
+ (%replacehash,$crs => '')
+ );
+ }
+ } else {
+ print $fh
+ &Apache::lonclonecourse::readfile($env{'request.course.id'},$item);
+ }
+ } else {
+ $fail = &mt('Source does not exist.');
+ }
+ }
+ $fh->close();
+ } else {
+ $fail = &mt('Could not write to destination.');
+ }
+ }
+ my $text;
+ if ($fail) {
+ $text = ''.&mt('fail').(' 'x3).$fail.' ';
+ } else {
+ $text = ''.&mt('ok').' ';
+ }
+ if ($i == 0) {
+ $result .= $text;
+ } else {
+ $depresult .= $text.' ';
+ }
+ }
+ $r->print($result);
+ if ($depresult) {
+ $r->print('');
+ }
+ }
+ } else {
+ my ($navmap,$errormsg) =
+ &Apache::loncourserespicker::get_navmap_object($crstype,'dumpdocs');
+ if (!ref($navmap)) {
+ $r->print($errormsg);
+ } else {
+ $r->print(''.&mt('Searching ...').'
');
+ $r->rflush();
+ my ($preamble,$formname);
+ $formname = 'dumpdoc';
+ unless ($home==1) {
+ $preamble = ''.
+ '
'.
+ &mt('Select the Authoring Space').
+ ' ';
+ }
+ my @orderspaces = ();
+ foreach my $key (sort(keys(%outhash))) {
+ if ($key=~/^home_(.+)$/) {
+ if ($1 eq $env{'user.name'}.':'.$env{'user.domain'}) {
+ unshift(@orderspaces,$1);
+ } else {
+ push(@orderspaces,$1);
+ }
+ }
+ }
+ if ($home>1) {
+ $preamble .= ''.&mt('Select').' ';
+ }
+ foreach my $user (@orderspaces) {
+ if ($home==1) {
+ $preamble .= ' ';
+ } else {
+ $preamble .= ''.$user.' - '.
+ &Apache::loncommon::plainname(split(/\:/,$user)).' ';
+ }
+ }
+ unless ($home==1) {
+ $preamble .= ' '."\n";
+ }
+ my $title=$origcrsdata{'description'};
+ $title=~s/[\/\s]+/\_/gs;
+ $title=&clean($title);
+ $preamble .= ''.
+ '
'.&mt('Folder in Authoring Space').' '.
+ ' '.
+ '
'."\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 recurse_html {
+ my ($mm,$prefix,$currdirpath,$currurlpath,$container,$item,$replacehash,$deps) = @_;
+ return unless ((ref($replacehash) eq 'HASH') && (ref($deps) eq 'HASH'));
+ my (%allfiles,%codebase);
+ if (&Apache::lonnet::extract_embedded_items($currdirpath,\%allfiles,\%codebase) eq 'ok') {
+ if (keys(%allfiles)) {
+ foreach my $dependency (keys(%allfiles)) {
+ next if (($dependency =~ m{^/(res|adm)/}) || ($dependency =~ m{^https?://}));
+ my ($depurl,$relfile,$newcontainer);
+ if ($dependency =~ m{^/}) {
+ if ($dependency =~ m{^\Q$currurlpath/\E(.+)$}) {
+ $relfile = $1;
+ if ($dependency =~ m{^\Q$prefix\E(.+)$}) {
+ $newcontainer = $1;
+ next if ($replacehash->{$newcontainer});
+ }
+ $depurl = $dependency;
+ } else {
+ next;
+ }
+ } else {
+ $relfile = $dependency;
+ $depurl = $currurlpath;
+ $depurl =~ s{[^/]+$}{};
+ $depurl .= $dependency;
+ ($newcontainer) = ($depurl =~ m{^\Q$prefix\E(.+)$});
+ }
+ next if ($relfile eq '');
+ my $newname = $replacehash->{$container};
+ $newname =~ s{[^/]+$}{};
+ $replacehash->{$newcontainer} = $newname.$relfile;
+ $deps->{$item}{$newcontainer} = 1;
+ my ($newurlpath) = ($depurl =~ m{^(.*)/[^/]+$});
+ my $depfile = &Apache::lonnet::filelocation('',$depurl);
+ my $type = $mm->checktype_filename($depfile);
+ if ($type eq 'text/html') {
+ &recurse_html($mm,$prefix,$depfile,$newurlpath,$newcontainer,$item,$replacehash,$deps);
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub group_import {
+ my ($coursenum, $coursedom, $folder, $container, $caller, $ltitoolsref, @files) = @_;
+ my ($donechk,$allmaps,%hierarchy,%titles,%addedmaps,%removefrommap,
+ %removeparam,$importuploaded,$fixuperrors);
+ $allmaps = {};
+ while (@files) {
+ my ($name, $url, $residx) = @{ shift(@files) };
+ if (($url =~ m{^/uploaded/\Q$coursedom\E/\Q$coursenum\E/(default_\d+\.)(page|sequence)$})
+ && ($caller eq 'londocs')
+ && (!&Apache::lonnet::stat_file($url))) {
+
+ my $errtext = '';
+ my $fatal = 0;
+ my $newmapstr = ''."\n".
+ ' '."\n".
+ ' '."\n".
+ ' '."\n".
+ ' ';
+ $env{'form.output'}=$newmapstr;
+ my $result=&Apache::lonnet::finishuserfileupload($coursenum,$coursedom,
+ 'output',$1.$2);
+ if ($result !~ m{^/uploaded/}) {
+ $errtext.='Map not saved: A network error occurred when trying to save the new map. ';
+ $fatal = 2;
+ }
+ if ($fatal) {
+ return ($errtext,$fatal);
+ }
+ }
+ if ($url) {
+ 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);
+ $toolid = $toolsettings{'id'};
+ } else {
+ $toolid = shift(@toolinfo);
+ }
+ $toolid =~ s/\D//g;
+ ($toolhash{'target'},$toolhash{'width'},$toolhash{'height'},
+ $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') {
+ if (ref($ltitoolsref->{$toolid}) eq 'HASH') {
+ my @deleted;
+ $toolhash{'id'} = $toolid;
+ 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') {
+ foreach my $item ('width','height') {
+ $toolhash{$item} = $toolsettings{$item};
+ }
+ }
+ } elsif (ref($ltitoolsref->{$toolid}->{'display'}) eq 'HASH') {
+ $toolhash{'target'} = $ltitoolsref->{$toolid}->{'display'}->{'target'};
+ if ($toolhash{'target'} eq 'window') {
+ $toolhash{'width'} = $ltitoolsref->{$toolid}->{'display'}->{'width'};
+ $toolhash{'height'} = $ltitoolsref->{$toolid}->{'display'}->{'height'};
+ }
+ }
+ if ($toolhash{'target'} eq 'iframe') {
+ foreach my $item ('width','height','linktext','explanation') {
+ delete($toolhash{$item});
+ if ($residx) {
+ if ($toolsettings{$item}) {
+ push(@deleted,$item);
+ }
+ }
+ }
+ } 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','linktext','explanation') {
+ my $crsitem;
+ if (($item eq 'label') || ($item eq 'title')) {
+ $crsitem = 'crs'.$item;
+ } else {
+ $crsitem = $item;
+ }
+ if ($ltitoolsref->{$toolid}->{'crsconf'}->{$item}) {
+ $toolhash{$crsitem} =~ s/^\s+//;
+ $toolhash{$crsitem} =~ s/\s+$//;
+ if ($toolhash{$crsitem} eq '') {
+ delete($toolhash{$crsitem});
+ }
+ } else {
+ delete($toolhash{$crsitem});
+ }
+ 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);
+ }
+ }
+ }
+ }
+ if (($caller eq 'londocs') &&
+ ($folder =~ /^default/)) {
+ if (($url =~ /\.(page|sequence)$/) && (!$donechk)) {
+ my $chome = &Apache::lonnet::homeserver($coursenum,$coursedom);
+ my $cid = $coursedom.'_'.$coursenum;
+ $allmaps =
+ &Apache::loncommon::allmaps_incourse($coursedom,$coursenum,
+ $chome,$cid);
+ $donechk = 1;
+ }
+ if ($url =~ m{^/uploaded/\Q$coursedom\E/\Q$coursenum\E/(default_\d+\.)(page|sequence)$}) {
+ &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});
+ }
+ }
+ if (!$residx
+ || defined($LONCAPA::map::zombies[$residx])) {
+ $residx = &LONCAPA::map::getresidx($url,$residx);
+ push(@LONCAPA::map::order, $residx);
+ }
+ 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+$/) {
+ $fname = 'web';
+ } else {
+ $fname =~ s/\W/_/g;
+ }
+ if (length($fname) > 15) {
+ $fname = substr($fname,0,14);
+ }
+ my $initialtext = &mt('Replace with your own content.');
+ my $newhtml = <
-The LearningOnline Network with CAPA
+$name
-
-Course Documents
-ENDDOCUMENT
-# ------------------------------------------------------- Print headers to docs
- my %currentdocs=&Apache::lonnet::dump('coursedocs',$coursedom,$coursenum);
- foreach (sort keys (%currentdocs)) {
- if ($_=~/(\d+)\_(\w+)\_(\w+)\.url/) {
- $r->print(' '.localtime($1).' '.$2.' '.$3.''.
- &Apache::lontexconvert::msgtexconverted(
- $currentdocs{$1.'_'.$2.'_'.$3.'.comment'}
- ).
- ' View ');
- if (($2 eq $ENV{'user.name'}) && ($3 eq $ENV{'user.domain'})
- && ($allowed)) {
- $r->print(' Remove ');
+
+$initialtext
+
+
+END
+ $env{'form.output'}=$newhtml;
+ my $result =
+ &Apache::lonnet::finishuserfileupload($coursenum,$coursedom,
+ 'output',
+ "$filepath/$residx/$fname.html");
+ if ($result =~ m{^/uploaded/}) {
+ $url = $result;
+ if ($filepath =~ /^supplemental/) {
+ $name = time.'___&&&___'.$env{'user.name'}.'___&&&___'.
+ $env{'user.domain'}.'___&&&___'.$name;
+ }
+ } else {
+ return (&mt('Failed to save new web page.'),1);
+ }
+ }
+ $url = &LONCAPA::map::qtunescape($url);
+ $LONCAPA::map::resources[$residx] =
+ join(':', ($name, $url, $ext, 'normal', 'res'));
+ }
+ }
+ if ($importuploaded) {
+ my %import_errors;
+ my %updated = (
+ removefrommap => \%removefrommap,
+ removeparam => \%removeparam,
+ );
+ my ($result,$msgsarray,$lockerror) =
+ &apply_fixups($folder,1,$coursedom,$coursenum,\%import_errors,\%updated);
+ if (keys(%import_errors) > 0) {
+ $fixuperrors =
+ ''."\n".
+ &mt('The following files are either dependencies of a web page or references within a folder and/or composite page for which errors occurred during import:')."\n".
+ '
'."\n";
+ foreach my $key (sort(keys(%import_errors))) {
+ $fixuperrors .= ''.$key.' '."\n";
+ }
+ $fixuperrors .= ' '."\n";
+ }
+ if (ref($msgsarray) eq 'ARRAY') {
+ if (@{$msgsarray} > 0) {
+ $fixuperrors .= ''.
+ join(' ',@{$msgsarray}).
+ '
';
+ }
+ }
+ if ($lockerror) {
+ $fixuperrors .= ''.
+ $lockerror.
+ '
';
+ }
+ }
+ my ($errtext,$fatal) =
+ &storemap($coursenum, $coursedom, $folder.'.'.$container,1);
+ unless ($fatal) {
+ if ($folder =~ /^supplemental/) {
+ &Apache::lonnet::get_numsuppfiles($coursenum,$coursedom,1);
+ my ($errtext,$fatal) = &mapread($coursenum,$coursedom,
+ $folder.'.'.$container);
+ }
+ }
+ return ($errtext,$fatal,$fixuperrors);
+}
+
+sub log_docs {
+ return &Apache::lonnet::write_log('course','docslog',@_);
+}
+
+{
+ my @oldresources=();
+ my @oldorder=();
+ my $parmidx;
+ my %parmaction=();
+ my %parmvalue=();
+ my $changedflag;
+
+ sub snapshotbefore {
+ @oldresources=@LONCAPA::map::resources;
+ @oldorder=@LONCAPA::map::order;
+ $parmidx=undef;
+ %parmaction=();
+ %parmvalue=();
+ $changedflag=0;
+ }
+
+ sub remember_parms {
+ my ($idx,$parameter,$action,$value)=@_;
+ $parmidx=$idx;
+ $parmaction{$parameter}=$action;
+ $parmvalue{$parameter}=$value;
+ $changedflag=1;
+ }
+
+ sub log_differences {
+ my ($plain)=@_;
+ my %storehash=('folder' => $plain,
+ 'currentfolder' => $env{'form.folder'});
+ if ($parmidx) {
+ $storehash{'parameter_res'}=$oldresources[$parmidx];
+ foreach my $parm (keys(%parmaction)) {
+ $storehash{'parameter_action_'.$parm}=$parmaction{$parm};
+ $storehash{'parameter_value_'.$parm}=$parmvalue{$parm};
+ }
+ }
+ my $maxidx=$#oldresources;
+ if ($#LONCAPA::map::resources>$#oldresources) {
+ $maxidx=$#LONCAPA::map::resources;
+ }
+ for (my $idx=0; $idx<=$maxidx; $idx++) {
+ if ($LONCAPA::map::resources[$idx] ne $oldresources[$idx]) {
+ $storehash{'before_resources_'.$idx}=$oldresources[$idx];
+ $storehash{'after_resources_'.$idx}=$LONCAPA::map::resources[$idx];
+ $changedflag=1;
+ }
+ if ($LONCAPA::map::order[$idx] ne $oldorder[$idx]) {
+ $storehash{'before_order_res_'.$idx}=$oldresources[$oldorder[$idx]];
+ $storehash{'after_order_res_'.$idx}=$LONCAPA::map::resources[$LONCAPA::map::order[$idx]];
+ $changedflag=1;
+ }
+ }
+ $storehash{'maxidx'}=$maxidx;
+ if ($changedflag) { &log_docs(\%storehash); }
+ }
+}
+
+sub docs_change_log {
+ my ($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath,$canedit)=@_;
+ my $supplementalflag=($env{'form.folderpath'}=~/^supplemental/);
+ my $navmap;
+ my $js = ''."\n";
+ $r->print(&Apache::loncommon::start_page('Content Change Log',$js));
+ $r->print(&Apache::lonhtmlcommon::breadcrumbs('Content Change Log'));
+ $r->print(&startContentScreen(($supplementalflag?'suppdocs':'docs')));
+ my %orderhash;
+ my $container='sequence';
+ my $pathitem;
+ if ($env{'form.folderpath'} =~ /\:1$/) {
+ $container='page';
+ }
+ my $folderpath=$env{'form.folderpath'};
+ if ($folderpath eq '') {
+ $folderpath = &default_folderpath($coursenum,$coursedom,\$navmap);
+ }
+ undef($navmap);
+ $pathitem = ' ';
+ my $readfile="/uploaded/$coursedom/$coursenum/$folder.$container";
+ my $jumpto = $readfile;
+ $jumpto =~ s{^/}{};
+ my $tid = 1;
+ if ($supplementalflag) {
+ $tid = 2;
+ }
+ my ($breadcrumbtrail) =
+ &Apache::lonhtmlcommon::docs_breadcrumbs($allowed,$crstype,1);
+ $r->print($breadcrumbtrail.
+ &generate_edit_table($tid,\%orderhash,undef,$iconpath,$jumpto,
+ $readfile));
+ my %docslog=&Apache::lonnet::dump('nohist_docslog',
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+
+ if ((keys(%docslog))[0]=~/^error\:/) { undef(%docslog); }
+
+ my %saveable_parameters = ('show' => 'scalar',);
+ &Apache::loncommon::store_course_settings('docs_log',
+ \%saveable_parameters);
+ &Apache::loncommon::restore_course_settings('docs_log',
+ \%saveable_parameters);
+ if (!$env{'form.show'}) { $env{'form.show'}=10; }
+# FIXME: internationalization seems wrong here
+ my %lt=('hiddenresource' => 'Resources hidden',
+ 'encrypturl' => 'URL hidden',
+ 'randompick' => 'Randomly pick',
+ 'randomorder' => 'Randomly ordered',
+ 'gradable' => 'Grade can be assigned to External Tool',
+ 'set' => 'set to',
+ 'del' => 'deleted');
+ my $filter = &Apache::loncommon::display_filter('docslog')."\n".
+ $pathitem."\n".
+ ' '.
+ (' 'x2).' ';
+ $r->print(''.
+ '
'.&mt('Display of Content Changes').' '."\n".
+ &makedocslogform($filter,1).
+ ' ');
+ $r->print(&Apache::loncommon::start_data_table().&Apache::loncommon::start_data_table_header_row().
+ ''.&mt('Time').' '.&mt('User').' '.&mt('Folder').' '.&mt('Before').' '.
+ &mt('After').' '.
+ &Apache::loncommon::end_data_table_header_row());
+ my $shown=0;
+ foreach my $id (sort { $docslog{$b}{'exe_time'}<=>$docslog{$a}{'exe_time'} } (keys(%docslog))) {
+ if ($env{'form.displayfilter'} eq 'currentfolder') {
+ if ($docslog{$id}{'logentry'}{'currentfolder'} ne $folder) { next; }
+ }
+ my @changes=keys(%{$docslog{$id}{'logentry'}});
+ if ($env{'form.displayfilter'} eq 'containing') {
+ my $wholeentry=$docslog{$id}{'exe_uname'}.':'.$docslog{$id}{'exe_udom'}.':'.
+ &Apache::loncommon::plainname($docslog{$id}{'exe_uname'},$docslog{$id}{'exe_udom'});
+ foreach my $key (@changes) {
+ $wholeentry.=':'.$docslog{$id}{'logentry'}{$key};
+ }
+ if ($wholeentry!~/\Q$env{'form.containingphrase'}\E/i) { next; }
+ }
+ my $count = 0;
+ my $time =
+ &Apache::lonlocal::locallocaltime($docslog{$id}{'exe_time'});
+ my $plainname =
+ &Apache::loncommon::plainname($docslog{$id}{'exe_uname'},
+ $docslog{$id}{'exe_udom'});
+ my $about_me_link =
+ &Apache::loncommon::aboutmewrapper($plainname,
+ $docslog{$id}{'exe_uname'},
+ $docslog{$id}{'exe_udom'});
+ my $send_msg_link='';
+ if ((($docslog{$id}{'exe_uname'} ne $env{'user.name'})
+ || ($docslog{$id}{'exe_udom'} ne $env{'user.domain'}))) {
+ $send_msg_link =' '.
+ &Apache::loncommon::messagewrapper(&mt('Send message'),
+ $docslog{$id}{'exe_uname'},
+ $docslog{$id}{'exe_udom'});
+ }
+ $r->print(&Apache::loncommon::start_data_table_row());
+ $r->print(''.$time.'
+ '.$about_me_link.
+ ''.$docslog{$id}{'exe_uname'}.
+ ':'.$docslog{$id}{'exe_udom'}.' '.
+ $send_msg_link.' '.
+ $docslog{$id}{'logentry'}{'folder'}.' ');
+ my $is_supp = 0;
+ if ($docslog{$id}{'logentry'}{'currentfolder'} =~ /^supplemental/) {
+ $is_supp = 1;
+ }
+# Before
+ for (my $idx=0;$idx<=$docslog{$id}{'logentry'}{'maxidx'};$idx++) {
+ my $oldname=(split(/\:/,$docslog{$id}{'logentry'}{'before_resources_'.$idx}))[0];
+ my $newname=(split(/\:/,$docslog{$id}{'logentry'}{'after_resources_'.$idx}))[0];
+ if ($oldname ne $newname) {
+ my $shown = &LONCAPA::map::qtescape($oldname);
+ if ($is_supp) {
+ $shown = &Apache::loncommon::parse_supplemental_title($shown);
+ }
+ $r->print($shown);
+ }
+ }
+ $r->print('');
+ for (my $idx=0;$idx<=$docslog{$id}{'logentry'}{'maxidx'};$idx++) {
+ if ($docslog{$id}{'logentry'}{'before_order_res_'.$idx}) {
+ my $shown = &LONCAPA::map::qtescape((split(/\:/,$docslog{$id}{'logentry'}{'before_order_res_'.$idx}))[0]);
+ if ($is_supp) {
+ $shown = &Apache::loncommon::parse_supplemental_title($shown);
+ }
+ $r->print(''.$shown.' ');
+ }
+ }
+ $r->print(' ');
+# After
+ $r->print(' ');
+
+ for (my $idx=0;$idx<=$docslog{$id}{'logentry'}{'maxidx'};$idx++) {
+ my $oldname=(split(/\:/,$docslog{$id}{'logentry'}{'before_resources_'.$idx}))[0];
+ my $newname=(split(/\:/,$docslog{$id}{'logentry'}{'after_resources_'.$idx}))[0];
+ if ($oldname ne '' && $oldname ne $newname) {
+ my $shown = &LONCAPA::map::qtescape($newname);
+ if ($is_supp) {
+ $shown = &Apache::loncommon::parse_supplemental_title(&LONCAPA::map::qtescape($newname));
+ }
+ $r->print($shown);
+ }
+ }
+ $r->print('');
+ for (my $idx=0;$idx<=$docslog{$id}{'logentry'}{'maxidx'};$idx++) {
+ if ($docslog{$id}{'logentry'}{'after_order_res_'.$idx}) {
+ my $shown = &LONCAPA::map::qtescape((split(/\:/,$docslog{$id}{'logentry'}{'after_order_res_'.$idx}))[0]);
+ if ($is_supp) {
+ $shown = &Apache::loncommon::parse_supplemental_title($shown);
+ }
+ $r->print(''.$shown.' ');
+ }
+ }
+ $r->print(' ');
+ if ($docslog{$id}{'logentry'}{'parameter_res'}) {
+ $r->print(&LONCAPA::map::qtescape((split(/\:/,$docslog{$id}{'logentry'}{'parameter_res'}))[0]).':');
+ foreach my $parameter ('randompick','hiddenresource','encrypturl','randomorder','gradable') {
+ if ($docslog{$id}{'logentry'}{'parameter_action_'.$parameter}) {
+# FIXME: internationalization seems wrong here
+ $r->print(''.
+ &mt($lt{$parameter}.' '.$lt{$docslog{$id}{'logentry'}{'parameter_action_'.$parameter}}.' [_1]',
+ $docslog{$id}{'logentry'}{'parameter_value_'.$parameter})
+ .' ');
+ }
+ }
+ $r->print(' ');
+ }
+# End
+ $r->print(' '.&Apache::loncommon::end_data_table_row());
+ $shown++;
+ if (!($env{'form.show'} eq &mt('all')
+ || $shown<=$env{'form.show'})) { last; }
+ }
+ $r->print(&Apache::loncommon::end_data_table()."\n".
+ &makesimpleeditform($pathitem)."\n".
+ '');
+ $r->print(&endContentScreen());
+}
+
+sub update_paste_buffer {
+ my ($coursenum,$coursedom,$folder) = @_;
+ my (@possibles,%removals,%cuts,$output);
+ if ($env{'form.multiremove'}) {
+ $env{'form.multiremove'} =~ s/,$//;
+ map { $removals{$_} = 1; } split(/,/,$env{'form.multiremove'});
+ }
+ if (($env{'form.multicopy'}) || ($env{'form.multicut'})) {
+ if ($env{'form.multicut'}) {
+ $env{'form.multicut'} =~ s/,$//;
+ foreach my $item (split(/,/,$env{'form.multicut'})) {
+ unless ($removals{$item}) {
+ $cuts{$item} = 1;
+ push(@possibles,$item.':cut');
+ }
+ }
+ }
+ if ($env{'form.multicopy'}) {
+ $env{'form.multicopy'} =~ s/,$//;
+ foreach my $item (split(/,/,$env{'form.multicopy'})) {
+ unless ($removals{$item} || $cuts{$item}) {
+ push(@possibles,$item.':copy');
+ }
+ }
+ }
+ } elsif ($env{'form.markcopy'}) {
+ @possibles = split(/,/,$env{'form.markcopy'});
+ }
+
+ return if (@possibles == 0);
+ return if (!defined($env{'form.copyfolder'}));
+
+ my ($errtext,$fatal) = &mapread($coursenum,$coursedom,
+ $env{'form.copyfolder'});
+ return if ($fatal);
+
+ my %curr_groups = &Apache::longroup::coursegroups();
+
+# Retrieve current paste buffer suffixes.
+ my @currpaste = split(/,/,$env{'docs.markedcopies'});
+ my (%pasteurls,@newpaste);
+
+# Construct identifiers for current contents of user's paste buffer
+ if (@currpaste) {
+ foreach my $suffix (@currpaste) {
+ my $cid = $env{'docs.markedcopy_crs_'.$suffix};
+ my $url = $env{'docs.markedcopy_url_'.$suffix};
+ my $mapidx = $env{'docs.markedcopy_map_'.$suffix};
+ if (($cid =~ /^$match_domain(?:_)$match_courseid$/) &&
+ ($url ne '')) {
+ $pasteurls{$cid.'_'.$url.'_'.$mapidx} = 1;
+ }
+ }
+ }
+
+# Mark items for copying (skip any items already in user's paste buffer)
+ my %addtoenv;
+
+ my @pathitems = split(/\&/,$env{'form.folderpath'});
+ my @folderconf = split(/\:/,$pathitems[-1]);
+ my $ispage = $folderconf[4];
+
+ foreach my $item (@possibles) {
+ my ($orderidx,$cmd) = split(/:/,$item);
+ next if ($orderidx =~ /\D/);
+ next unless (($cmd eq 'cut') || ($cmd eq 'copy') || ($cmd eq 'remove'));
+ my $mapidx = $folder.':'.$orderidx.':'.$ispage;
+ my ($title,$url)=split(':',$LONCAPA::map::resources[$orderidx]);
+ my %denied = &action_restrictions($coursenum,$coursedom,
+ &LONCAPA::map::qtescape($url),
+ $env{'form.folderpath'},\%curr_groups);
+ next if ($denied{'copy'});
+ $url=~s{http(:|:)//https(:|:)//}{https$2//};
+ next if (exists($pasteurls{$coursedom.'_'.$coursenum.'_'.$mapidx}));
+ my ($suffix,$errortxt,$locknotfreed) =
+ &new_timebased_suffix($env{'user.domain'},$env{'user.name'},'paste');
+ if ($suffix ne '') {
+ push(@newpaste,$suffix);
+ } else {
+ if ($locknotfreed) {
+ return $locknotfreed;
+ }
+ }
+ if (&is_supplemental_title($title)) {
+ &Apache::lonnet::appenv({'docs.markedcopy_supplemental_'.$suffix => $title});
+ ($title) = &Apache::loncommon::parse_supplemental_title($title);
+ }
+
+ $addtoenv{'docs.markedcopy_title_'.$suffix} = $title,
+ $addtoenv{'docs.markedcopy_url_'.$suffix} = $url,
+ $addtoenv{'docs.markedcopy_cmd_'.$suffix} = $cmd,
+ $addtoenv{'docs.markedcopy_crs_'.$suffix} = $env{'request.course.id'};
+ $addtoenv{'docs.markedcopy_map_'.$suffix} = $mapidx;
+ if ($url =~ m{^/uploaded/$match_domain/$match_courseid/(default|supplemental)_?(\d*)\.(page|sequence)$}) {
+ my $prefix = $1;
+ my $subdir =$2;
+ if ($subdir eq '') {
+ $subdir = $prefix;
+ }
+ my (%addedmaps,%removefrommap,%removeparam,%hierarchy,%titles,%allmaps);
+ &contained_map_check($url,$folder,$coursenum,$coursedom,\%removefrommap,
+ \%removeparam,\%addedmaps,\%hierarchy,\%titles,\%allmaps);
+ if (ref($hierarchy{$url}) eq 'HASH') {
+ my ($nested,$nestednames);
+ &recurse_uploaded_maps($url,$subdir,\%hierarchy,\%titles,\$nested,\$nestednames);
+ $nested =~ s/\&$//;
+ $nestednames =~ s/\Q___&&&___\E$//;
+ if ($nested ne '') {
+ $addtoenv{'docs.markedcopy_nested_'.$suffix} = $nested;
+ }
+ if ($nestednames ne '') {
+ $addtoenv{'docs.markedcopy_nestednames_'.$suffix} = $nestednames;
+ }
+ }
+ }
+ if ($locknotfreed) {
+ $output = $locknotfreed;
+ last;
+ }
+ }
+ if (@newpaste) {
+ $addtoenv{'docs.markedcopies'} = join(',',(@currpaste,@newpaste));
+ }
+ &Apache::lonnet::appenv(\%addtoenv);
+ delete($env{'form.markcopy'});
+ return $output;
+}
+
+sub recurse_uploaded_maps {
+ my ($url,$dir,$hierarchy,$titlesref,$nestref,$namesref) = @_;
+ if (ref($hierarchy->{$url}) eq 'HASH') {
+ my @maps = map { $hierarchy->{$url}{$_}; } sort { $a <=> $b } (keys(%{$hierarchy->{$url}}));
+ my @titles = map { $titlesref->{$url}{$_}; } sort { $a <=> $b } (keys(%{$titlesref->{$url}}));
+ my (@uploaded,@names,%shorter);
+ for (my $i=0; $i<@maps; $i++) {
+ my ($inner) = ($maps[$i] =~ m{^/uploaded/$match_domain/$match_courseid/(?:default|supplemental)_(\d+)\.(?:page|sequence)$});
+ if ($inner ne '') {
+ push(@uploaded,$inner);
+ push(@names,&escape($titles[$i]));
+ $shorter{$maps[$i]} = $inner;
+ }
+ }
+ $$nestref .= "$dir:".join(',',@uploaded).'&';
+ $$namesref .= "$dir:".(join(',',@names)).'___&&&___';
+ foreach my $map (@maps) {
+ if ($shorter{$map} ne '') {
+ &recurse_uploaded_maps($map,$shorter{$map},$hierarchy,$titlesref,$nestref,$namesref);
+ }
+ }
+ }
+ return;
+}
+
+sub print_paste_buffer {
+ my ($r,$container,$folder,$coursedom,$coursenum) = @_;
+ return if (!defined($env{'docs.markedcopies'}));
+
+ unless (($env{'form.pastemarked'}) || ($env{'form.clearmarked'})) {
+ return if ($env{'docs.markedcopies'} eq '');
+ }
+
+ my @currpaste = split(/,/,$env{'docs.markedcopies'});
+ my ($pasteitems,@pasteable);
+ my $clipboardcount = 0;
+
+# Construct identifiers for current contents of user's paste buffer
+ foreach my $suffix (@currpaste) {
+ next if ($suffix =~ /\D/);
+ my $cid = $env{'docs.markedcopy_crs_'.$suffix};
+ my $url = $env{'docs.markedcopy_url_'.$suffix};
+ my $mapidx = $env{'docs.markedcopy_map_'.$suffix};
+ if (($cid =~ /^$match_domain\_$match_courseid$/) &&
+ ($url ne '')) {
+ $clipboardcount ++;
+ my ($is_external,$othercourse,$fromsupp,$is_uploaded_map,$parent,
+ $canpaste,$nopaste,$othercrs,$areachange,$is_exttool);
+ my $extension = (split(/\./,$env{'docs.markedcopy_url_'.$suffix}))[-1];
+ if ($url =~ m{^(?:/adm/wrapper/ext|(?:http|https)(?::|:))//} ) {
+ $is_external = 1;
+ } elsif ($url =~ m{^/adm/$match_domain/$match_courseid/\d+/ext\.tool$}) {
+ $is_exttool = 1;
+ }
+ if ($folder =~ /^supplemental/) {
+ $canpaste = &supp_pasteable($env{'docs.markedcopy_url_'.$suffix});
+ unless ($canpaste) {
+ $nopaste = &mt('Paste into Supplemental Content unavailable.');
+ }
+ } else {
+ $canpaste = 1;
+ }
+ if ($canpaste) {
+ if ($url =~ m{^/uploaded/($match_domain)/($match_courseid)/(.+)$}) {
+ my $srcdom = $1;
+ my $srcnum = $2;
+ my $rem = $3;
+ if (($srcdom ne $coursedom) || ($srcnum ne $coursenum)) {
+ $othercourse = 1;
+ if ($env{"user.priv.cm./$srcdom/$srcnum"} =~ /\Q:mdc&F\E/) {
+ $othercrs = ' '.&mt('(from another course)');
+ } else {
+ $canpaste = 0;
+ $nopaste = &mt('Paste from another course unavailable.');
+ }
+ }
+ if ($rem =~ m{^(default|supplemental)_?(\d*)\.(?:page|sequence)$}) {
+ my $prefix = $1;
+ $parent = $2;
+ if ($folder !~ /^\Q$prefix\E/) {
+ $areachange = 1;
+ }
+ $is_uploaded_map = 1;
+ }
+ } elsif (($url =~ m{^/res/lib/templates/\w+\.problem$}) ||
+ ($url =~ m{^/adm/($match_domain)/($match_username)/\d+/(bulletinboard|smppg|ext\.tool)$})) {
+ if ($cid ne $env{'request.course.id'}) {
+ my ($srcdom,$srcnum) = split(/_/,$cid);
+ if ($env{"user.priv.cm./$srcdom/$srcnum"} =~ /\Q:mdc&F\E/) {
+ if (($is_exttool) && ($srcdom ne $coursedom)) {
+ $canpaste = 0;
+ $nopaste = &mt('Paste from another domain unavailable.');
+ } else {
+ $othercrs = ' '.&mt('(from another course)');
+ }
+ } else {
+ $canpaste = 0;
+ $nopaste = &mt('Paste from another course unavailable.');
+ }
+ }
+ }
+ if ($canpaste) {
+ push(@pasteable,$suffix);
+ }
+ }
+ my $buffer;
+ if ($is_external) {
+ $buffer = &mt('External Resource').': '.
+ &LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix}).' ('.
+ &LONCAPA::map::qtescape($url).')';
+ } elsif ($is_exttool) {
+ $buffer = &mt('External Tool').': '.
+ &LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix});
+ } else {
+ my $icon = &Apache::loncommon::icon($extension);
+ if ($extension eq 'sequence' &&
+ $url =~ m{/default_\d+\.sequence$}x) {
+ $icon = &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL'));
+ $icon .= '/navmap.folder.closed.gif';
+ }
+ my $title = $env{'docs.markedcopy_title_'.$suffix};
+ if ($title eq '') {
+ ($title) = ($url =~ m{/([^/]+)$});
+ }
+ $buffer = ' '.
+ ': '.
+ &Apache::loncommon::parse_supplemental_title(
+ &LONCAPA::map::qtescape($title));
+ }
+ $pasteitems .= '';
+ my ($options,$onclick);
+ if (($canpaste) && (!$areachange) && (!$othercourse) &&
+ ($env{'docs.markedcopy_cmd_'.$suffix} eq 'cut')) {
+ if (($is_uploaded_map) ||
+ ($url =~ /(bulletinboard|smppg)$/) ||
+ ($url =~ m{^/uploaded/$coursedom/$coursenum/(?:docs|supplemental)/(.+)$})) {
+ $options = &paste_options($suffix,$is_uploaded_map,$parent);
+ $onclick= 'onclick="showOptions(this,'."'$suffix'".');" ';
+ }
+ }
+ $pasteitems .= ' '.$buffer.' ';
+ if ($nopaste) {
+ $pasteitems .= $nopaste;
+ } else {
+ if ($othercrs) {
+ $pasteitems .= $othercrs;
+ }
+ if ($options) {
+ $pasteitems .= $options;
+ }
+ }
+ $pasteitems .= '
';
+ }
+ }
+ if ($pasteitems eq '') {
+ &Apache::lonnet::delenv('docs.markedcopies');
+ }
+ my ($pasteform,$form_start,$buttons,$form_end);
+ if ($pasteitems) {
+ $pasteitems .= '
';
+ $form_start = '';
+ } else {
+ $pasteitems = &mt('Clipboard is empty');
+ }
+ $r->print($form_start
+ .''
+ .''.&mt('Clipboard').(' ' x2).$buttons.' '
+ .$pasteitems
+ .' '
+ .$form_end);
+}
+
+sub paste_options {
+ my ($suffix,$is_uploaded_map,$parent) = @_;
+ my ($copytext,$movetext);
+ if ($is_uploaded_map) {
+ $copytext = &mt('Copy to new folder');
+ $movetext = &mt('Move old');
+ } elsif ($env{'docs.markedcopy_url_'.$suffix} =~ /bulletinboard$/) {
+ $copytext = &mt('Copy to new board');
+ $movetext = &mt('Move (not posts)');
+ } elsif ($env{'docs.markedcopy_url_'.$suffix} =~ /smppg$/) {
+ $copytext = &mt('Copy to new page');
+ $movetext = &mt('Move');
+ } else {
+ $copytext = &mt('Copy to new file');
+ $movetext = &mt('Move');
+ }
+ my $output = ' '.
+ ' '.
+ ''.(' 'x 4).
+ ''.
+ ' '.
+ $copytext.' '.(' 'x2).' '.
+ '
'.
+ ' '.
+ $movetext.' ';
+ if (($is_uploaded_map) && ($env{'docs.markedcopy_nested_'.$suffix})) {
+ $output .= '
'.&mt('Folder to paste contains sub-folders').
+ ' ';
+ my @pastemaps = split(/\&/,$env{'docs.markedcopy_nested_'.$suffix});
+ my @titles = split(/\Q___&&&___\E/,$env{'docs.markedcopy_nestednames_'.$suffix});
+ my $lastdir = $parent;
+ my %depths = (
+ $lastdir => 0,
+ );
+ my (%display,%deps);
+ for (my $i=0; $i<@pastemaps; $i++) {
+ ($lastdir,my $subfolderstr) = split(/\:/,$pastemaps[$i]);
+ my ($namedir,$esctitlestr) = split(/\:/,$titles[$i]);
+ my @subfolders = split(/,/,$subfolderstr);
+ $deps{$lastdir} = \@subfolders;
+ my @subfoldertitles = map { &unescape($_); } split(/,/,$esctitlestr);
+ my $depth = $depths{$lastdir} + 1;
+ my $offset = int($depth * 4);
+ my $indent = (' ' x $offset);
+ for (my $j=0; $j<@subfolders; $j++) {
+ $depths{$subfolders[$j]} = $depth;
+ $display{$subfolders[$j]} =
+ ''.$indent.$subfoldertitles[$j].' '.
+ ''.
+ ' '.&mt('Copy to new').' '.(' ' x2).
+ ''.
+ ' '.
+ &mt('Move old').' '.
+ ' ';
+ }
+ }
+ &recurse_print(\$output,$parent,\%deps,\%display);
+ $output .= '
';
+ }
+ $output .= '
';
+ return $output;
+}
+
+sub recurse_print {
+ my ($outputref,$dir,$deps,$display) = @_;
+ $$outputref .= $display->{$dir}."\n";
+ if (ref($deps->{$dir}) eq 'ARRAY') {
+ foreach my $subdir (@{$deps->{$dir}}) {
+ &recurse_print($outputref,$subdir,$deps,$display);
+ }
+ }
+}
+
+sub supp_pasteable {
+ my ($url) = @_;
+ if (($url =~ m{^(?:/adm/wrapper/ext|(?:http|https)(?::|:))//}) ||
+ (($url =~ /\.sequence$/) && ($url =~ m{^/uploaded/})) ||
+ ($url =~ m{^/uploaded/$match_domain/$match_courseid/(docs|supplemental)/(default|\d+)/\d+/}) ||
+ ($url =~ m{^/adm/$match_domain/$match_username/aboutme}) ||
+ ($url =~ m{^/public/$match_domain/$match_courseid/syllabus}) ||
+ ($url =~ m{^/adm/$match_domain/$match_courseid/\d+/ext\.tool$})) {
+ return 1;
+ }
+ return;
+}
+
+sub paste_popup_js {
+ my %html_js_lt = &Apache::lonlocal::texthash(
+ show => 'Show Options',
+ hide => 'Hide Options',
+ );
+ my %js_lt = &Apache::lonlocal::texthash(
+ none => 'No items selected from clipboard.',
+ );
+ &html_escape(\%html_js_lt);
+ &js_escape(\%html_js_lt);
+ &js_escape(\%js_lt);
+ return <<"END";
+
+function showPasteOptions(suffix) {
+ document.getElementById('pasteoptions_'+suffix).style.display='block';
+ document.getElementById('pasteoptionstext_'+suffix).innerHTML = ' ';
+ return;
+}
+
+function hidePasteOptions(suffix) {
+ document.getElementById('pasteoptions_'+suffix).style.display='none';
+ document.getElementById('pasteoptionstext_'+suffix).innerHTML =' ';
+ return;
+}
+
+function showOptions(caller,suffix) {
+ if (document.getElementById('pasteoptionstext_'+suffix)) {
+ if (caller.checked) {
+ document.getElementById('pasteoptionstext_'+suffix).innerHTML =' ';
+ } else {
+ document.getElementById('pasteoptionstext_'+suffix).innerHTML ='';
+ }
+ if (document.getElementById('pasteoptions_'+suffix)) {
+ document.getElementById('pasteoptions_'+suffix).style.display='none';
+ }
+ }
+ return;
+}
+
+function validateClipboard() {
+ var numchk = 0;
+ if (document.pasteform.pasting.length > 1) {
+ for (var i=0; i 0) {
+ return true;
+ } else {
+ alert("$js_lt{'none'}");
+ return false;
+ }
+}
+
+function checkClipboard() {
+ if (document.pasteform.pasting.length > 1) {
+ for (var i=0; i1) {
+ for (var i=0; i supplemental content
+# changes
+
+ %msgs = &Apache::lonlocal::texthash (
+ notinsupp => 'Paste failed: content type is not supported within Supplemental Content',
+ notincrs => 'Paste failed: Item is from a different course which you do not have rights to edit.',
+ notindom => 'Paste failed: Item is an external tool from a course in a different donain.',
+ duplicate => 'Paste failed: only one instance of a particular published sequence or page is allowed within each course.',
+ );
+
+ %before = (
+ docstosupp => {
+ map => 'default',
+ doc => 'docs',
+ },
+ supptodocs => {
+ map => 'supplemental',
+ doc => 'supplemental',
+ },
+ );
+
+ %after = (
+ docstosupp => {
+ map => 'supplemental',
+ doc => 'supplemental'
+ },
+ supptodocs => {
+ map => 'default',
+ doc => 'docs',
+ },
+ );
+
+# Retrieve information about all course maps in main content area
+
+ my $allmaps = {};
+ if ($folder =~ /^default/) {
+ $allmaps =
+ &Apache::loncommon::allmaps_incourse($coursedom,$coursenum,
+ $env{"course.$env{'request.course.id'}.home"},
+ $env{'request.course.id'});
+ }
+
+ my (@toclear,%mapurls,%lockerrs,%msgerrs,%results);
+
+# Loop over the items to paste
+ foreach my $suffix (@dopaste) {
+# Maps need to be copied first
+ my (%removefrommap,%removeparam,%addedmaps,%rewrites,%retitles,%copies,
+ %dbcopies,%zombies,%params,%docmoves,%mapmoves,%mapchanges,%newsubdir,
+ %newurls,%tomove,%resdatacopy);
+ if (ref($marktomove{$suffix}) eq 'ARRAY') {
+ map { $tomove{$_} = 1; } @{$marktomove{$suffix}};
+ }
+ my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url_'.$suffix});
+ my $title=&LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix});
+ my $cid=&LONCAPA::map::qtescape($env{'docs.markedcopy_crs_'.$suffix});
+ my $oldurl = $url;
+ if ($is_map{$suffix}) {
+# If pasting a map, check if map contains other maps
+ my (%hierarchy,%titles);
+ &contained_map_check($url,$folder,$coursenum,$coursedom,
+ \%removefrommap,\%removeparam,\%addedmaps,
+ \%hierarchy,\%titles,$allmaps);
+ if ($url=~ m{^/uploaded/}) {
+ my $newurl;
+ unless ($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') {
+ ($newurl,my $error) =
+ &get_newmap_url($url,$folder,$prefixchg{$suffix},$coursedom,
+ $coursenum,$srcdom{$suffix},$srcnum{$suffix},
+ \$title,$allmaps,\%newurls);
+ if ($error) {
+ $allerrors{$suffix} = $error;
+ next;
+ }
+ if ($newurl ne '') {
+ if ($newurl ne $url) {
+ if ($newurl =~ /(?:default|supplemental)_(\d+).(?:sequence|page)$/) {
+ $newsubdir{$url} = $1;
+ }
+ $mapchanges{$url} = 1;
+ }
+ }
+ }
+ if (($srcdom{$suffix} ne $coursedom) ||
+ ($srcnum{$suffix} ne $coursenum) ||
+ ($prefixchg{$suffix}) || (($newurl ne '') && ($newurl ne $url))) {
+ unless (&url_paste_fixups($url,$folder,$prefixchg{$suffix},
+ $coursedom,$coursenum,$srcdom{$suffix},
+ $srcnum{$suffix},$allmaps,\%rewrites,
+ \%retitles,\%copies,\%dbcopies,
+ \%zombies,\%params,\%mapmoves,
+ \%mapchanges,\%tomove,\%newsubdir,
+ \%newurls,\%resdatacopy)) {
+ $mapmoves{$url} = 1;
+ }
+ $url = $newurl;
+ } elsif ($env{'docs.markedcopy_nested_'.$suffix}) {
+ &url_paste_fixups($url,$folder,$prefixchg{$suffix},$coursedom,
+ $coursenum,$srcdom{$suffix},$srcnum{$suffix},
+ $allmaps,\%rewrites,\%retitles,\%copies,\%dbcopies,
+ \%zombies,\%params,\%mapmoves,\%mapchanges,
+ \%tomove,\%newsubdir,\%newurls,\%resdatacopy);
+ }
+ } elsif ($url=~m {^/res/}) {
+# published map can only exist once, so remove from paste buffer when done
+ push(@toclear,$suffix);
+# if pasting published map (main content area only) check map not already in course
+ if ($folder =~ /^default/) {
+ if ((ref($allmaps) eq 'HASH') && ($allmaps->{$url})) {
+ $duplicate{$suffix} = 1;
+ next;
+ }
+ }
+ }
+ }
+ if ($url=~ m{/(bulletinboard|smppg|ext\.tool)$}) {
+ my $prefix = $1;
+ my $fromothercrs;
+ #need to copy the db contents to a new one, unless this is a move.
+ my %info = (
+ src => $url,
+ cdom => $coursedom,
+ cnum => $coursenum,
+ );
+ if (($srcdom{$suffix} =~ /^$match_domain$/) && ($srcnum{$suffix} =~ /^$match_courseid$/)) {
+ unless (($srcdom{$suffix} eq $coursedom) && ($srcnum{$suffix} eq $coursenum)) {
+ $fromothercrs = 1;
+ $info{'cdom'} = $srcdom{$suffix};
+ $info{'cnum'} = $srcnum{$suffix};
+ }
+ }
+ unless (($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') && (!$fromothercrs)) {
+ my (%lockerr,$msg);
+ my ($newurl,$result,$errtext) =
+ &dbcopy(\%info,$coursedom,$coursenum,\%lockerr);
+ if ($result eq 'ok') {
+ $url = $newurl;
+ $title=&mt('Copy of').' '.$title;
+ } else {
+ if ($prefix eq 'smppg') {
+ $msg = &mt('Paste failed: An error occurred when copying the simple page.').' '.$errtext;
+ } elsif ($prefix eq 'bulletinboard') {
+ $msg = &mt('Paste failed: An error occurred when copying the discussion board.').' '.$errtext;
+ } elsif ($prefix eq 'ext.tool') {
+ $msg = &mt('Paste failed: An error occurred when copying the external tool.').' '.$errtext;
+ }
+ $results{$suffix} = $result;
+ $msgerrs{$suffix} = $msg;
+ $lockerrs{$suffix} = $lockerr{$prefix};
+ next;
+ }
+ if ($lockerr{$prefix}) {
+ $lockerrs{$suffix} = $lockerr{$prefix};
+ }
+ }
+ }
+ $title = &LONCAPA::map::qtunescape($title);
+ my $ext='false';
+ if ($url=~m{^http(|s)://}) { $ext='true'; }
+ if ($env{'docs.markedcopy_supplemental_'.$suffix}) {
+ if ($folder !~ /^supplemental/) {
+ (undef,undef,$title) =
+ &Apache::loncommon::parse_supplemental_title($env{'docs.markedcopy_supplemental_'.$suffix});
+ }
+ } else {
+ if ($folder=~/^supplemental/) {
+ $title=time.'___&&&___'.$env{'user.name'}.'___&&&___'.
+ $env{'user.domain'}.'___&&&___'.$title;
+ }
+ }
+
+# For uploaded files (excluding pages/sequences) path in copied file is changed
+# if paste is from Main to Supplemental (or vice versa), or if pasting between
+# courses.
+
+ unless ($is_map{$suffix}) {
+ my $newidx;
+# Now insert the URL at the bottom
+ $newidx = &LONCAPA::map::getresidx(&LONCAPA::map::qtunescape($url));
+ if ($url =~ m{^/uploaded/$match_domain/$match_courseid/(?:docs|supplemental)/(.+)$}) {
+ my $relpath = $1;
+ if ($relpath ne '') {
+ my ($prefix,$subdir,$rem) = ($relpath =~ m{^(default|\d+)/(\d+)/(.+)$});
+ my ($newloc,$newdocsdir) = ($folder =~ /^(default|supplemental)_?(\d*)/);
+ my $newprefix = $newloc;
+ if ($newloc eq 'default') {
+ $newprefix = 'docs';
+ }
+ if ($newdocsdir eq '') {
+ $newdocsdir = 'default';
+ }
+ if (($prefixchg{$suffix}) ||
+ ($srcdom{$suffix} ne $coursedom) ||
+ ($srcnum{$suffix} ne $coursenum) ||
+ ($env{'form.docs.markedcopy_options_'.$suffix} ne 'move')) {
+ my $newpath = "$newprefix/$newdocsdir/$newidx/$rem";
+ $url =
+ &Apache::lonclonecourse::writefile($env{'request.course.id'},$newpath,
+ &Apache::lonnet::getfile($oldurl));
+ if ($url eq '/adm/notfound.html') {
+ $msgs{$suffix} = &mt('Paste failed: an error occurred saving the file.');
+ next;
+ } else {
+ my ($newsubpath) = ($newpath =~ m{^(.*/)[^/]*$});
+ $newsubpath =~ s{/+$}{/};
+ $docmoves{$oldurl} = $newsubpath;
+ }
+ }
+ }
+ } elsif ($url =~ m{^/res/lib/templates/(\w+)\.problem$}) {
+ my $template = $1;
+ if ($newidx) {
+ ©_templated_files($url,$srcdom{$suffix},$srcnum{$suffix},$srcmapidx{$suffix},
+ $coursedom,$coursenum,$template,$newidx,"$folder.$container");
+ }
+ }
+ $LONCAPA::map::resources[$newidx]=$title.':'.&LONCAPA::map::qtunescape($url).
+ ':'.$ext.':normal:res';
+ push(@LONCAPA::map::order,$newidx);
+# Store the result
+ my ($errtext,$fatal) =
+ &storemap($coursenum,$coursedom,$folder.'.'.$container,1);
+ if ($fatal) {
+ $save_err .= $errtext;
+ $allresult = 'fail';
+ }
+ }
+
+# Apply any changes to maps, or copy dependencies for uploaded HTML pages, or update
+# resourcedata for simpleproblems copied from another course
+ unless ($allresult eq 'fail') {
+ my %updated = (
+ rewrites => \%rewrites,
+ zombies => \%zombies,
+ removefrommap => \%removefrommap,
+ removeparam => \%removeparam,
+ dbcopies => \%dbcopies,
+ resdatacopy => \%resdatacopy,
+ retitles => \%retitles,
+ );
+ my %info = (
+ newsubdir => \%newsubdir,
+ params => \%params,
+ );
+ if ($prefixchg{$suffix}) {
+ $info{'before'} = $before{$prefixchg{$suffix}};
+ $info{'after'} = $after{$prefixchg{$suffix}};
+ }
+ my %moves = (
+ copies => \%copies,
+ docmoves => \%docmoves,
+ mapmoves => \%mapmoves,
+ );
+ (my $result,$msgs{$suffix},my $lockerror) =
+ &apply_fixups($folder,$is_map{$suffix},$coursedom,$coursenum,$errors,
+ \%updated,\%info,\%moves,$prefixchg{$suffix},$oldurl,
+ $url,'paste');
+ $lockerrors .= $lockerror;
+ if ($result eq 'ok') {
+ if ($is_map{$suffix}) {
+ my ($errtext,$fatal) = &mapread($coursenum,$coursedom,
+ $folder.'.'.$container);
+ if ($fatal) {
+ $allresult = 'failread';
+ } else {
+ if ($#LONCAPA::map::order<1) {
+ my $idx=&LONCAPA::map::getresidx();
+ if ($idx<=0) { $idx=1; }
+ $LONCAPA::map::order[0]=$idx;
+ $LONCAPA::map::resources[$idx]='';
+ }
+ my $newidx = &LONCAPA::map::getresidx(&LONCAPA::map::qtunescape($url));
+ $LONCAPA::map::resources[$newidx]=$title.':'.&LONCAPA::map::qtunescape($url).
+ ':'.$ext.':normal:res';
+ push(@LONCAPA::map::order,$newidx);
+
+# Store the result
+ my ($errtext,$fatal) =
+ &storemap($coursenum,$coursedom,$folder.'.'.$container,1);
+ if ($fatal) {
+ $save_err .= $errtext;
+ $allresult = 'failstore';
+ }
+ }
+ }
+ if ($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') {
+ push(@toclear,$suffix);
+ }
+ }
+ }
+ }
+ &clear_from_buffer(\@toclear,\@currpaste);
+ my $msgsarray;
+ foreach my $suffix (keys(%msgs)) {
+ if (ref($msgs{$suffix}) eq 'ARRAY') {
+ $msgsarray .= join(',',@{$msgs{$suffix}});
+ }
+ }
+ return ($allresult,$save_err,$msgsarray,$lockerrors);
+}
+
+sub do_buffer_empty {
+ my @currpaste = split(/,/,$env{'docs.markedcopies'});
+ if (@currpaste == 0) {
+ return &mt('Clipboard is already empty');
+ }
+ my @toclear = &Apache::loncommon::get_env_multiple('form.pasting');
+ if (@toclear == 0) {
+ return &mt('Nothing selected to clear from clipboard');
+ }
+ my $numdel = &clear_from_buffer(\@toclear,\@currpaste);
+ if ($numdel) {
+ return &mt('[quant,_1,item] cleared from clipboard',$numdel);
+ } else {
+ return &mt('Clipboard unchanged');
+ }
+ return;
+}
+
+sub clear_from_buffer {
+ my ($toclear,$currpaste) = @_;
+ return unless ((ref($toclear) eq 'ARRAY') && (ref($currpaste) eq 'ARRAY'));
+ my %pastebuffer;
+ map { $pastebuffer{$_} = 1; } @{$currpaste};
+ my $numdel = 0;
+ foreach my $suffix (@{$toclear}) {
+ next if ($suffix =~ /\D/);
+ next unless (exists($pastebuffer{$suffix}));
+ my $regexp = 'docs.markedcopy_[a-z]+_'.$suffix;
+ if (&Apache::lonnet::delenv($regexp,1) eq 'ok') {
+ delete($pastebuffer{$suffix});
+ $numdel ++;
+ }
+ }
+ my $newbuffer = join(',',sort(keys(%pastebuffer)));
+ &Apache::lonnet::appenv({'docs.markedcopies' => $newbuffer});
+ return $numdel;
+}
+
+sub get_newmap_url {
+ my ($url,$folder,$prefixchg,$coursedom,$coursenum,$srcdom,$srcnum,
+ $titleref,$allmaps,$newurls) = @_;
+ my $newurl;
+ if ($url=~ m{^/uploaded/}) {
+ $$titleref=&mt('Copy of').' '.$$titleref;
+ }
+ my $now = time;
+ my $suffix=$$.int(rand(100)).$now;
+ my ($oldid,$ext) = ($url=~/^(.+)\.(\w+)$/);
+ if ($oldid =~ m{^(/uploaded/$match_domain/$match_courseid/)(\D+)(\d+)$}) {
+ my $path = $1;
+ my $prefix = $2;
+ my $ancestor = $3;
+ if (length($ancestor) > 10) {
+ $ancestor = substr($ancestor,-10,10);
+ }
+ my $newid;
+ if ($prefixchg) {
+ if ($folder =~ /^supplemental/) {
+ $prefix =~ s/^default/supplemental/;
+ } else {
+ $prefix =~ s/^supplemental/default/;
+ }
+ }
+ if (($srcdom eq $coursedom) && ($srcnum eq $coursenum)) {
+ $newurl = $path.$prefix.$ancestor.$suffix.'.'.$ext;
+ } else {
+ $newurl = "/uploaded/$coursedom/$coursenum/$prefix".$now.'.'.$ext;
+ }
+ my $counter = 0;
+ my $is_unique = &uniqueness_check($newurl);
+ if ($folder =~ /^default/) {
+ if ($allmaps->{$newurl}) {
+ $is_unique = 0;
+ }
+ }
+ while ((!$is_unique || $allmaps->{$newurl} || $newurls->{$newurl}) && ($counter < 100)) {
+ $counter ++;
+ $suffix ++;
+ if (($srcdom eq $coursedom) && ($srcnum eq $coursenum)) {
+ $newurl = $path.$prefix.$ancestor.$suffix.'.'.$ext;
+ } else {
+ $newurl = "/uploaded/$coursedom/$coursenum/$prefix".$ancestor.$suffix.'.'.$ext;
+ }
+ $is_unique = &uniqueness_check($newurl);
+ }
+ if ($is_unique) {
+ $newurls->{$newurl} = 1;
+ } else {
+ if ($url=~/\.page$/) {
+ return (undef,&mt('Paste failed: an error occurred creating a unique URL for the composite page'));
+ } else {
+ return (undef,&mt('Paste failed: an error occurred creating a unique URL for the folder'));
+ }
+ }
+ }
+ return ($newurl);
+}
+
+sub dbcopy {
+ my ($dbref,$coursedom,$coursenum,$lockerrorsref) = @_;
+ my ($url,$result,$errtext);
+ if (ref($dbref) eq 'HASH') {
+ $url = $dbref->{'src'};
+ if ($url =~ m{/(smppg|bulletinboard|ext\.tool)$}) {
+ my $prefix = $1;
+ if ($prefix eq 'ext.tool') {
+ $prefix = 'exttool';
+ }
+ if (($dbref->{'cdom'} =~ /^$match_domain$/) &&
+ ($dbref->{'cnum'} =~ /^$match_courseid$/)) {
+ my $db_name;
+ my $marker = (split(m{/},$url))[4];
+ $marker=~s/\D//g;
+ if ($dbref->{'src'} =~ m{/smppg$}) {
+ $db_name =
+ &Apache::lonsimplepage::get_db_name($url,$marker,
+ $dbref->{'cdom'},
+ $dbref->{'cnum'});
+ } elsif ($dbref->{'src'} =~ m{/ext\.tool$}) {
+ $db_name = 'exttool_'.$marker;
+ } else {
+ $db_name = 'bulletinpage_'.$marker;
+ }
+ my ($suffix,$freedlock,$error) =
+ &Apache::lonnet::get_timebased_id($prefix,'num','templated',
+ $coursedom,$coursenum,
+ 'concat');
+ if (!$suffix) {
+ if ($prefix eq 'smppg') {
+ $errtext = &mt('Failed to acquire a unique timestamp-based suffix when copying a simple page [_1].',$url);
+ } elsif ($prefix eq 'exttool') {
+ $errtext = &mt('Failed to acquire a unique timestamp-based suffix when copying an external tool [_1].',$url);
+ } else {
+ $errtext = &mt('Failed to acquire a unique timestamp-based suffix when copying a discussion board [_1].',$url);
+ }
+ if ($error) {
+ $errtext .= ' '.$error;
+ }
+ } else {
+ #need to copy the db contents to a new one.
+ my %contents=&Apache::lonnet::dump($db_name,
+ $dbref->{'cdom'},
+ $dbref->{'cnum'});
+ if (exists($contents{'uploaded.photourl'})) {
+ my $photo = $contents{'uploaded.photourl'};
+ my ($subdir,$fname) =
+ ($photo =~ m{^/uploaded/$match_domain/$match_courseid/+(bulletin|simplepage)/(?:|\d+/)([^/]+)$});
+ my $newphoto;
+ if ($fname ne '') {
+ my $content = &Apache::lonnet::getfile($photo);
+ unless ($content eq '-1') {
+ $env{'form.'.$suffix.'.photourl'} = $content;
+ $newphoto =
+ &Apache::lonnet::finishuserfileupload($coursenum,$coursedom,$suffix.'.photourl',"$subdir/$suffix/$fname");
+ delete($env{'form.'.$suffix.'.photourl'});
+ }
+ }
+ if ($newphoto =~ m{^/uploaded/}) {
+ $contents{'uploaded.photourl'} = $newphoto;
+ }
+ }
+ $db_name =~ s{_\d*$ }{_$suffix}x;
+ $result=&Apache::lonnet::put($db_name,\%contents,
+ $coursedom,$coursenum);
+ if ($result eq 'ok') {
+ $url =~ s{/(\d*)/(smppg|bulletinboard|ext\.tool)$}{/$suffix/$2}x;
+ }
+ }
+ if (($freedlock ne 'ok') && (ref($lockerrorsref) eq 'HASH')) {
+ $lockerrorsref->{$prefix} =
+ ''.
+ &mt('There was a problem removing a lockfile.');
+ if ($prefix eq 'smppg') {
+ $lockerrorsref->{$prefix} .=
+ ' '.&mt('This will prevent creation of additional simple pages in this course.');
+ } elsif ($prefix eq 'exttool') {
+ $lockerrorsref->{$prefix} .=
+ ' '.&mt('This will prevent addition of more external tools to this course.');
+ } else {
+ $lockerrorsref->{$prefix} .= ' '.&mt('This will prevent creation of additional discussion boards in this course.');
+ }
+ $lockerrorsref->{$prefix} .= ' '.&mt('Please contact the [_1]helpdesk[_2] for assistance.',
+ '
',' ').
+ '
';
+ }
+ }
+ } elsif ($url =~ m{/syllabus$}) {
+ if (($dbref->{'cdom'} =~ /^$match_domain$/) &&
+ ($dbref->{'cnum'} =~ /^$match_courseid$/)) {
+ if (($dbref->{'cdom'} ne $coursedom) ||
+ ($dbref->{'cnum'} ne $coursenum)) {
+ my %contents=&Apache::lonnet::dump('syllabus',
+ $dbref->{'cdom'},
+ $dbref->{'cnum'});
+ $result=&Apache::lonnet::put('syllabus',\%contents,
+ $coursedom,$coursenum);
+ }
+ }
+ }
+ }
+ return ($url,$result,$errtext);
+}
+
+sub copy_templated_files {
+ my ($srcurl,$srcdom,$srcnum,$srcmapinfo,$coursedom,$coursenum,$template,$newidx,$newmapname) = @_;
+ my ($srcfolder,$srcid,$srcwaspage) = split(/:/,$srcmapinfo);
+ my $srccontainer = 'sequence';
+ if ($srcwaspage) {
+ $srccontainer = 'page';
+ }
+ my $srcsymb = "uploaded/$srcdom/$srcnum/$srcfolder.$srccontainer".
+ '___'.$srcid.'___'.&Apache::lonnet::declutter($srcurl);
+ my $srcprefix = $srcdom.'_'.$srcnum.'.'.$srcsymb;
+ my %srcparms=&Apache::lonnet::dump('resourcedata',$srcdom,$srcnum,$srcprefix);
+ my $newsymb = "uploaded/$coursedom/$coursenum/$newmapname".'___'.$newidx.'___lib/templates/'.
+ $template.'.problem';
+ my $newprefix = $coursedom.'_'.$coursenum.'.'.$newsymb;
+ if ($template eq 'simpleproblem') {
+ $srcprefix .= '.0.';
+ my $weightprefix = $newprefix;
+ $newprefix .= '.0.';
+ my @simpleprobqtypes = qw(radio option string essay numerical);
+ my $qtype=$srcparms{$srcprefix.'questiontype'};
+ if (grep(/^\Q$qtype\E$/,@simpleprobqtypes)) {
+ my %newdata;
+ foreach my $type (@simpleprobqtypes) {
+ if ($type eq $qtype) {
+ $newdata{"$weightprefix.$type.weight"}=1;
+ } else {
+ $newdata{"$weightprefix.$type.weight"}=0;
+ }
+ }
+ $newdata{$newprefix.'hiddenparts'} = '!'.$qtype;
+ $newdata{$newprefix.'questiontext'} = $srcparms{$srcprefix.'questiontext'};
+ $newdata{$newprefix.'hinttext'} = $srcparms{$srcprefix.'hinttext'};
+ if ($qtype eq 'numerical') {
+ $newdata{$newprefix.'numericalscript'} = $srcparms{$srcprefix.'numericalscript'};
+ $newdata{$newprefix.'numericalanswer'} = $srcparms{$srcprefix.'numericalanswer'};
+ $newdata{$newprefix.'numericaltolerance'} = $srcparms{$srcprefix.'numericaltolerance'};
+ $newdata{$newprefix.'numericalsigfigs'} = $srcparms{$srcprefix.'numericalsigfigs'};
+ } elsif (($qtype eq 'option') || ($qtype eq 'radio')) {
+ my $maxfoils=$srcparms{$srcprefix.'maxfoils'};
+ unless (defined($maxfoils)) { $maxfoils=10; }
+ unless ($maxfoils=~/^\d+$/) { $maxfoils=10; }
+ if ($maxfoils<=0) { $maxfoils=10; }
+ my $randomize=$srcparms{$srcprefix.'randomize'};
+ unless (defined($randomize)) { $randomize='yes'; }
+ unless ($randomize eq 'no') { $randomize='yes'; }
+ $newdata{$newprefix.'maxfoils'} = $maxfoils;
+ $newdata{$newprefix.'randomize'} = $randomize;
+ if ($qtype eq 'option') {
+ $newdata{$newprefix.'options'} = $srcparms{$srcprefix.'options'};
+ }
+ for (my $i=1; $i<=10; $i++) {
+ $newdata{$newprefix.'value'.$i} = $srcparms{$srcprefix.'value'.$i};
+ $newdata{$newprefix.'position'.$i} = $srcparms{$srcprefix.'position'.$i};
+ $newdata{$newprefix.'text'.$i} = $srcparms{$srcprefix.'text'.$i};
+ }
+
+ } elsif (($qtype eq 'option') || ($qtype eq 'radio')) {
+ my $maxfoils=$srcparms{$srcprefix.'maxfoils'};
+ unless (defined($maxfoils)) { $maxfoils=10; }
+ unless ($maxfoils=~/^\d+$/) { $maxfoils=10; }
+ if ($maxfoils<=0) { $maxfoils=10; }
+ my $randomize=$srcparms{$srcprefix.'randomize'};
+ unless (defined($randomize)) { $randomize='yes'; }
+ unless ($randomize eq 'no') { $randomize='yes'; }
+ $newdata{$newprefix.'maxfoils'} = $maxfoils;
+ $newdata{$newprefix.'randomize'} = $randomize;
+ if ($qtype eq 'option') {
+ $newdata{$newprefix.'options'} = $srcparms{$srcprefix.'options'};
+ }
+ for (my $i=1; $i<=10; $i++) {
+ $newdata{$newprefix.'value'.$i} = $srcparms{$srcprefix.'value'.$i};
+ $newdata{$newprefix.'position'.$i} = $srcparms{$srcprefix.'position'.$i};
+ $newdata{$newprefix.'text'.$i} = $srcparms{$srcprefix.'text'.$i};
+ }
+ } elsif ($qtype eq 'string') {
+ $newdata{$newprefix.'stringanswer'} = $srcparms{$srcprefix.'stringanswer'};
+ $newdata{$newprefix.'stringtype'} = $srcparms{$srcprefix.'stringtype'};
+ }
+ if (keys(%newdata)) {
+ my $putres = &Apache::lonnet::cput('resourcedata',\%newdata,$coursedom,
+ $coursenum);
+ if ($putres eq 'ok') {
+ &Apache::lonnet::devalidatecourseresdata($coursenum,$coursedom);
+ }
+ }
+ }
+ }
+}
+
+sub uniqueness_check {
+ my ($newurl) = @_;
+ my $unique = 1;
+ foreach my $res (@LONCAPA::map::order) {
+ my ($name,$url)=split(/\:/,$LONCAPA::map::resources[$res]);
+ $url=&LONCAPA::map::qtescape($url);
+ if ($newurl eq $url) {
+ $unique = 0;
+ last;
+ }
+ }
+ return $unique;
+}
+
+sub contained_map_check {
+ my ($url,$folder,$coursenum,$coursedom,$removefrommap,$removeparam,$addedmaps,
+ $hierarchy,$titles,$allmaps) = @_;
+ my $content = &Apache::lonnet::getfile($url);
+ unless ($content eq '-1') {
+ my $parser = HTML::TokeParser->new(\$content);
+ $parser->attr_encoded(1);
+ while (my $token = $parser->get_token) {
+ next if ($token->[0] ne 'S');
+ if ($token->[1] eq 'resource') {
+ next if ($token->[2]->{'type'} eq 'zombie');
+ my $ressrc = $token->[2]->{'src'};
+ if ($ressrc =~ m{^/adm/($match_domain)/$match_courseid/\d+/ext\.tool$}) {
+ my $srcdom = $1;
+ unless ($srcdom eq $coursedom) {
+ $removefrommap->{$url}{$token->[2]->{'id'}} = $ressrc;
+ next;
+ }
+ } elsif ($folder =~ /^supplemental/) {
+ unless (&supp_pasteable($ressrc)) {
+ $removefrommap->{$url}{$token->[2]->{'id'}} = $ressrc;
+ next;
+ }
+ }
+ if ($ressrc =~ m{^/(res|uploaded)/.+\.(sequence|page)$}) {
+ if ($1 eq 'uploaded') {
+ $hierarchy->{$url}{$token->[2]->{'id'}} = $ressrc;
+ $titles->{$url}{$token->[2]->{'id'}} = $token->[2]->{'title'};
+ } else {
+ if ($allmaps->{$ressrc}) {
+ $removefrommap->{$url}{$token->[2]->{'id'}} = $ressrc;
+ } elsif (ref($addedmaps->{$ressrc}) eq 'ARRAY') {
+ $removefrommap->{$url}{$token->[2]->{'id'}} = $ressrc;
+ } else {
+ $addedmaps->{$ressrc} = [$url];
+ }
+ }
+ &contained_map_check($ressrc,$folder,$coursenum,$coursedom,$removefrommap,
+ $removeparam,$addedmaps,$hierarchy,$titles,$allmaps);
+ }
+ } elsif ($token->[1] eq 'param') {
+ if ($folder =~ /^supplemental/) {
+ if (ref($removeparam->{$url}{$token->[2]->{'to'}}) eq 'ARRAY') {
+ push(@{$removeparam->{$url}{$token->[2]->{'to'}}},$token->[2]->{'name'});
+ } else {
+ $removeparam->{$url}{$token->[2]->{'to'}} = [$token->[2]->{'name'}];
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub url_paste_fixups {
+ my ($oldurl,$folder,$prefixchg,$cdom,$cnum,$fromcdom,$fromcnum,$allmaps,
+ $rewrites,$retitles,$copies,$dbcopies,$zombies,$params,$mapmoves,
+ $mapchanges,$tomove,$newsubdir,$newurls,$resdatacopy) = @_;
+ my $checktitle;
+ if (($prefixchg) &&
+ ($oldurl =~ m{^/uploaded/$match_domain/$match_courseid/supplemental})) {
+ $checktitle = 1;
+ }
+ my $skip;
+ if ($oldurl =~ m{^\Q/uploaded/$cdom/$cnum/\E(default|supplemental)(_?\d*)\.(?:page|sequence)$}) {
+ my $mapid = $1.$2;
+ if ($tomove->{$mapid}) {
+ $skip = 1;
+ }
+ }
+ my $file = &Apache::lonnet::getfile($oldurl);
+ return if ($file eq '-1');
+ my $parser = HTML::TokeParser->new(\$file);
+ $parser->attr_encoded(1);
+ my $changed = 0;
+ while (my $token = $parser->get_token) {
+ next if ($token->[0] ne 'S');
+ if ($token->[1] eq 'resource') {
+ my $ressrc = $token->[2]->{'src'};
+ next if ($ressrc eq '');
+ my $id = $token->[2]->{'id'};
+ my $title = $token->[2]->{'title'};
+ if ($checktitle) {
+ if ($title =~ m{\d+\Q___&&&___\E$match_username\Q___&&&___\E$match_domain\Q___&&&___\E(.+)$}) {
+ $retitles->{$oldurl}{$id} = $ressrc;
+ }
+ }
+ next if ($token->[2]->{'type'} eq 'external');
+ if ($token->[2]->{'type'} eq 'zombie') {
+ next if ($skip);
+ $zombies->{$oldurl}{$id} = $ressrc;
+ $changed = 1;
+ } elsif ($ressrc =~ m{^/uploaded/($match_domain)/($match_courseid)/(.+)$}) {
+ my $srcdom = $1;
+ my $srcnum = $2;
+ my $rem = $3;
+ my $newurl;
+ my $mapname;
+ if ($rem =~ /^(default|supplemental)(_?\d*).(sequence|page)$/) {
+ my $prefix = $1;
+ $mapname = $prefix.$2;
+ if ($tomove->{$mapname}) {
+ &url_paste_fixups($ressrc,$folder,$prefixchg,$cdom,$cnum,
+ $srcdom,$srcnum,$allmaps,$rewrites,
+ $retitles,$copies,$dbcopies,$zombies,
+ $params,$mapmoves,$mapchanges,$tomove,
+ $newsubdir,$newurls,$resdatacopy);
+ next;
+ } else {
+ ($newurl,my $error) =
+ &get_newmap_url($ressrc,$folder,$prefixchg,$cdom,$cnum,
+ $srcdom,$srcnum,\$title,$allmaps,$newurls);
+ if ($newurl =~ /(?:default|supplemental)_(\d+)\.(?:sequence|page)$/) {
+ $newsubdir->{$ressrc} = $1;
+ }
+ if ($error) {
+ next;
+ }
+ }
+ }
+ if (($srcdom ne $cdom) || ($srcnum ne $cnum) || ($prefixchg) ||
+ ($mapchanges->{$oldurl}) || (($newurl ne '') && ($newurl ne $oldurl))) {
+
+ if ($rem =~ /^(default|supplemental)(_?\d*).(sequence|page)$/) {
+ $rewrites->{$oldurl}{$id} = $ressrc;
+ $mapchanges->{$ressrc} = 1;
+ unless (&url_paste_fixups($ressrc,$folder,$prefixchg,$cdom,
+ $cnum,$srcdom,$srcnum,$allmaps,
+ $rewrites,$retitles,$copies,$dbcopies,
+ $zombies,$params,$mapmoves,$mapchanges,
+ $tomove,$newsubdir,$newurls,$resdatacopy)) {
+ $mapmoves->{$ressrc} = 1;
+ }
+ $changed = 1;
+ } else {
+ $rewrites->{$oldurl}{$id} = $ressrc;
+ $copies->{$oldurl}{$ressrc} = $id;
+ $changed = 1;
+ }
+ }
+ } elsif ($ressrc =~ m{^/adm/($match_domain)/($match_courseid)/.+$}) {
+ next if ($skip);
+ my $srcdom = $1;
+ my $srcnum = $2;
+ if (($srcdom ne $cdom) || ($srcnum ne $cnum)) {
+ $rewrites->{$oldurl}{$id} = $ressrc;
+ $dbcopies->{$oldurl}{$id}{'src'} = $ressrc;
+ $dbcopies->{$oldurl}{$id}{'cdom'} = $srcdom;
+ $dbcopies->{$oldurl}{$id}{'cnum'} = $srcnum;
+ $changed = 1;
+ }
+ } elsif ($ressrc =~ m{^/adm/$match_domain/$match_username/\d+/(smppg|bulletinboard)$}) {
+ if (($fromcdom ne $cdom) || ($fromcnum ne $cnum) ||
+ ($env{'form.docs.markedcopy_options'} ne 'move')) {
+ $dbcopies->{$oldurl}{$id}{'src'} = $ressrc;
+ $dbcopies->{$oldurl}{$id}{'cdom'} = $fromcdom;
+ $dbcopies->{$oldurl}{$id}{'cnum'} = $fromcnum;
+ $changed = 1;
+ }
+ } elsif ($ressrc eq '/res/lib/templates/simpleproblem.problem') {
+ if (($fromcdom ne $cdom) || ($fromcnum ne $cnum)) {
+ $resdatacopy->{$oldurl}{$id}{'src'} = $ressrc;
+ $resdatacopy->{$oldurl}{$id}{'cdom'} = $fromcdom;
+ $resdatacopy->{$oldurl}{$id}{'cnum'} = $fromcnum;
+ }
+ } elsif ($ressrc =~ m{^/public/($match_domain)/($match_courseid)/(.+)$}) {
+ next if ($skip);
+ my $srcdom = $1;
+ my $srcnum = $2;
+ if (($srcdom ne $cdom) || ($srcnum ne $cnum)) {
+ $dbcopies->{$oldurl}{$id}{'src'} = $ressrc;
+ $dbcopies->{$oldurl}{$id}{'cdom'} = $srcdom;
+ $dbcopies->{$oldurl}{$id}{'cnum'} = $srcnum;
+ $changed = 1;
+ }
+ }
+ } elsif ($token->[1] eq 'param') {
+ next if ($skip);
+ my $to = $token->[2]->{'to'};
+ if ($to ne '') {
+ if (ref($params->{$oldurl}{$to}) eq 'ARRAY') {
+ push(@{$params->{$oldurl}{$to}},$token->[2]->{'name'});
+ } else {
+ @{$params->{$oldurl}{$to}} = ($token->[2]->{'name'});
+ }
+ }
+ }
+ }
+ return $changed;
+}
+
+sub apply_fixups {
+ my ($folder,$is_map,$cdom,$cnum,$errors,$updated,$info,$moves,$prefixchg,
+ $oldurl,$url,$caller) = @_;
+ my (%rewrites,%zombies,%removefrommap,%removeparam,%dbcopies,%retitles,
+ %params,%newsubdir,%before,%after,%copies,%docmoves,%mapmoves,@msgs,
+ %resdatacopy,%lockerrors,$lockmsg);
+ if (ref($updated) eq 'HASH') {
+ if (ref($updated->{'rewrites'}) eq 'HASH') {
+ %rewrites = %{$updated->{'rewrites'}};
+ }
+ if (ref($updated->{'zombies'}) eq 'HASH') {
+ %zombies = %{$updated->{'zombies'}};
+ }
+ if (ref($updated->{'removefrommap'}) eq 'HASH') {
+ %removefrommap = %{$updated->{'removefrommap'}};
+ }
+ if (ref($updated->{'removeparam'}) eq 'HASH') {
+ %removeparam = %{$updated->{'removeparam'}};
+ }
+ if (ref($updated->{'dbcopies'}) eq 'HASH') {
+ %dbcopies = %{$updated->{'dbcopies'}};
+ }
+ if (ref($updated->{'retitles'}) eq 'HASH') {
+ %retitles = %{$updated->{'retitles'}};
+ }
+ if (ref($updated->{'resdatacopy'}) eq 'HASH') {
+ %resdatacopy = %{$updated->{'resdatacopy'}};
+ }
+ }
+ if (ref($info) eq 'HASH') {
+ if (ref($info->{'newsubdir'}) eq 'HASH') {
+ %newsubdir = %{$info->{'newsubdir'}};
+ }
+ if (ref($info->{'params'}) eq 'HASH') {
+ %params = %{$info->{'params'}};
+ }
+ if (ref($info->{'before'}) eq 'HASH') {
+ %before = %{$info->{'before'}};
+ }
+ if (ref($info->{'after'}) eq 'HASH') {
+ %after = %{$info->{'after'}};
+ }
+ }
+ if (ref($moves) eq 'HASH') {
+ if (ref($moves->{'copies'}) eq 'HASH') {
+ %copies = %{$moves->{'copies'}};
+ }
+ if (ref($moves->{'docmoves'}) eq 'HASH') {
+ %docmoves = %{$moves->{'docmoves'}};
+ }
+ if (ref($moves->{'mapmoves'}) eq 'HASH') {
+ %mapmoves = %{$moves->{'mapmoves'}};
+ }
+ }
+ foreach my $key (keys(%copies),keys(%docmoves)) {
+ my @allcopies;
+ if (exists($copies{$key})) {
+ if (ref($copies{$key}) eq 'HASH') {
+ my %added;
+ foreach my $innerkey (keys(%{$copies{$key}})) {
+ if (($innerkey ne '') && (!$added{$innerkey})) {
+ push(@allcopies,$innerkey);
+ $added{$innerkey} = 1;
+ }
+ }
+ undef(%added);
+ }
+ }
+ if ($key eq $oldurl) {
+ if ((exists($docmoves{$key}))) {
+ unless (grep(/^\Q$oldurl\E$/,@allcopies)) {
+ push(@allcopies,$oldurl);
+ }
+ }
+ }
+ if (@allcopies > 0) {
+ foreach my $item (@allcopies) {
+ my ($relpath,$oldsubdir,$fname) =
+ ($item =~ m{^(/uploaded/$match_domain/$match_courseid/(?:docs|supplemental)/(default|\d+)/.*/)([^/]+)$});
+ if ($fname ne '') {
+ my $content = &Apache::lonnet::getfile($item);
+ unless ($content eq '-1') {
+ my $storefn;
+ if (($key eq $oldurl) && (exists($docmoves{$key}))) {
+ $storefn = $docmoves{$key};
+ } else {
+ $storefn = $relpath;
+ $storefn =~s{^/uploaded/$match_domain/$match_courseid/}{};
+ if ($prefixchg && $before{'doc'} && $after{'doc'}) {
+ $storefn =~ s/^\Q$before{'doc'}\E/$after{'doc'}/;
+ }
+ if ($newsubdir{$key}) {
+ $storefn =~ s#^(docs|supplemental)/\Q$oldsubdir\E/#$1/$newsubdir{$key}/#;
+ }
+ }
+ ©_dependencies($item,$storefn,$relpath,$errors,\$content);
+ my $copyurl =
+ &Apache::lonclonecourse::writefile($env{'request.course.id'},
+ $storefn.$fname,$content);
+ if ($copyurl eq '/adm/notfound.html') {
+ if (exists($docmoves{$oldurl})) {
+ return &mt('Paste failed: an error occurred copying the file.');
+ } elsif (ref($errors) eq 'HASH') {
+ $errors->{$item} = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ foreach my $key (keys(%mapmoves)) {
+ my $storefn=$key;
+ $storefn=~s{^/uploaded/$match_domain/$match_courseid/}{};
+ if ($prefixchg && $before{'map'} && $after{'map'}) {
+ $storefn =~ s/^\Q$before{'map'}\E/$after{'map'}/;
+ }
+ if ($newsubdir{$key}) {
+ $storefn =~ s/^((?:default|supplemental)_)(\d+)/$1$newsubdir{$key}/;
+ }
+ my $mapcontent = &Apache::lonnet::getfile($key);
+ if ($mapcontent eq '-1') {
+ if (ref($errors) eq 'HASH') {
+ $errors->{$key} = 1;
+ }
+ } else {
+ my $newmap =
+ &Apache::lonclonecourse::writefile($env{'request.course.id'},$storefn,
+ $mapcontent);
+ if ($newmap eq '/adm/notfound.html') {
+ if (ref($errors) eq 'HASH') {
+ $errors->{$key} = 1;
+ }
+ }
+ }
+ }
+ my %updates;
+ if ($is_map) {
+ if (ref($updated) eq 'HASH') {
+ foreach my $type (keys(%{$updated})) {
+ if (ref($updated->{$type}) eq 'HASH') {
+ foreach my $key (keys(%{$updated->{$type}})) {
+ $updates{$key} = 1;
+ }
+ }
+ }
+ }
+ foreach my $key (keys(%updates)) {
+ my (%torewrite,%toretitle,%toremove,%remparam,%currparam,%zombie,%newdb);
+ if (ref($rewrites{$key}) eq 'HASH') {
+ %torewrite = %{$rewrites{$key}};
+ }
+ if (ref($retitles{$key}) eq 'HASH') {
+ %toretitle = %{$retitles{$key}};
+ }
+ if (ref($removefrommap{$key}) eq 'HASH') {
+ %toremove = %{$removefrommap{$key}};
+ }
+ if (ref($removeparam{$key}) eq 'HASH') {
+ %remparam = %{$removeparam{$key}};
+ }
+ if (ref($zombies{$key}) eq 'HASH') {
+ %zombie = %{$zombies{$key}};
+ }
+ if (ref($dbcopies{$key}) eq 'HASH') {
+ foreach my $idx (keys(%{$dbcopies{$key}})) {
+ if (ref($dbcopies{$key}{$idx}) eq 'HASH') {
+ my ($newurl,$result,$errtext) =
+ &dbcopy($dbcopies{$key}{$idx},$cdom,$cnum,\%lockerrors);
+ if ($result eq 'ok') {
+ $newdb{$idx} = $newurl;
+ } elsif (ref($errors) eq 'HASH') {
+ $errors->{$key} = 1;
+ }
+ push(@msgs,$errtext);
+ }
+ }
+ }
+ if (ref($resdatacopy{$key}) eq 'HASH') {
+ if ($newsubdir{$key}) {
+
+ }
+ foreach my $idx (keys(%{$resdatacopy{$key}})) {
+ if (ref($resdatacopy{$key}{$idx}) eq 'HASH') {
+ my $srcurl = $resdatacopy{$key}{$idx}{'src'};
+ if ($srcurl =~ m{^/res/lib/templates/(\w+)\.problem$}) {
+ my $template = $1;
+ if (($resdatacopy{$key}{$idx}{'cdom'} =~ /^$match_domain$/) &&
+ ($resdatacopy{$key}{$idx}{'cnum'} =~ /^$match_courseid$/)) {
+ my $srcdom = $resdatacopy{$key}{$idx}{'cdom'};
+ my $srcnum = $resdatacopy{$key}{$idx}{'cnum'};
+ my ($newmapname) = ($key =~ m{/([^/]+)$});
+ my ($srcfolder,$srccontainer) = split(/\./,$newmapname);
+ my $srcmapinfo = $srcfolder.':'.$idx;
+ if ($srccontainer eq 'page') {
+ $srcmapinfo .= ':1';
+ }
+ if ($newsubdir{$key}) {
+ $newmapname =~ s/^((?:default|supplemental)_)(\d+)/$1$newsubdir{$key}/;
+ }
+ ©_templated_files($srcurl,$srcdom,$srcnum,$srcmapinfo,$cdom,
+ $cnum,$template,$idx,$newmapname);
+ }
+ }
+ }
+ }
+ }
+ if (ref($params{$key}) eq 'HASH') {
+ %currparam = %{$params{$key}};
+ }
+ my ($errtext,$fatal) = &LONCAPA::map::mapread($key);
+ if ($fatal) {
+ return ($errtext);
+ }
+ for (my $i=0; $i<@LONCAPA::map::zombies; $i++) {
+ if (defined($LONCAPA::map::zombies[$i])) {
+ my ($title,$src,$ext,$type)=split(/\:/,$LONCAPA::map::zombies[$i]);
+ if ($zombie{$i} eq $src) {
+ undef($LONCAPA::map::zombies[$i]);
+ }
+ }
+ }
+ my $total = scalar(@LONCAPA::map::order) - 1;
+ for (my $i=$total; $i>=0; $i--) {
+ my $idx = $LONCAPA::map::order[$i];
+ if (defined($LONCAPA::map::resources[$idx])) {
+ my $changed;
+ my ($title,$src,$ext,$type)=split(/\:/,$LONCAPA::map::resources[$idx]);
+ if ((exists($toremove{$idx})) &&
+ ($toremove{$idx} eq &LONCAPA::map::qtescape($src))) {
+ splice(@LONCAPA::map::order,$i,1);
+ if (ref($currparam{$idx}) eq 'ARRAY') {
+ foreach my $name (@{$currparam{$idx}}) {
+ &LONCAPA::map::delparameter($idx,$name);
+ }
+ }
+ next;
+ }
+ my $origsrc = $src;
+ if ((exists($toretitle{$idx})) && ($toretitle{$idx} eq $src)) {
+ if ($title =~ m{^\d+\Q___&&&___\E$match_username\Q___&&&___\E$match_domain\Q___&&&___\E(.+)$}) {
+ $changed = 1;
+ }
+ }
+ if ((exists($torewrite{$idx})) && ($torewrite{$idx} eq $src)) {
+ $src =~ s{^/(uploaded|adm|public)/$match_domain/$match_courseid/}{/$1/$cdom/$cnum/};
+ if ($origsrc =~ m{^/uploaded/}) {
+ if ($prefixchg && $before{'map'} && $after{'map'}) {
+ if ($src =~ /\.(page|sequence)$/) {
+ $src =~ s#^(/uploaded/$match_domain/$match_courseid/)\Q$before{'map'}\E#$1$after{'map'}#;
+ } else {
+ $src =~ s#^(/uploaded/$match_domain/$match_courseid/)\Q$before{'doc'}\E#$1$after{'doc'}#;
+ }
+ }
+ if ($origsrc =~ /\.(page|sequence)$/) {
+ if ($newsubdir{$origsrc}) {
+ $src =~ s#^(/uploaded/$match_domain/$match_courseid/(?:default|supplemental)_)(\d+)#$1$newsubdir{$origsrc}#;
+ }
+ } elsif ($newsubdir{$key}) {
+ $src =~ s#^(/uploaded/$match_domain/$match_courseid/\w+/)(\d+)#$1$newsubdir{$key}#;
+ }
+ }
+ $changed = 1;
+ } elsif ($newdb{$idx} ne '') {
+ $src = $newdb{$idx};
+ $changed = 1;
+ }
+ if ($changed) {
+ $LONCAPA::map::resources[$idx] = join(':',($title,&LONCAPA::map::qtunescape($src),$ext,$type));
+ }
+ }
+ }
+ foreach my $idx (keys(%remparam)) {
+ if (ref($remparam{$idx}) eq 'ARRAY') {
+ foreach my $name (@{$remparam{$idx}}) {
+ &LONCAPA::map::delparameter($idx,$name);
+ }
+ }
+ }
+ if (values(%lockerrors) > 0) {
+ $lockmsg = join(' ',values(%lockerrors));
+ }
+ my $storefn;
+ if ($key eq $oldurl) {
+ $storefn = $url;
+ $storefn=~s{^/uploaded/$match_domain/$match_courseid/}{};
+ } else {
+ $storefn = $key;
+ $storefn=~s{^/uploaded/$match_domain/$match_courseid/}{};
+ if ($prefixchg && $before{'map'} && $after{'map'}) {
+ $storefn =~ s/^\Q$before{'map'}\E/$after{'map'}/;
+ }
+ if ($newsubdir{$key}) {
+ $storefn =~ s/^((?:default|supplemental)_)(\d+)/$1$newsubdir{$key}/;
+ }
+ }
+ my $report;
+ if ($folder !~ /^supplemental/) {
+ $report = 1;
+ }
+ (my $outtext,$errtext) =
+ &LONCAPA::map::storemap("/uploaded/$cdom/$cnum/$storefn",1,$report);
+ if ($errtext) {
+ if ($caller eq 'paste') {
+ return (&mt('Paste failed: an error occurred saving the folder or page.'));
+ }
+ }
+ }
+ }
+ return ('ok',\@msgs,$lockmsg);
+}
+
+sub copy_dependencies {
+ my ($item,$storefn,$relpath,$errors,$contentref) = @_;
+ my $content;
+ if (ref($contentref)) {
+ $content = $$contentref;
+ } else {
+ $content = &Apache::lonnet::getfile($item);
+ }
+ unless ($content eq '-1') {
+ my $mm = new File::MMagic;
+ my $mimetype = $mm->checktype_contents($content);
+ if ($mimetype eq 'text/html') {
+ my (%allfiles,%codebase,$state);
+ my $res = &Apache::lonnet::extract_embedded_items(undef,\%allfiles,\%codebase,\$content);
+ if ($res eq 'ok') {
+ my ($numexisting,$numpathchanges,$existing);
+ (undef,$numexisting,$numpathchanges,$existing) =
+ &Apache::loncommon::ask_for_embedded_content(
+ '/adm/coursedocs',$state,\%allfiles,\%codebase,
+ {'error_on_invalid_names' => 1,
+ 'ignore_remote_references' => 1,
+ 'docs_url' => $item,
+ 'context' => 'paste'});
+ if ($numexisting > 0) {
+ if (ref($existing) eq 'HASH') {
+ foreach my $dep (keys(%{$existing})) {
+ my $depfile = $dep;
+ unless ($depfile =~ m{^\Q$relpath\E}) {
+ $depfile = $relpath.$dep;
+ }
+ my $depcontent = &Apache::lonnet::getfile($depfile);
+ unless ($depcontent eq '-1') {
+ my $storedep = $dep;
+ $storedep =~ s{^\Q$relpath\E}{};
+ my $dep_url =
+ &Apache::lonclonecourse::writefile(
+ $env{'request.course.id'},
+ $storefn.$storedep,$depcontent);
+ if ($dep_url eq '/adm/notfound.html') {
+ if (ref($errors) eq 'HASH') {
+ $errors->{$depfile} = 1;
+ }
+ } else {
+ ©_dependencies($depfile,$storefn,$relpath,$errors,\$depcontent);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+my %parameter_type = ( 'randompick' => 'int_pos',
+ 'hiddenresource' => 'string_yesno',
+ 'encrypturl' => 'string_yesno',
+ 'randomorder' => 'string_yesno',);
+my $valid_parameters_re = join('|',keys(%parameter_type));
+# set parameters
+sub update_parameter {
+ if ($env{'form.changeparms'} eq 'all') {
+ my (@allidx,@allmapidx,%allchecked,%currchecked);
+ %allchecked = (
+ 'hiddenresource' => {},
+ 'encrypturl' => {},
+ 'randompick' => {},
+ 'randomorder' => {},
+ );
+ foreach my $which (keys(%allchecked)) {
+ $env{'form.all'.$which} =~ s/,$//;
+ if ($which eq 'randompick') {
+ foreach my $item (split(/,/,$env{'form.all'.$which})) {
+ my ($res,$value) = split(/:/,$item);
+ if ($value =~ /^\d+$/) {
+ $allchecked{$which}{$res} = $value;
+ }
+ }
+ } else {
+ if ($env{'form.all'.$which}) {
+ map { $allchecked{$which}{$_} = 1; } split(/,/,$env{'form.all'.$which});
+ }
+ }
+ }
+ my $haschanges = 0;
+ foreach my $res (@LONCAPA::map::order) {
+ my ($name,$url)=split(/\:/,$LONCAPA::map::resources[$res]);
+ $name=&LONCAPA::map::qtescape($name);
+ $url=&LONCAPA::map::qtescape($url);
+ next unless ($name && $url);
+ my $is_map;
+ if ($url =~ m{/uploaded/.+\.(page|sequence)$}) {
+ $is_map = 1;
+ }
+ foreach my $which (keys(%allchecked)) {
+ if (($which eq 'randompick' || $which eq 'randomorder')) {
+ next if (!$is_map);
+ }
+ my $oldvalue = 0;
+ my $newvalue = 0;
+ if ($allchecked{$which}{$res}) {
+ $newvalue = $allchecked{$which}{$res};
+ }
+ my $current = (&LONCAPA::map::getparameter($res,'parameter_'.$which))[0];
+ if ($which eq 'randompick') {
+ if ($current =~ /^(\d+)$/) {
+ $oldvalue = $1;
+ }
+ } else {
+ if ($current =~ /^yes$/i) {
+ $oldvalue = 1;
+ }
+ }
+ if ($oldvalue ne $newvalue) {
+ $haschanges = 1;
+ if ($newvalue) {
+ my $storeval = 'yes';
+ if ($which eq 'randompick') {
+ $storeval = $newvalue;
+ }
+ &LONCAPA::map::storeparameter($res,'parameter_'.$which,
+ $storeval,
+ $parameter_type{$which});
+ &remember_parms($res,$which,'set',$storeval);
+ } elsif ($oldvalue) {
+ &LONCAPA::map::delparameter($res,'parameter_'.$which);
+ &remember_parms($res,$which,'del');
+ }
+ }
+ }
+ }
+ return $haschanges;
+ } else {
+ my $haschanges = 0;
+ return $haschanges if ($env{'form.changeparms'} !~ /^($valid_parameters_re)$/);
+
+ my $which = $env{'form.changeparms'};
+ my $idx = $env{'form.setparms'};
+ my $oldvalue = 0;
+ my $newvalue = 0;
+ my $current = (&LONCAPA::map::getparameter($idx,'parameter_'.$which))[0];
+ if ($which eq 'randompick') {
+ if ($current =~ /^(\d+)$/) {
+ $oldvalue = $1;
+ }
+ } elsif ($current =~ /^yes$/i) {
+ $oldvalue = 1;
+ }
+ if ($env{'form.'.$which.'_'.$idx}) {
+ $newvalue = ($which eq 'randompick') ? $env{'form.rpicknum_'.$idx}
+ : 1;
+ }
+ if ($oldvalue ne $newvalue) {
+ $haschanges = 1;
+ if ($newvalue) {
+ my $storeval = 'yes';
+ if ($which eq 'randompick') {
+ $storeval = $newvalue;
+ }
+ &LONCAPA::map::storeparameter($idx, 'parameter_'.$which, $storeval,
+ $parameter_type{$which});
+ &remember_parms($idx,$which,'set',$storeval);
+ } else {
+ &LONCAPA::map::delparameter($idx,'parameter_'.$which);
+ &remember_parms($idx,$which,'del');
+ }
+ }
+ return $haschanges;
+ }
+}
+
+sub handle_edit_cmd {
+ my ($coursenum,$coursedom) =@_;
+ my $haschanges = 0;
+ if ($env{'form.cmd'} eq '') {
+ return $haschanges;
+ }
+ my ($cmd,$idx)=split('_',$env{'form.cmd'});
+
+ my $ratstr = $LONCAPA::map::resources[$LONCAPA::map::order[$idx]];
+ my ($title, $url, @rrest) = split(':', $ratstr);
+
+ if ($cmd eq 'remove') {
+ if (($url=~m|/+uploaded/\Q$coursedom\E/\Q$coursenum\E/|) &&
+ ($url!~/$LONCAPA::assess_page_seq_re/)) {
+ &Apache::lonnet::removeuploadedurl($url);
+ } else {
+ &LONCAPA::map::makezombie($LONCAPA::map::order[$idx]);
+ }
+ splice(@LONCAPA::map::order, $idx, 1);
+ $haschanges = 1;
+ } elsif ($cmd eq 'cut') {
+ &LONCAPA::map::makezombie($LONCAPA::map::order[$idx]);
+ splice(@LONCAPA::map::order, $idx, 1);
+ $haschanges = 1;
+ } elsif ($cmd eq 'up'
+ && ($idx) && (defined($LONCAPA::map::order[$idx-1]))) {
+ @LONCAPA::map::order[$idx-1,$idx] = @LONCAPA::map::order[$idx,$idx-1];
+ $haschanges = 1;
+ } elsif ($cmd eq 'down'
+ && defined($LONCAPA::map::order[$idx+1])) {
+ @LONCAPA::map::order[$idx+1,$idx] = @LONCAPA::map::order[$idx,$idx+1];
+ $haschanges = 1;
+ } elsif ($cmd eq 'rename') {
+ my $comment = &LONCAPA::map::qtunescape($env{'form.title'});
+ if ($comment=~/\S/) {
+ $LONCAPA::map::resources[$LONCAPA::map::order[$idx]]=
+ $comment.':'.join(':', $url, @rrest);
+ }
+# Devalidate title cache
+ my $renamed_url=&LONCAPA::map::qtescape($url);
+ &Apache::lonnet::devalidate_title_cache($renamed_url);
+ $haschanges = 1;
+ } elsif ($cmd eq 'setalias') {
+ my $newvalue = $env{'form.alias'};
+ if ($newvalue ne '') {
+ unless (Apache::lonnet::get_symb_from_alias($newvalue)) {
+ &LONCAPA::map::storeparameter($idx,'parameter_0_mapalias',$newvalue,
+ 'string');
+ &remember_parms($idx,'mapalias','set',$newvalue);
+ $haschanges = 1;
+ }
+ }
+ } elsif ($cmd eq 'delalias') {
+ my $current = (&LONCAPA::map::getparameter($idx,'parameter_0_mapalias'))[0];
+ if ($current ne '') {
+ &LONCAPA::map::delparameter($idx,'parameter_0_mapalias');
+ &remember_parms($idx,'mapalias','del');
+ $haschanges = 1;
+ }
+ }
+ return $haschanges;
+}
+
+sub editor {
+ my ($r,$coursenum,$coursedom,$folder,$allowed,$upload_output,$crstype,
+ $supplementalflag,$orderhash,$iconpath,$pathitem,$ltitoolsref,
+ $canedit,$hostname,$navmapref,$hiddentop)=@_;
+ my ($randompick,$ishidden,$isencrypted,$plain,$is_random_order,$container);
+ if ($allowed) {
+ (my $breadcrumbtrail,$randompick,$ishidden,$isencrypted,$plain,
+ $is_random_order,$container) =
+ &Apache::lonhtmlcommon::docs_breadcrumbs($allowed,$crstype,1);
+ $r->print($breadcrumbtrail);
+ } elsif ($env{'form.folderpath'} =~ /\:1$/) {
+ $container = 'page';
+ } else {
+ $container = 'sequence';
+ }
+
+ my $jumpto;
+
+ unless ($supplementalflag) {
+ $jumpto = "uploaded/$coursedom/$coursenum/$folder.$container";
+ }
+
+ unless ($allowed) {
+ $randompick = -1;
+ }
+
+ my ($errtext,$fatal);
+ if (($folder eq '') && (!$supplementalflag)) {
+ if (@LONCAPA::map::order) {
+ undef(@LONCAPA::map::order);
+ undef(@LONCAPA::map::resources);
+ undef(@LONCAPA::map::resparms);
+ undef(@LONCAPA::map::zombies);
+ }
+ $folder = 'default';
+ $container = 'sequence';
+ } else {
+ ($errtext,$fatal) = &mapread($coursenum,$coursedom,
+ $folder.'.'.$container);
+ return $errtext if ($fatal);
+ }
+
+ if ($#LONCAPA::map::order<1) {
+ my $idx=&LONCAPA::map::getresidx();
+ if ($idx<=0) { $idx=1; }
+ $LONCAPA::map::order[0]=$idx;
+ $LONCAPA::map::resources[$idx]='';
+ }
+
+# ------------------------------------------------------------ Process commands
+
+# ---------------- if they are for this folder and user allowed to make changes
+ if (($allowed && $canedit) && ($env{'form.folder'} eq $folder)) {
+# set parameters and change order
+ &snapshotbefore();
+
+ if (&update_parameter()) {
+ ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container,1);
+ return $errtext if ($fatal);
+ }
+
+ if ($env{'form.newpos'} && $env{'form.currentpos'}) {
+# change order
+ my $res = splice(@LONCAPA::map::order,$env{'form.currentpos'}-1,1);
+ splice(@LONCAPA::map::order,$env{'form.newpos'}-1,0,$res);
+
+ ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container);
+ return $errtext if ($fatal);
+ }
+
+ if ($env{'form.pastemarked'}) {
+ my %paste_errors;
+ my ($paste_res,$save_error,$pastemsgarray,$lockerror) =
+ &do_paste_from_buffer($coursenum,$coursedom,$folder,$container,
+ \%paste_errors);
+ if (ref($pastemsgarray) eq 'ARRAY') {
+ if (@{$pastemsgarray} > 0) {
+ $r->print(''.
+ join(' ',@{$pastemsgarray}).
+ '
');
+ }
+ }
+ if ($lockerror) {
+ $r->print(''.
+ $lockerror.
+ '
');
+ }
+ if ($save_error ne '') {
+ return $save_error;
+ }
+ if ($paste_res) {
+ my %errortext = &Apache::lonlocal::texthash (
+ fail => 'Storage of folder contents failed',
+ failread => 'Reading folder contents failed',
+ failstore => 'Storage of folder contents failed',
+ );
+ if ($errortext{$paste_res}) {
+ $r->print(''.$errortext{$paste_res}.'
');
+ }
+ }
+ if (keys(%paste_errors) > 0) {
+ $r->print(''."\n".
+ &mt('The following files are either dependencies of a web page or references within a folder and/or composite page which could not be copied during the paste operation:')."\n".
+ '
'."\n");
+ foreach my $key (sort(keys(%paste_errors))) {
+ $r->print(''.$key.' '."\n");
+ }
+ $r->print(' '."\n");
+ }
+ } elsif ($env{'form.clearmarked'}) {
+ my $output = &do_buffer_empty();
+ if ($output) {
+ $r->print(''.$output.'
');
+ }
+ }
+
+ $r->print($upload_output);
+
+# Rename, cut, copy or remove a single resource
+ if (&handle_edit_cmd($coursenum,$coursedom)) {
+ my $contentchg;
+ if ($env{'form.cmd'} =~ m{^(remove|cut|setalias|delalias)_}) {
+ $contentchg = 1;
+ }
+ ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container,$contentchg);
+ return $errtext if ($fatal);
+ }
+
+# Cut, copy and/or remove multiple resources
+ if ($env{'form.multichange'}) {
+ my %allchecked = (
+ cut => {},
+ remove => {},
+ );
+ my $needsupdate;
+ foreach my $which (keys(%allchecked)) {
+ $env{'form.multi'.$which} =~ s/,$//;
+ if ($env{'form.multi'.$which}) {
+ map { $allchecked{$which}{$_} = 1; } split(/,/,$env{'form.multi'.$which});
+ if (ref($allchecked{$which}) eq 'HASH') {
+ $needsupdate += scalar(keys(%{$allchecked{$which}}));
+ }
+ }
+ }
+ if ($needsupdate) {
+ my $haschanges = 0;
+ my %curr_groups = &Apache::longroup::coursegroups();
+ my $total = scalar(@LONCAPA::map::order) - 1;
+ for (my $i=$total; $i>=0; $i--) {
+ my $res = $LONCAPA::map::order[$i];
+ my ($name,$url)=split(/\:/,$LONCAPA::map::resources[$res]);
+ $name=&LONCAPA::map::qtescape($name);
+ $url=&LONCAPA::map::qtescape($url);
+ next unless $url;
+ my %denied =
+ &action_restrictions($coursenum,$coursedom,$url,
+ $env{'form.folderpath'},\%curr_groups);
+ foreach my $which (keys(%allchecked)) {
+ next if ($denied{$which});
+ next unless ($allchecked{$which}{$res});
+ if ($which eq 'remove') {
+ if (($url=~m|/+uploaded/\Q$coursedom\E/\Q$coursenum\E/|) &&
+ ($url!~/$LONCAPA::assess_page_seq_re/)) {
+ &Apache::lonnet::removeuploadedurl($url);
+ } else {
+ &LONCAPA::map::makezombie($res);
+ }
+ splice(@LONCAPA::map::order,$i,1);
+ $haschanges ++;
+ } elsif ($which eq 'cut') {
+ &LONCAPA::map::makezombie($res);
+ splice(@LONCAPA::map::order,$i,1);
+ $haschanges ++;
+ }
+ }
+ }
+ if ($haschanges) {
+ ($errtext,$fatal) =
+ &storemap($coursenum,$coursedom,$folder.'.'.$container,1);
+ return $errtext if ($fatal);
+ }
+ }
+ }
+
+# Group import/search
+ if ($env{'form.importdetail'}) {
+ my @imports;
+ foreach my $item (split(/\&/,$env{'form.importdetail'})) {
+ if (defined($item)) {
+ my ($name,$url,$residx)=
+ map { &unescape($_); } split(/\=/,$item);
+ if ($url =~ m{^\Q/uploaded/$coursedom/$coursenum/\E(default|supplemental)_new\.(sequence|page)$}) {
+ my ($suffix,$errortxt,$locknotfreed) =
+ &new_timebased_suffix($coursedom,$coursenum,'map',$1,$2);
+ if ($locknotfreed) {
+ $r->print($locknotfreed);
+ }
+ if ($suffix) {
+ $url =~ s/_new\./_$suffix./;
+ } else {
+ return $errortxt;
+ }
+ } elsif ($url =~ m{^/adm/$match_domain/$match_username/new/(smppg|bulletinboard)$}) {
+ my $type = $1;
+ my ($suffix,$errortxt,$locknotfreed) =
+ &new_timebased_suffix($coursedom,$coursenum,$type);
+ if ($locknotfreed) {
+ $r->print($locknotfreed);
+ }
+ if ($suffix) {
+ $url =~ s{^(/adm/$match_domain/$match_username)/new}{$1/$suffix};
+ } else {
+ return $errortxt;
+ }
+ } elsif ($url =~ m{^/adm/$coursedom/$coursenum/new/ext\.tool}) {
+ my ($suffix,$errortxt,$locknotfreed) =
+ &new_timebased_suffix($coursedom,$coursenum,'exttool');
+ if ($locknotfreed) {
+ $r->print($locknotfreed);
+ }
+ if ($suffix) {
+ $url =~ s{^(/adm/$coursedom/$coursenum)/new}{$1/$suffix};
+ } else {
+ return $errortxt;
+ }
+ } elsif ($url =~ m{^/uploaded/$coursedom/$coursenum/(docs|supplemental)/(default|\d+)/new.html$}) {
+ if ($supplementalflag) {
+ next unless ($1 eq 'supplemental');
+ if ($folder eq 'supplemental') {
+ next unless ($2 eq 'default');
+ } else {
+ next unless ($folder eq 'supplemental_'.$2);
+ }
+ } else {
+ next unless ($1 eq 'docs');
+ if ($folder eq 'default') {
+ next unless ($2 eq 'default');
+ } else {
+ next unless ($folder eq 'default_'.$2);
+ }
+ }
+ }
+ push(@imports, [$name, $url, $residx]);
+ }
+ }
+ ($errtext,$fatal,my $fixuperrors) =
+ &group_import($coursenum, $coursedom, $folder,$container,
+ 'londocs',$ltitoolsref,@imports);
+ return $errtext if ($fatal);
+ if ($fixuperrors) {
+ $r->print($fixuperrors);
+ }
+ }
+# Loading a complete map
+ if ($env{'form.loadmap'}) {
+ if ($env{'form.importmap'}=~/\w/) {
+ foreach my $res (&Apache::lonsequence::attemptread(&Apache::lonnet::filelocation('',$env{'form.importmap'}))) {
+ my ($title,$url,$ext,$type)=split(/\:/,$res);
+ my $idx=&LONCAPA::map::getresidx($url);
+ $LONCAPA::map::resources[$idx]=$res;
+ $LONCAPA::map::order[$#LONCAPA::map::order+1]=$idx;
+ }
+ ($errtext,$fatal)=&storemap($coursenum,$coursedom,
+ $folder.'.'.$container,1);
+ return $errtext if ($fatal);
+ } else {
+ $r->print(''.&mt('No map selected.').'
');
+
+ }
+ }
+ &log_differences($plain);
+ }
+# ---------------------------------------------------------------- End commands
+# ---------------------------------------------------------------- Print screen
+ my $idx=0;
+ my $shown=0;
+ if (($ishidden) || ($isencrypted) || ($randompick>=0) || ($is_random_order)) {
+ $r->print(''.
+ '
'.&mt('Parameters:').' '.
+ ($randompick>=0?''.&mt('randomly pick [quant,_1,resource]',$randompick).' ':'').
+ ($ishidden?''.&mt('contents hidden').' ':'').
+ ($isencrypted?''.&mt('URLs hidden').' ':'').
+ ($is_random_order?''.&mt('random order').' ':'').
+ '');
+ if ($randompick>=0) {
+ $r->print('
'
+ .&mt('Caution: this folder is set to randomly pick a subset'
+ .' of resources. Adding or removing resources from this'
+ .' folder will change the set of resources that the'
+ .' students see, resulting in spurious or missing credit'
+ .' for completed problems, not limited to ones you'
+ .' modify. Do not modify the contents of this folder if'
+ .' it is in active student use.')
+ .'
'
+ );
+ }
+ if ($is_random_order) {
+ $r->print('
'
+ .&mt('Caution: this folder is set to randomly order its'
+ .' contents. Adding or removing resources from this folder'
+ .' will change the order of resources shown.')
+ .'
'
+ );
+ }
+ $r->print('
');
+ }
+
+ my ($to_show,$output,@allidx,@allmapidx,%filters,%lists,%curr_groups);
+ %filters = (
+ canremove => [],
+ cancut => [],
+ cancopy => [],
+ hiddenresource => [],
+ encrypturl => [],
+ randomorder => [],
+ randompick => [],
+ );
+ %curr_groups = &Apache::longroup::coursegroups();
+ &Apache::loncommon::start_data_table_count(); #setup a row counter
+ foreach my $res (@LONCAPA::map::order) {
+ my ($name,$url)=split(/\:/,$LONCAPA::map::resources[$res]);
+ $name=&LONCAPA::map::qtescape($name);
+ $url=&LONCAPA::map::qtescape($url);
+ unless ($name) { $name=(split(/\//,$url))[-1]; }
+ unless ($name) { $idx++; next; }
+ push(@allidx,$res);
+ if ($url =~ m{/uploaded/.+\.(page|sequence)$}) {
+ push(@allmapidx,$res);
+ }
+
+ $output .= &entryline($idx,$name,$url,$folder,$allowed,$res,
+ $coursenum,$coursedom,$crstype,
+ $pathitem,$supplementalflag,$container,
+ \%filters,\%curr_groups,$ltitoolsref,$canedit,
+ $isencrypted,$navmapref,$hostname);
+ $idx++;
+ $shown++;
+ }
+ &Apache::loncommon::end_data_table_count();
+
+ my $need_save;
+ if ($allowed || ($supplementalflag && $folder eq 'supplemental')) {
+ my $toolslink;
+ if ($allowed) {
+ $toolslink = ''
+ .&Apache::loncommon::help_open_menu('Navigation Screen',
+ 'Navigation_Screen',undef,'RAT')
+ .' '.&mt('Tools:').' '
+ .'
';
+ }
+ if ($shown) {
+ if ($allowed) {
+ $to_show = &Apache::loncommon::start_scrollbox('900px','880px','400px','contentscroll')
+ .&Apache::loncommon::start_data_table(undef,'contentlist')
+ .&Apache::loncommon::start_data_table_header_row()
+ .''.&mt('Move').' '
+ .''.&mt('Actions').' '
+ .''.&mt('Document').' ';
+ if ($folder !~ /^supplemental/) {
+ $to_show .= ''.&mt('Settings').' ';
+ }
+ $to_show .= &Apache::loncommon::end_data_table_header_row();
+ if ($folder !~ /^supplemental/) {
+ $lists{'canhide'} = join(',',@allidx);
+ $lists{'canrandomlyorder'} = join(',',@allmapidx);
+ my @possfilters = ('canremove','cancut','cancopy','hiddenresource','encrypturl',
+ 'randomorder','randompick');
+ foreach my $item (@possfilters) {
+ if (ref($filters{$item}) eq 'ARRAY') {
+ if (@{$filters{$item}} > 0) {
+ $lists{$item} = join(',',@{$filters{$item}});
+ }
+ }
+ }
+ if (@allidx > 0) {
+ my $path;
+ if ($env{'form.folderpath'}) {
+ $path =
+ &HTML::Entities::encode($env{'form.folderpath'},'<>&"');
+ }
+ if (@allidx > 1) {
+ $to_show .=
+ &Apache::loncommon::continue_data_table_row().
+ ' '.
+ ''.
+ &multiple_check_form('actions',\%lists,$canedit).
+ ' '.
+ ' '.
+ ''.
+ &multiple_check_form('settings',\%lists,$canedit).
+ ' '.
+ &Apache::loncommon::end_data_table_row();
+ $need_save = 1;
+ }
+ }
+ }
+ $to_show .= $output.' '
+ .&Apache::loncommon::end_data_table()
+ .' '
+ .&Apache::loncommon::end_scrollbox();
+ } else {
+ $to_show .= $toolslink
+ .&Apache::loncommon::start_data_table('LC_tableOfContent')
+ .$output.' '
+ .&Apache::loncommon::end_data_table();
+ }
+ } else {
+ if (!$allowed) {
+ $to_show .= $toolslink;
+ }
+ my $noresmsg;
+ if ($allowed && $hiddentop && !$supplementalflag) {
+ $noresmsg = &mt('Main Content Hidden');
+ } else {
+ $noresmsg = &mt('Currently empty');
+ }
+ $to_show .= &Apache::loncommon::start_scrollbox('400px','380px','200px','contentscroll')
+ .''
+ .$noresmsg
+ .'
'
+ .&Apache::loncommon::end_scrollbox();
+ }
+ } else {
+ if ($shown) {
+ $to_show = ''
+ .&Apache::loncommon::start_data_table('LC_tableOfContent')
+ .$output
+ .&Apache::loncommon::end_data_table()
+ .'
';
+ } else {
+ $to_show = ''
+ .&mt('Currently empty')
+ .'
'
+ }
+ }
+ my $tid = 1;
+ if ($supplementalflag) {
+ $tid = 2;
+ }
+ if ($allowed) {
+ my $readfile="/uploaded/$coursedom/$coursenum/$folder.$container";
+ $r->print(&generate_edit_table($tid,$orderhash,$to_show,$iconpath,
+ $jumpto,$readfile,$need_save,"$folder.$container",$canedit));
+ if ($canedit) {
+ &print_paste_buffer($r,$container,$folder,$coursedom,$coursenum);
+ }
+ } else {
+ $r->print($to_show);
+ }
+ return;
+}
+
+sub multiple_check_form {
+ my ($caller,$listsref,$canedit) = @_;
+ return unless (ref($listsref) eq 'HASH');
+ my $disabled;
+ unless ($canedit) {
+ $disabled = 'disabled="disabled"';
+ }
+ my $output =
+ ''.
+ '';
+ return $output;
+}
+
+sub process_file_upload {
+ my ($upload_output,$coursenum,$coursedom,$allfiles,$codebase,$uploadcmd,$crstype) = @_;
+# upload a file, if present
+ my $filesize = length($env{'form.uploaddoc'});
+ if (!$filesize) {
+ $$upload_output = ''.
+ &mt('Unable to upload [_1]. (size = [_2] bytes)',
+ ''.$env{'form.uploaddoc.filename'}.' ',
+ $filesize).' '.
+ &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').' '.
+ '
';
+ return;
+ }
+ my $quotatype = 'unofficial';
+ if ($crstype eq 'Community') {
+ $quotatype = 'community';
+ } elsif ($crstype eq 'Placement') {
+ $quotatype = 'placement';
+ } elsif ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.coursecode'}) {
+ $quotatype = 'official';
+ } elsif ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.textbook'}) {
+ $quotatype = 'textbook';
+ }
+ if (&Apache::loncommon::get_user_quota($coursenum,$coursedom,'course',$quotatype)) {
+ $filesize = int($filesize/1000); #expressed in kb
+ $$upload_output = &Apache::loncommon::excess_filesize_warning($coursenum,$coursedom,'course',
+ $env{'form.uploaddoc.filename'},$filesize,
+ 'upload',$quotatype);
+ return if ($$upload_output);
+ }
+ my ($parseaction,$showupload,$nextphase,$mimetype);
+ if ($env{'form.parserflag'}) {
+ $parseaction = 'parse';
+ }
+ my $folder=$env{'form.folder'};
+ if ($folder eq '') {
+ $folder='default';
+ }
+ if ( ($folder=~/^$uploadcmd/) || ($uploadcmd eq 'default') ) {
+ my $errtext='';
+ my $fatal=0;
+ my $container='sequence';
+ if ($env{'form.folderpath'} =~ /:1$/) {
+ $container='page';
+ }
+ ($errtext,$fatal)=
+ &mapread($coursenum,$coursedom,$folder.'.'.$container);
+ if ($#LONCAPA::map::order<1) {
+ $LONCAPA::map::order[0]=1;
+ $LONCAPA::map::resources[1]='';
+ }
+ my $destination = 'docs/';
+ if ($folder =~ /^supplemental/) {
+ $destination = 'supplemental/';
+ }
+ if (($folder eq 'default') || ($folder eq 'supplemental')) {
+ $destination .= 'default/';
+ } elsif ($folder =~ /^(default|supplemental)_(\d+)$/) {
+ $destination .= $2.'/';
+ }
+ if ($fatal) {
+ $$upload_output = ''.&mt('The uploaded file has not been stored as an error occurred reading the contents of the current folder.').'
';
+ return;
+ }
+# this is for a course, not a user, so set context to coursedoc.
+ my $newidx=&LONCAPA::map::getresidx();
+ $destination .= $newidx;
+ my $url=&Apache::lonnet::userfileupload('uploaddoc','coursedoc',$destination,
+ $parseaction,$allfiles,
+ $codebase,undef,undef,undef,undef,
+ undef,undef,\$mimetype);
+ if ($url =~ m{^/uploaded/\Q$coursedom\E/\Q$coursenum\E.*/([^/]+)$}) {
+ my $stored = $1;
+ $showupload = ''.&mt('Uploaded [_1]',''.
+ $stored.' ').'
';
+ } else {
+ my ($filename) = ($env{'form.uploaddoc.filename'} =~ m{([^/]+)$});
+
+ $$upload_output = ''.&mt('Unable to save file [_1].',''.$filename.' ').'
';
+ return;
+ }
+ my $ext='false';
+ if ($url=~m{^http://}) { $ext='true'; }
+ $url = &LONCAPA::map::qtunescape($url);
+ my $comment=$env{'form.comment'};
+ $comment = &LONCAPA::map::qtunescape($comment);
+ if ($folder=~/^supplemental/) {
+ $comment=time.'___&&&___'.$env{'user.name'}.'___&&&___'.
+ $env{'user.domain'}.'___&&&___'.$comment;
+ }
+
+ $LONCAPA::map::resources[$newidx]=
+ $comment.':'.$url.':'.$ext.':normal:res';
+ $LONCAPA::map::order[$#LONCAPA::map::order+1]= $newidx;
+ ($errtext,$fatal)=&storemap($coursenum,$coursedom,
+ $folder.'.'.$container,1);
+ if ($fatal) {
+ $$upload_output = ''.$errtext.'
';
+ return;
+ } else {
+ if ($parseaction eq 'parse' && $mimetype eq 'text/html') {
+ $$upload_output = $showupload;
+ my $total_embedded = scalar(keys(%{$allfiles}));
+ if ($total_embedded > 0) {
+ my $uploadphase = 'upload_embedded';
+ my $primaryurl = &HTML::Entities::encode($url,'<>&"');
+ my $state = &embedded_form_elems($uploadphase,$primaryurl,$newidx);
+ my ($embedded,$num) =
+ &Apache::loncommon::ask_for_embedded_content(
+ '/adm/coursedocs',$state,$allfiles,$codebase,{'docs_url' => $url});
+ if ($embedded) {
+ if ($num) {
+ $$upload_output .=
+ ''.&mt('This file contains embedded multimedia objects, which need to be uploaded.').'
'.$embedded;
+ $nextphase = $uploadphase;
+ } else {
+ $$upload_output .= $embedded;
+ }
+ } else {
+ $$upload_output .= &mt('Embedded item(s) already present, so no additional upload(s) required').' ';
+ }
+ } else {
+ $$upload_output .= &mt('No embedded items identified').' ';
+ }
+ $$upload_output = ''.$$upload_output.'
';
+ } elsif ((&Apache::loncommon::is_archive_file($mimetype)) &&
+ ($env{'form.uploaddoc.filename'} =~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/i)) {
+ $nextphase = 'decompress_uploaded';
+ my $position = scalar(@LONCAPA::map::order)-1;
+ my $noextract = &return_to_editor();
+ my $archiveurl = &HTML::Entities::encode($url,'<>&"');
+ my %archiveitems = (
+ folderpath => $env{'form.folderpath'},
+ cmd => $nextphase,
+ newidx => $newidx,
+ position => $position,
+ phase => $nextphase,
+ comment => $comment,
+ );
+ my ($destination,$dir_root) = &embedded_destination($coursenum,$coursedom);
+ my @current = &get_dir_list($url,$coursenum,$coursedom,$newidx);
+ $$upload_output = $showupload.
+ &Apache::loncommon::decompress_form($mimetype,
+ $archiveurl,'/adm/coursedocs',$noextract,
+ \%archiveitems,\@current);
+ }
+ }
+ }
+ return $nextphase;
+}
+
+sub get_dir_list {
+ my ($url,$coursenum,$coursedom,$newidx) = @_;
+ my ($destination,$dir_root) = &embedded_destination();
+ my ($dirlistref,$listerror) =
+ &Apache::lonnet::dirlist("$dir_root/$destination/$newidx",$coursedom,$coursenum,1);
+ my @dir_lines;
+ my $dirptr=16384;
+ if (ref($dirlistref) eq 'ARRAY') {
+ foreach my $dir_line (sort
+ {
+ my ($afile)=split('&',$a,2);
+ my ($bfile)=split('&',$b,2);
+ return (lc($afile) cmp lc($bfile));
+ } (@{$dirlistref})) {
+ my ($filename,$dom,undef,$testdir,undef,undef,undef,undef,$size,undef,$mtime,undef,undef,undef,$obs,undef)=split(/\&/,$dir_line,16);
+ $filename =~ s/\s+$//;
+ next if ($filename =~ /^\.\.?$/);
+ my $isdir = 0;
+ if ($dirptr&$testdir) {
+ $isdir = 1;
+ }
+ push(@dir_lines, [$filename,$dom,$isdir,$size,$mtime,$obs]);
+ }
+ }
+ return @dir_lines;
+}
+
+sub is_supplemental_title {
+ my ($title) = @_;
+ return scalar($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/);
+}
+
+# --------------------------------------------------------------- An entry line
+
+sub entryline {
+ my ($index,$title,$url,$folder,$allowed,$residx,$coursenum,$coursedom,
+ $crstype,$pathitem,$supplementalflag,$container,$filtersref,$currgroups,
+ $ltitoolsref,$canedit,$isencrypted,$navmapref,$hostname)=@_;
+ my ($foldertitle,$renametitle,$oldtitle);
+ if (&is_supplemental_title($title)) {
+ ($title,$foldertitle,$renametitle) = &Apache::loncommon::parse_supplemental_title($title);
+ } else {
+ $title=&HTML::Entities::encode($title,'"<>&\'');
+ $renametitle=$title;
+ $foldertitle=$title;
+ }
+
+ my ($disabled,$readonly,$js_lt);
+ unless ($canedit) {
+ $disabled = 'disabled="disabled"';
+ $readonly = 1;
+ }
+
+ my $orderidx=$LONCAPA::map::order[$index];
+
+ $renametitle=~s/\\/\\\\/g;
+ $renametitle=~s/\"\;/\\\"/g;
+ $renametitle=~s/"/%22/g;
+ $renametitle=~s/ /%20/g;
+ $oldtitle = $renametitle;
+ $renametitle=~s/\'/\\\'/g;
+ my $line=&Apache::loncommon::start_data_table_row();
+ my ($form_start,$form_end,$form_common,$form_param);
+# Edit commands
+ my ($esc_path, $path, $symb, $curralias);
+ if ($env{'form.folderpath'}) {
+ $esc_path=&escape($env{'form.folderpath'});
+ $path = &HTML::Entities::encode($env{'form.folderpath'},'<>&"');
+ # $htmlfoldername=&HTML::Entities::encode($env{'form.foldername'},'<>&"');
+ }
+ my $isexternal;
+ if ($residx) {
+ my $currurl = $url;
+ $currurl =~ s{^http(|s)(:|:)//}{/adm/wrapper/ext/};
+ if ($currurl =~ m{^/adm/wrapper/ext/}) {
+ $isexternal = 1;
+ }
+ if (!$supplementalflag) {
+ my $path = 'uploaded/'.
+ $env{'course.'.$env{'request.course.id'}.'.domain'}.'/'.
+ $env{'course.'.$env{'request.course.id'}.'.num'}.'/';
+ $symb = &Apache::lonnet::encode_symb($path.$folder.".$container",
+ $residx,
+ &Apache::lonnet::declutter($currurl));
+ }
+ }
+ my ($renamelink,%lt,$ishash);
+ if (ref($filtersref) eq 'HASH') {
+ $ishash = 1;
+ }
+
+ if ($allowed) {
+ $form_start = '
+ ';
+
+ my $incindex=$index+1;
+ my $selectbox='';
+ if (($#LONCAPA::map::order>0) &&
+ ((split(/\:/,
+ $LONCAPA::map::resources[$LONCAPA::map::order[0]]))[1]
+ ne '') &&
+ ((split(/\:/,
+ $LONCAPA::map::resources[$LONCAPA::map::order[1]]))[1]
+ ne '')) {
+ $selectbox=
+ ' '.
+ '';
+ for (my $i=1;$i<=$#LONCAPA::map::order+1;$i++) {
+ if ($i==$incindex) {
+ $selectbox.='('.$i.') ';
+ } else {
+ $selectbox.=''.$i.' ';
+ }
+ }
+ $selectbox.=' ';
+ }
+ %lt=&Apache::lonlocal::texthash(
+ 'up' => 'Move Up',
+ 'dw' => 'Move Down',
+ 'rm' => 'Remove',
+ 'ct' => 'Cut',
+ 'rn' => 'Rename',
+ 'cp' => 'Copy',
+ 'da' => 'Unset alias',
+ 'sa' => 'Set alias',
+ 'ex' => 'External Resource',
+ 'et' => 'External Tool',
+ 'ed' => 'Edit',
+ 'pr' => 'Preview',
+ 'sv' => 'Save',
+ 'ul' => 'URL',
+ 'ti' => 'Title',
+ 'er' => 'Editing rights unavailable for your current role.',
+ );
+ my %denied = &action_restrictions($coursenum,$coursedom,$url,
+ $env{'form.folderpath'},
+ $currgroups);
+ my ($copylink,$cutlink,$removelink);
+ my $skip_confirm = 0;
+ my $confirm_removal = 0;
+ if ( $folder =~ /^supplemental/
+ || ($url =~ m{( /smppg$
+ |/syllabus$
+ |/aboutme$
+ |/navmaps$
+ |/bulletinboard$
+ |/ext\.tool$
+ |\.html$)}x)
+ || $isexternal) {
+ $skip_confirm = 1;
+ }
+ if (($url=~m|/+uploaded/\Q$coursedom\E/\Q$coursenum\E/|) &&
+ ($url!~/$LONCAPA::assess_page_seq_re/)) {
+ $confirm_removal = 1;
+ }
+ if ($url =~ /$LONCAPA::assess_re/) {
+ $curralias = (&LONCAPA::map::getparameter($orderidx,'parameter_0_mapalias'))[0];
+ }
+
+ if ($denied{'copy'}) {
+ $copylink=(<$lt{'cp'}
+ENDCOPY
+ } else {
+ my $formname = 'edit_copy_'.$orderidx;
+ my $js = "javascript:checkForSubmit(document.forms.renameform,'copy','actions','$orderidx','$esc_path','$index','$renametitle',$skip_confirm,'$container','$folder');";
+ $copylink=(<
+$form_common
+$lt{'cp'}
+$form_end
+ENDCOPY
+ if (($ishash) && (ref($filtersref->{'cancopy'}) eq 'ARRAY')) {
+ push(@{$filtersref->{'cancopy'}},$orderidx);
+ }
+ }
+ if ($denied{'cut'}) {
+ $cutlink=(<$lt{'ct'}
+ENDCUT
+ } else {
+ my $formname = 'edit_cut_'.$orderidx;
+ my $js = "javascript:checkForSubmit(document.forms.renameform,'cut','actions','$orderidx','$esc_path','$index','$renametitle',$skip_confirm,'$container','$folder');";
+ $cutlink=(<
+$form_common
+
+$lt{'ct'}
+$form_end
+ENDCUT
+ if (($ishash) && (ref($filtersref->{'cancut'}) eq 'ARRAY')) {
+ push(@{$filtersref->{'cancut'}},$orderidx);
+ }
+ }
+ if ($denied{'remove'}) {
+ $removelink=(<$lt{'rm'}
+ENDREM
+ } else {
+ my $formname = 'edit_remove_'.$orderidx;
+ my $js = "javascript:checkForSubmit(document.forms.renameform,'remove','actions','$orderidx','$esc_path','$index','$renametitle',$skip_confirm,'$container','$folder',$confirm_removal);";
+ $removelink=(<
+$form_common
+
+
+$lt{'rm'}
+$form_end
+ENDREM
+ if (($ishash) && (ref($filtersref->{'canremove'}) eq 'ARRAY')) {
+ push(@{$filtersref->{'canremove'}},$orderidx);
+ }
+ }
+ $renamelink=(<$lt{'rn'}
+ENDREN
+ my ($uplink,$downlink);
+ if ($canedit) {
+ $uplink = "/adm/coursedocs?cmd=up_$index&folderpath=$esc_path&symb=$symb";
+ $downlink = "/adm/coursedocs?cmd=down_$index&folderpath=$esc_path&symb=$symb";
+ } else {
+ $uplink = "javascript:alert('".&js_escape($lt{'er'})."');";
+ $downlink = $uplink;
+ }
+ $line.=(<
+
+
+
+
+
+
+
+
+
+
+
+
+ $form_start
+ $form_param
+ $form_common
+ $selectbox
+ $form_end
+
+
+$removelink
+$cutlink
+$copylink
+
+END
+ }
+# Figure out what kind of a resource this is
+ my ($extension)=($url=~/\.(\w+)$/);
+ my $uploaded=($url=~/^\/*uploaded\//);
+ my $icon=&Apache::loncommon::icon($url);
+ my $isfolder;
+ my $ispage;
+ my $containerarg;
+ my $folderurl;
+ if ($uploaded) {
+ if (($extension eq 'sequence') || ($extension eq 'page')) {
+ $url=~/\Q$coursenum\E\/([\/\w]+)\.\Q$extension\E$/;
+ $containerarg = $1;
+ if ($extension eq 'sequence') {
+ $icon=$iconpath.'navmap.folder.closed.gif';
+ $isfolder=1;
+ } else {
+ $icon=$iconpath.'page.gif';
+ $ispage=1;
+ }
+ $folderurl = &Apache::lonnet::declutter($url);
+ if ($allowed) {
+ $url='/adm/coursedocs?';
+ } else {
+ $url='/adm/supplemental?';
+ }
+ } else {
+ &Apache::lonnet::allowuploaded('/adm/coursedoc',$url);
+ }
+ }
+
+ my ($editlink,$extresform,$anchor,$hiddenres,$nomodal);
+ my $orig_url = $url;
+ $orig_url=~s{http(:|:)//https(:|:)//}{https$2//};
+ $url=~s{^http(|s)(:|:)//}{/adm/wrapper/ext/};
+ if (!$supplementalflag && $residx && $symb) {
+ if ((!$isfolder) && (!$ispage)) {
+ (undef,undef,$url)=&Apache::lonnet::decode_symb($symb);
+ $url=&Apache::lonnet::clutter($url);
+ if ($url=~/^\/*uploaded\//) {
+ $url=~/\.(\w+)$/;
+ my $embstyle=&Apache::loncommon::fileembstyle($1);
+ if (($embstyle eq 'img') || ($embstyle eq 'emb')) {
+ $url='/adm/wrapper'.$url;
+ } elsif ($embstyle eq 'ssi') {
+ #do nothing with these
+ } elsif ($url!~/\.(sequence|page)$/) {
+ $url='/adm/coursedocs/showdoc'.$url;
+ }
+ } elsif ($url=~m{^(|/adm/wrapper)/ext/([^#]+)}) {
+ my $wrapped = $1;
+ my $exturl = $2;
+ if ($wrapped eq '') {
+ $url='/adm/wrapper'.$url;
+ }
+ if (($ENV{'SERVER_PORT'} == 443) && ($exturl !~ /^https:/)) {
+ $nomodal = 1;
+ }
+ } elsif ($url=~m{^/adm/$coursedom/$coursenum/\d+/ext\.tool$}) {
+ $url='/adm/wrapper'.$url;
+ } elsif ($url eq "/public/$coursedom/$coursenum/syllabus") {
+ if (($ENV{'SERVER_PORT'} == 443) &&
+ ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {
+ $url .= '?usehttp=1';
+ $nomodal = 1;
+ }
+ }
+ if (&Apache::lonnet::symbverify($symb,$url)) {
+ my $shownsymb = $symb;
+ if ($isexternal) {
+ if ($url =~ /^([^#]+)#([^#]+)$/) {
+ $url = $1;
+ $anchor = $2;
+ if ($symb =~ m{^([^#]+)\Q#$anchor\E$}) {
+ $shownsymb = $1.&escape('#').$anchor;
+ }
+ }
+ }
+ unless ($env{'request.role.adv'}) {
+ if ((&LONCAPA::map::getparameter($orderidx,'parameter_hiddenresource'))[0]=~/^yes$/i) {
+ $url = '';
+ }
+ if (&Apache::lonnet::EXT('resource.0.hiddenresource',$symb) =~ /^yes$/i) {
+ $url = '';
+ $hiddenres = 1;
+ }
+ }
+ if ($url ne '') {
+ $url.=(($url=~/\?/)?'&':'?').'symb='.&escape($shownsymb);
+ }
+ } elsif (!$env{'request.role.adv'}) {
+ my $checkencrypt;
+ if (((&LONCAPA::map::getparameter($orderidx,'parameter_encrypturl'))[0]=~/^yes$/i) ||
+ $isencrypted || (&Apache::lonnet::EXT('resource.0.encrypturl',$symb) =~ /^yes$/i)) {
+ $checkencrypt = 1;
+ } elsif (ref($navmapref)) {
+ unless (ref($$navmapref)) {
+ $$navmapref = Apache::lonnavmaps::navmap->new();
+ }
+ if (ref($$navmapref)) {
+ if (lc($$navmapref->get_mapparam($symb,undef,"0.encrypturl")) eq 'yes') {
+ $checkencrypt = 1;
+ }
+ }
+ }
+ if ($checkencrypt) {
+ my $shownsymb = &Apache::lonenc::encrypted($symb);
+ my $shownurl = &Apache::lonenc::encrypted($url);
+ if (&Apache::lonnet::symbverify($shownsymb,$shownurl)) {
+ $url = $shownurl.(($shownurl=~/\?/)?'&':'?').'symb='.&escape($shownsymb);
+ if ($env{'request.enc'} ne '') {
+ delete($env{'request.enc'});
+ }
+ } else {
+ $url='';
+ }
+ } else {
+ $url='';
+ }
+ } else {
+ $url='';
+ }
+ }
+ } elsif ($supplementalflag) {
+ if ($isexternal) {
+ if ($url =~ /^([^#]+)#([^#]+)$/) {
+ $url = $1;
+ $anchor = $2;
+ if (($url =~ m{^(|/adm/wrapper)/ext/(?!https:)}) && ($ENV{'SERVER_PORT'} == 443)) {
+ if ($hostname ne '') {
+ $url = 'http://'.$hostname.$url;
+ }
+ $nomodal = 1;
+ }
+ }
+ } elsif ($url =~ m{^\Q/public/$coursedom/$coursenum/syllabus\E}) {
+ if (($ENV{'SERVER_PORT'} == 443) &&
+ ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {
+ if ($hostname ne '') {
+ $url = 'http://'.$hostname.$url;
+ }
+ $url .= (($url =~ /\?/) ? '&':'?').'usehttp=1';
+ $nomodal = 1;
+ }
+ }
+ }
+ my ($rand_pick_text,$rand_order_text,$hiddenfolder);
+ my $filterFunc = sub { my $res = shift; return (!$res->randomout() && !$res->is_map()) };
+ if ($isfolder || $ispage || $extension eq 'sequence' || $extension eq 'page') {
+ my $foldername=&escape($foldertitle);
+ my $folderpath=$env{'form.folderpath'};
+ if ($folderpath) { $folderpath.='&' };
+ if (!$allowed && $supplementalflag) {
+ $folderpath.=$containerarg.'&'.$foldername;
+ $url.='folderpath='.&escape($folderpath);
+ } else {
+ my $rpicknum = (&LONCAPA::map::getparameter($orderidx,
+ 'parameter_randompick'))[0];
+ my $randorder = ((&LONCAPA::map::getparameter($orderidx,
+ 'parameter_randomorder'))[0]=~/^yes$/i);
+ my $hiddenmap = ((&LONCAPA::map::getparameter($orderidx,
+ 'parameter_hiddenresource'))[0]=~/^yes$/i);
+ my $encryptmap = ((&LONCAPA::map::getparameter($orderidx,
+ 'parameter_encrypturl'))[0]=~/^yes$/i);
+ unless ($hiddenmap) {
+ if (ref($navmapref)) {
+ unless (ref($$navmapref)) {
+ $$navmapref = Apache::lonnavmaps::navmap->new();
+ }
+ if (ref($$navmapref)) {
+ if (lc($$navmapref->get_mapparam(undef,$folderurl,"0.hiddenresource")) eq 'yes') {
+ my @resources = $$navmapref->retrieveResources($folderurl,$filterFunc,1,1);
+ unless (@resources) {
+ $hiddenmap = 1;
+ unless ($env{'request.role.adv'}) {
+ $url = '';
+ $hiddenfolder = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ unless ($encryptmap) {
+ if ((ref($navmapref)) && (ref($$navmapref))) {
+ if (lc($$navmapref->get_mapparam(undef,$folderurl,"0.encrypturl")) eq 'yes') {
+ $encryptmap = 1;
+ }
+ }
+ }
+
+# Append randompick number, hidden, and encrypted with ":" to foldername,
+# so it gets transferred between levels
+ $folderpath.=$containerarg.'&'.$foldername.
+ ':'.$rpicknum.':'.$hiddenmap.':'.$encryptmap.':'.$randorder.':'.$ispage;
+ unless ($url eq '') {
+ $url.='folderpath='.&escape($folderpath);
+ }
+ my $rpckchk;
+ if ($rpicknum) {
+ $rpckchk = ' checked="checked"';
+ if (($ishash) && (ref($filtersref->{'randompick'}) eq 'ARRAY')) {
+ push(@{$filtersref->{'randompick'}},$orderidx.':'.$rpicknum);
+ }
+ }
+ my $formname = 'edit_randompick_'.$orderidx;
+ $rand_pick_text =
+'