'."\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 = '';
+ $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,$toolprefix,$tooltype,%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);
+ }
+ if ($toolid =~ /^c/) {
+ $tooltype = 'crs';
+ $toolprefix = 'c';
+ } else {
+ $tooltype = 'dom';
+ }
+ $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->{$tooltype}) eq 'HASH') {
+ if (ref($ltitoolsref->{$tooltype}->{$toolid}) eq 'HASH') {
+ my %tools = %{$ltitoolsref->{$tooltype}->{$toolid}};
+ my @deleted;
+ $toolhash{'id'} = $toolprefix.$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($tools{'display'}) eq 'HASH') {
+ $toolhash{'target'} = $tools{'display'}{'target'};
+ if ($toolhash{'target'} eq 'window') {
+ $toolhash{'width'} = $tools{'display'}{'width'};
+ $toolhash{'height'} = $tools{'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($tools{'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 ($tools{'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'; }
+ if ($url =~ m{^/uploaded/$coursedom/$coursenum/((?:docs|supplemental)/(?:default|\d+))/new\.html$}) {
+ my $filepath = $1;
+ my $fname;
+ if ($name eq '') {
+ $name = &mt('Web Page');
+ $fname = 'web';
+ } else {
+ $fname = $name;
+ $fname=&Apache::lonnet::clean_filename($fname);
+ if ($fname eq '') {
+ $fname = 'web';
+ } elsif (length($fname) > 15) {
+ $fname = substr($fname,0,14);
+ }
+ }
+ my $title = &Apache::loncommon::cleanup_html($name);
+ my $initialtext = &mt('Replace with your own content.');
+ my $newhtml = <
+
+$title
+
+
+$initialtext
-ENDDOCUMENT
+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);
+ }
+ }
+ $name = &LONCAPA::map::qtunescape($name);
+ $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".
+ '
'.
+ &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.',
+ '','').
+ '
'."\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('
':'').
+ '');
+ 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.')
+ .'
'
+ .&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.')
+ .'
';
+ 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.').' '.
+ '
'.&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 = '