--- loncom/interface/londocs.pm 2013/03/17 14:27:34 1.537 +++ loncom/interface/londocs.pm 2013/12/18 23:03:43 1.572 @@ -1,7 +1,7 @@ # The LearningOnline Network # Documents # -# $Id: londocs.pm,v 1.537 2013/03/17 14:27:34 raeburn Exp $ +# $Id: londocs.pm,v 1.572 2013/12/18 23:03:43 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -41,6 +41,8 @@ use Apache::lonclonecourse; use Apache::lonnavmaps; use Apache::lonnavdisplay(); use Apache::lonextresedit(); +use Apache::lontemplate(); +use Apache::lonsimplepage(); use HTML::Entities; use HTML::TokeParser; use GDBM_File; @@ -56,6 +58,7 @@ my $hashtied; my %alreadyseen=(); my $hadchanges; +my $suppchanges; my %help=(); @@ -79,7 +82,11 @@ sub storemap { $map,1,$report); if ($errtext) { return ($errtext,2); } - $hadchanges=1; + if ($map =~ /^default/) { + $hadchanges=1; + } else { + $suppchanges=1; + } return ($errtext,0); } @@ -136,8 +143,28 @@ sub clean { sub dumpcourse { my ($r) = @_; my $crstype = &Apache::loncommon::course_type(); - $r->print(&Apache::loncommon::start_page('Dump '.$crstype.' Content to Authoring Space')."\n". - &Apache::lonhtmlcommon::breadcrumbs('Dump '.$crstype.' Content to Authoring Space')."\n"); + 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) { @@ -156,118 +183,407 @@ sub dumpcourse { $r->print('

'.&mt('Copying Files').'

'); my $title=$env{'form.authorfolder'}; $title=&clean($title); - my %replacehash=(); - foreach my $key (keys(%env)) { - if ($key=~/^form\.namefor\_(.+)/) { - $replacehash{$1}=$env{$key}; - } + 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; - foreach my $item (keys(%replacehash)) { - my $newfilename=$title.'/'.$replacehash{$item}; - $newfilename=~s/\.(\w+)$//; - my $ext=$1; - $newfilename=&clean($newfilename); - $newfilename.='.'.$ext; - my @dirs=split(/\//,$newfilename); - my $path=$r->dir_config('lonDocRoot')."/priv/$cd/$ca"; - my $makepath=$path; - my $fail=0; - for (my $i=0;$i<$#dirs;$i++) { - $makepath.='/'.$dirs[$i]; - unless (-e $makepath) { - unless(mkdir($makepath,0777)) { $fail=1; } - } - } - $r->print('
'.$item.' => '.$newfilename.': '); - if (my $fh=Apache::File->new('>'.$path.'/'.$newfilename)) { - if ($item=~/\.(sequence|page|html|htm|xml|xhtml)$/) { - 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); - } - $fh->close(); - } else { - $fail=1; - } - if ($fail) { - $r->print(''.&mt('fail').''); - } else { - $r->print(''.&mt('ok').''); - } - } + 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} = '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 .= ' +
+

Content

'. +$contents{content}.' +
'; + } + if ($contents{webreferences}) { + $content .= ' +
+

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 { - $r->print(&mt('Searching ...').'
    '); - $r->rflush(); -# Input form - $r->print('
    '."\n"); - unless ($home==1) { - $r->print('
    '. - '
    '. - &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 .= ''; + } + foreach my $user (@orderspaces) { if ($home==1) { - $r->print( - ''); + $preamble .= ''; } else { - $r->print(''); - } + $preamble .= ''; + } } - } - unless ($home==1) { - $r->print('
    '."\n"); - } - my $title=$origcrsdata{'description'}; - $title=~s/[\/\s]+/\_/gs; - $title=&clean($title); - $r->print('
    '. - '
    '.&mt('Folder in Authoring Space').''. - ''. - '

    '."\n"); - &tiehash(); - $r->print('

    '.&mt('Filenames in Authoring Space').'

    ' - .&Apache::loncommon::start_data_table() - .&Apache::loncommon::start_data_table_header_row() - .''.&mt('Internal Filename').'' - .''.&mt('Title').'' - .''.&mt('Save as ...').'' - .&Apache::loncommon::end_data_table_header_row()); - foreach my $file (&Apache::lonclonecourse::crsdirlist($origcrsid,'userfiles')) { - $r->print(&Apache::loncommon::start_data_table_row() - .''.$file.''); - my ($ext)=($file=~/\.(\w+)$/); - my $title=$hash{'title_'.$hash{ - 'ids_/uploaded/'.$origcrsdata{'domain'}.'/'.$origcrsdata{'num'}.'/'.$file}}; - $r->print(''.($title?$title:' ').''); - if (!$title) { - $title=$file; - } else { - $title=~s|/|_|g; + unless ($home==1) { + $preamble .= ''."\n"; } - $title=~s/\.(\w+)$//; + my $title=$origcrsdata{'description'}; + $title=~s/[\/\s]+/\_/gs; $title=&clean($title); - $title.='.'.$ext; - $r->print("\n" - .&Apache::loncommon::end_data_table_row()); - } - $r->print(&Apache::loncommon::end_data_table()); - &untiehash(); - $r->print( - '

    '); + $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, @files) = @_; my ($donechk,$allmaps,%hierarchy,%titles,%addedmaps,%removefrommap, @@ -323,10 +639,9 @@ sub group_import { } my $ext = 'false'; if ($url=~m{^http://} || $url=~m{^https://}) { $ext = 'true'; } - $url = &LONCAPA::map::qtunescape($url); $name = &LONCAPA::map::qtunescape($name); if ($name eq '') { - $name = &mt('Web Page'); + $name = &LONCAPA::map::qtunescape(&mt('Web Page')); } if ($url =~ m{^/uploaded/$coursedom/$coursenum/((?:docs|supplemental)/(?:default|\d+))/new\.html$}) { my $filepath = $1; @@ -341,10 +656,8 @@ sub group_import { } my $initialtext = &mt('Replace with your own content.'); my $newhtml = < - + - $name @@ -367,6 +680,7 @@ END return (&mt('Failed to save new web page.'),1); } } + $url = &LONCAPA::map::qtunescape($url); $LONCAPA::map::resources[$residx] = join(':', ($name, $url, $ext, 'normal', 'res')); } @@ -404,6 +718,13 @@ END } 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); } @@ -490,7 +811,7 @@ sub docs_change_log { } my $folderpath=$env{'form.folderpath'}; if ($folderpath eq '') { - $folderpath = 'default&'.&escape(&mt('Main '.$crstype.' Documents').':::::'); + $folderpath = 'default&'.&escape(&mt('Main Content').':::::'); } $pathitem = ''; @@ -655,57 +976,115 @@ sub docs_change_log { sub update_paste_buffer { my ($coursenum,$coursedom,$folder) = @_; + my (@possibles,%removals,%cuts); + 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 (!defined($env{'form.markcopy'})); + return if (@possibles == 0); return if (!defined($env{'form.copyfolder'})); - return if ($env{'form.markcopy'} < 0); my ($errtext,$fatal) = &mapread($coursenum,$coursedom, $env{'form.copyfolder'}); - return if ($fatal); -# Mark for copying - my ($title,$url)=split(':',$LONCAPA::map::resources[$LONCAPA::map::order[$env{'form.markcopy'}]]); - if (&is_supplemental_title($title)) { - &Apache::lonnet::appenv({'docs.markedcopy_supplemental' => $title}); - ($title) = &Apache::loncommon::parse_supplemental_title($title); - } elsif ($env{'docs.markedcopy_supplemental'}) { - &Apache::lonnet::delenv('docs.markedcopy_supplemental'); - } - $url=~s{http(:|:)//https(:|:)//}{https$2//}; - - (my $cmd,undef)=split('_',$env{'form.cmd'}); - - my %addtoenv = ( - 'docs.markedcopy_title' => $title, - 'docs.markedcopy_url' => $url, - 'docs.markedcopy_cmd' => $cmd, - ); - &Apache::lonnet::delenv('docs.markedcopy_nested'); - &Apache::lonnet::delenv('docs.markedcopy_nestednames'); - 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,\%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'} = $nested; + 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}; + if (($cid =~ /^$match_domain(?:_)$match_courseid$/) && + ($url ne '')) { + $pasteurls{$cid.'_'.$url} = 1; + } + } + } + +# Mark items for copying (skip any items already in user's paste buffer) + my %addtoenv; + + 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 ($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.'_'.$url})); + my ($suffix,$errortxt,$locknotfreed) = + &new_timebased_suffix($env{'user.domain'},$env{'user.name'},'paste'); + push(@newpaste,$suffix); + if ($locknotfreed) { + return $locknotfreed; + last; + } + 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'}; + + if ($url =~ m{^/uploaded/$match_domain/$match_courseid/(default|supplemental)_?(\d*)\.(page|sequence)$}) { + my $prefix = $1; + my $subdir =$2; + if ($subdir eq '') { + $subdir = $prefix; } - if ($nestednames ne '') { - $addtoenv{'docs.markedcopy_nestednames'} = $nestednames; + my (%addedmaps,%removefrommap,%removeparam,%hierarchy,%titles,%allmaps); + &contained_map_check($url,$folder,\%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 (@newpaste) { + $addtoenv{'docs.markedcopies'} = join(',',(@currpaste,@newpaste)); + } &Apache::lonnet::appenv(\%addtoenv); delete($env{'form.markcopy'}); } @@ -737,155 +1116,200 @@ sub recurse_uploaded_maps { sub print_paste_buffer { my ($r,$container,$folder,$coursedom,$coursenum) = @_; - return if (!defined($env{'docs.markedcopy_url'})); + return if (!defined($env{'docs.markedcopies'})); - my ($is_external,$othercourse,$fromsupp,$is_uploaded_map,$parent); - my $extension = (split(/\./,$env{'docs.markedcopy_url'}))[-1]; - if ($env{'docs.markedcopy_url'} =~ m{^(?:/adm/wrapper/ext|(?:http|https)(?::|:))//} ) { - $is_external = 1; + unless (($env{'form.pastemarked'}) || ($env{'form.clearmarked'})) { + return if ($env{'docs.markedcopies'} eq ''); } - my ($canpaste,$nopaste,$othercrs,$areachange); - if ($folder =~ /^supplemental/) { - $canpaste = &supp_pasteable($env{'docs.markedcopy_url'}); - unless ($canpaste) { - $nopaste = &mt('Paste into Supplemental Content unavailable for this type of content.'); - } - } else { - $canpaste = 1; - } + my @currpaste = split(/,/,$env{'docs.markedcopies'}); + my ($pasteitems,@pasteable); - if ($canpaste) { - if ($env{'docs.markedcopy_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/) { - if ($canpaste) { - $othercrs = '
    '.&mt('(from another course).'); +# 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}; + if (($cid =~ /^$match_domain\_$match_courseid$/) && + ($url ne '')) { + my ($is_external,$othercourse,$fromsupp,$is_uploaded_map,$parent, + $canpaste,$nopaste,$othercrs,$areachange); + my $extension = (split(/\./,$env{'docs.markedcopy_url_'.$suffix}))[-1]; + if ($url =~ m{^(?:/adm/wrapper/ext|(?:http|https)(?::|:))//} ) { + $is_external = 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/) { + if ($canpaste) { + $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; } - } 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; + 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).')'; + } 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'; + } + $buffer = ''. + ': '. + &Apache::loncommon::parse_supplemental_title( + &LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix})); + } + $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 .= ''; + if ($nopaste) { + $pasteitems .= $nopaste; + } else { + if ($othercrs) { + $pasteitems .= $othercrs; + } + if ($options) { + $pasteitems .= $options; } - $is_uploaded_map = 1; } + $pasteitems .= '
    '; } } - - $r->print('
    ' - .''.&mt('Clipboard').''); - my ($type,$buffer); - if ($is_external) { - $type = &mt('External Resource'); - $buffer = $type.': '. - &LONCAPA::map::qtescape($env{'docs.markedcopy_title'}).' ('. - &LONCAPA::map::qtescape($env{'docs.markedcopy_url'}).')'; + if ($pasteitems eq '') { + &Apache::lonnet::delenv('docs.markedcopies'); + } + my ($pasteform,$form_start,$buttons,$form_end); + if ($pasteitems) { + $pasteitems .= '
    '; + $form_start = '
    '; + if (@pasteable) { + $buttons = ''.(' 'x2); + } + $buttons .= ''. + ''; + $form_end = '
    '; } else { - my $icon = &Apache::loncommon::icon($extension); - if ($extension eq 'sequence' && - $env{'docs.markedcopy_url'} =~ m{/default_\d+\.sequence$ }x) { - $icon = &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL')); - $icon .= '/navmap.folder.closed.gif'; - } - $icon = ''; - $buffer = $icon.$type.': '. &Apache::loncommon::parse_supplemental_title(&LONCAPA::map::qtescape($env{'docs.markedcopy_title'})); - } - if ($canpaste) { - $r->print('
    '.$buffer); - if ((!$areachange) && (!$othercourse) && - ($env{'docs.markedcopy_cmd'} eq 'cut')) { - if (($is_uploaded_map) || - ($env{'docs.markedcopy_url'} =~ /(bulletinboard|smppg)$/) || - ($env{'docs.markedcopy_url'} =~ m{^/uploaded/$coursedom/$coursenum/(?:docs|supplemental)/(.+)$})) { - my ($copytext,$movetext); - if ($is_uploaded_map) { - $copytext = &mt('Copy to new folder'); - $movetext = &mt('Move old folder'); - } elsif ($env{'docs.markedcopy_url'} =~ /bulletinboard$/) { - $copytext = &mt('Copy to new bulletin board (not posts)'); - $movetext = &mt('Move old bulletin board (not posts)'); - } elsif ($env{'docs.markedcopy_url'} =~ /smppg$/) { - $copytext = &mt('Copy to new simple page'); - $movetext = &mt('Move old simple page'); - } else { - $copytext = &mt('Copy to new uploaded document'); - $movetext = &mt('Move old uploaded document'); - } - $r->print((' 'x 4).''. - ''. - &mt('Show Paste Options').'
    '. - '
    '.(' 'x 4). - ''.(' ' x2). - '
    '); - if (($is_uploaded_map) && ($env{'docs.markedcopy_nested'})) { - $r->print('
    '.&mt('Folder to paste contains sub-folders'). - '
    '); - my @pastemaps = split(/\&/,$env{'docs.markedcopy_nested'}); - my @titles = split(/\Q___&&&___\E/,$env{'docs.markedcopy_nestednames'}); - 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]} = - ''. - ''; - } - } - &recurse_print($r,$parent,\%deps,\%display); - $r->print('
    '.$indent.$subfoldertitles[$j].' '.(' ' x2). - ''. - '
    '); - } - $r->print('
    '); - } - } - $r->print('
    '.$othercrs); - $r->print(' - -'); - $r->print('
    '); + $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 { - $r->print(&mt('Paste buffer contains:').' '.$buffer. - '

    '.$nopaste.'

    '); + $copytext = &mt('Copy to new file'); + $movetext = &mt('Move'); + } + my $output = '
    '. + ''. + '
    '); + $output .= ''; + return $output; } sub recurse_print { - my ($r,$dir,$deps,$display) = @_; - $r->print($display->{$dir}."\n"); + my ($outputref,$dir,$deps,$display) = @_; + $$outputref .= $display->{$dir}."\n"; if (ref($deps->{$dir}) eq 'ARRAY') { foreach my $subdir (@{$deps->{$dir}}) { - &recurse_print($r,$subdir,$deps,$display); + &recurse_print($outputref,$subdir,$deps,$display); } } } @@ -904,303 +1328,484 @@ sub supp_pasteable { sub paste_popup_js { my %lt = &Apache::lonlocal::texthash( - show => 'Show Paste Options', - hide => 'Hide Paste Options', + show => 'Show Options', + hide => 'Hide Options', + none => 'No items selected from clipboard.', ); return <<"END"; -function showPasteOptions() { - document.getElementById('pasteoptions').style.display='block'; - document.getElementById('pasteoptions').style.textAlign='left'; - document.getElementById('pasteoptions').style.textFace='normal'; - document.getElementById('pasteoptionstext').innerHTML ='$lt{'hide'}
    '; +function showPasteOptions(suffix) { + document.getElementById('pasteoptions_'+suffix).style.display='block'; + document.getElementById('pasteoptionstext_'+suffix).innerHTML = '    $lt{'hide'}'; return; } -function hidePasteOptions() { - document.getElementById('pasteoptions').style.display='none'; - document.getElementById('pasteoptionstext').innerHTML ='$lt{'show'}'; +function hidePasteOptions(suffix) { + document.getElementById('pasteoptions_'+suffix).style.display='none'; + document.getElementById('pasteoptionstext_'+suffix).innerHTML ='    $lt{'show'}'; return; } -END +function showOptions(caller,suffix) { + if (document.getElementById('pasteoptionstext_'+suffix)) { + if (caller.checked) { + document.getElementById('pasteoptionstext_'+suffix).innerHTML ='    $lt{'show'}'; + } 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("$lt{'none'}"); + return false; + } } +END + +} sub do_paste_from_buffer { my ($coursenum,$coursedom,$folder,$container,$errors) = @_; +# Array of items in paste buffer + my (@currpaste,%pastebuffer,%allerrors); + @currpaste = split(/,/,$env{'docs.markedcopies'}); + # Early out if paste buffer is empty - if (!$env{'form.pastemarked'}) { + if (@currpaste == 0) { return (); + } + map { $pastebuffer{$_} = 1; } @currpaste; + +# Array of items selected items to paste + my @reqpaste = &Apache::loncommon::get_env_multiple('form.pasting'); + +# Early out if nothing selected to paste + if (@reqpaste == 0) { + return(); + } + my @topaste; + foreach my $suffix (@reqpaste) { + next if ($suffix =~ /\D/); + next unless (exists($pastebuffer{$suffix})); + push(@topaste,$suffix); } -# Supplemental content may only include certain types of content -# Early out if pasted content is not supported in Supplemental area - if ($folder =~ /^supplemental/) { - unless (&supp_pasteable($env{'docs.markedcopy_url'})) { - return (&mt('Paste failed: content type is not supported within Supplemental Content')); - } +# Early out if nothing available to paste + if (@topaste == 0) { + return(); } -# Prepare to paste resource at end of list - my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url'}); - my $title=&LONCAPA::map::qtescape($env{'docs.markedcopy_title'}); + my (%msgs,%before,%after,@dopaste,%is_map,%notinsupp,%notincrs,%duplicate, + %prefixchg,%srcdom,%srcnum,%marktomove,$save_err,$lockerrors,$allresult); - my ($is_map,$srcdom,$srcnum,$prefixchg,%before,%after,%mapchanges,%tomove); - if ($url=~/\.(page|sequence)$/) { - $is_map = 1; - } - if ($url =~ m{^/uploaded/($match_domain)/($match_courseid)/([^/]+)}) { - $srcdom = $1; - $srcnum = $2; - my $oldprefix = $3; + foreach my $suffix (@topaste) { + my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url_'.$suffix}); +# Supplemental content may only include certain types of content +# Early out if pasted content is not supported in Supplemental area + if ($folder =~ /^supplemental/) { + unless (&supp_pasteable($url)) { + $notinsupp{$suffix} = 1; + next; + } + } + if ($url =~ m{^/uploaded/($match_domain)/($match_courseid)/}) { + my $srcd = $1; + my $srcn = $2; # When paste buffer was populated using an active role in a different course -# check for mdc privilege in the course from which the resource was pasted - if (($srcdom ne $coursedom) || ($srcnum ne $coursenum)) { - unless ($env{"user.priv.cm./$srcdom/$srcnum"} =~ /\Q:mdc&F\E/) { - return (&mt('Paste failed: Item is from a different course which you do not have rights to edit.')); +# check for mdc privilege in the course from which the resource was pasted + if (($srcd ne $coursedom) || ($srcn ne $coursenum)) { + unless ($env{"user.priv.cm./$srcd/$srcn"} =~ /\Q:mdc&F\E/) { + $notincrs{$suffix} = 1; + next; + } } + $srcdom{$suffix} = $srcd; + $srcnum{$suffix} = $srcn; } + + push(@dopaste,$suffix); + if ($url=~/\.(page|sequence)$/) { + $is_map{$suffix} = 1; + } + + if ($url =~ m{^/uploaded/$match_domain/$match_courseid/([^/]+)}) { + my $oldprefix = $1; # When pasting content from Main Content to Supplemental Content and vice versa # URLs will contain different paths (which depend on whether pasted item is # a folder/page or a document. - if (($folder =~ /^supplemental/) && (($oldprefix =~ /^default/) || ($oldprefix eq 'docs'))) { - $prefixchg = 1; - %before = ( map => 'default', - doc => 'docs'); - %after = ( map => 'supplemental', - doc => 'supplemental' ); - } elsif (($folder =~ /^default/) && ($oldprefix =~ /^supplemental/)) { - $prefixchg = 1; - %before = ( map => 'supplemental', - doc => 'supplemental'); - %after = ( map => 'default', - doc => 'docs'); - } + if (($folder =~ /^supplemental/) && (($oldprefix =~ /^default/) || ($oldprefix eq 'docs'))) { + $prefixchg{$suffix} = 'docstosupp'; + } elsif (($folder =~ /^default/) && ($oldprefix =~ /^supplemental/)) { + $prefixchg{$suffix} = 'supptodocs'; + } # If pasting an uploaded map, get list of contained uploaded maps. - my @nested; - if ($env{'docs.markedcopy_nested'}) { - my ($type) = ($oldprefix =~ /^(default|supplemental)/); - my @items = split(/\&/,$env{'docs.markedcopy_nested'}); - my @deps = map { /\d+:([\d,]+$)/ } @items; - foreach my $dep (@deps) { - if ($dep =~ /,/) { - push(@nested,split(/,/,$dep)); - } else { - push(@nested,$dep); + if ($env{'docs.markedcopy_nested_'.$suffix}) { + my @nested; + my ($type) = ($oldprefix =~ /^(default|supplemental)/); + my @items = split(/\&/,$env{'docs.markedcopy_nested_'.$suffix}); + my @deps = map { /\d+:([\d,]+$)/ } @items; + foreach my $dep (@deps) { + if ($dep =~ /,/) { + push(@nested,split(/,/,$dep)); + } else { + push(@nested,$dep); + } } - } - foreach my $item (@nested) { - if ($env{'form.docs.markedcopy_'.$item} eq 'move') { - $tomove{$type.'_'.$item} = 1; + foreach my $item (@nested) { + if ($env{'form.docs.markedcopy_'.$suffix.'_'.$item} eq 'move') { + push(@{$marktomove{$suffix}},$type.'_'.$item); + } } } } } +# Early out if nothing available to paste + if (@dopaste == 0) { + return (); + } + +# Populate message hash and hashes used for main content <=> 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.', + 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 ($oldurl,%removefrommap,%removeparam,%addedmaps,%rewrites,%retitles,%copies, - %dbcopies,%zombies,%params,%docmoves,%mapmoves,%newsubdir,%newurls); - $oldurl = $url; - if ($is_map) { + my (%removefrommap,%removeparam,%addedmaps,%rewrites,%retitles,%copies, + %dbcopies,%zombies,%params,%docmoves,%mapmoves,%mapchanges,%newsubdir, + %newurls,%tomove); + 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 $oldurl = $url; + if ($is_map{$suffix}) { # If pasting a map, check if map contains other maps - my ($allmaps,%hierarchy,%titles); - $allmaps = {}; - if ($folder =~ /^default/) { - $allmaps = - &Apache::loncommon::allmaps_incourse($coursedom,$coursenum, - $env{"course.$env{'request.course.id'}.home"}, - $env{'request.course.id'}); - } - &contained_map_check($url,$folder,\%removefrommap,\%removeparam, - \%addedmaps,\%hierarchy,\%titles,$allmaps); - if ($url=~ m{^/uploaded/}) { - my $newurl; - unless ($env{'form.docs.markedcopy_options'} eq 'move') { - ($newurl,my $error) = - &get_newmap_url($url,$folder,$prefixchg,$coursedom,$coursenum, - $srcdom,$srcnum,\$title,$allmaps,\%newurls); - if ($error) { - return ($error); - } - if ($newurl ne '') { - if ($newurl ne $url) { - if ($newurl =~ /(?:default|supplemental)_(\d+).(?:sequence|page)$/) { - $newsubdir{$url} = $1; - } - $mapchanges{$url} = 1; + my (%hierarchy,%titles); + &contained_map_check($url,$folder,\%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 ne $coursedom) || ($srcnum ne $coursenum) || ($prefixchg) || - (($newurl ne '') && ($newurl ne $url))) { - unless (&url_paste_fixups($url,$folder,$prefixchg,$coursedom, - $coursenum,$srcdom,$srcnum,$allmaps, - \%rewrites,\%retitles,\%copies,\%dbcopies, - \%zombies,\%params,\%mapmoves,\%mapchanges,\%tomove, - \%newsubdir,\%newurls)) { - $mapmoves{$url} = 1; - } - $url = $newurl; - } elsif ($env{'docs.markedcopy_nested'}) { - &url_paste_fixups($url,$folder,$prefixchg,$coursedom,$coursenum, - $srcdom,$srcnum,$allmaps,\%rewrites, - \%retitles,\%copies,\%dbcopies,\%zombies,\%params,\%mapmoves, - \%mapchanges,\%tomove,\%newsubdir,\%newurls); - } - } elsif ($url=~m {^/res/}) { -# published maps can only exists once, so remove it from paste buffer when done - &Apache::lonnet::delenv('docs.markedcopy'); -# if pasting published map (main content are only) check map is not already in course - if ($folder =~ /^default/) { - if ((ref($allmaps) eq 'HASH') && ($allmaps->{$url})) { - return (&mt('Paste failed: only one instance of a particular published sequence or page is allowed within each course.')); + 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)) { + $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); + } + } elsif ($url=~m {^/res/}) { +# published map can only exists 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; + } } } } - } - my $lockerrors; - if ($url=~ m{/(bulletinboard|smppg)$}) { - my $prefix = $1; - #need to copy the db contents to a new one, unless this is a move. - my %info = ( - src => $url, - cdom => $coursedom, - cnum => $coursenum, - ); - my (%lockerr,$msg); - unless ($env{'form.docs.markedcopy_options'} eq 'move') { - 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 bulletin board.').' '.$errtext; + if ($url=~ m{/(bulletinboard|smppg)$}) { + my $prefix = $1; + #need to copy the db contents to a new one, unless this is a move. + my %info = ( + src => $url, + cdom => $coursedom, + cnum => $coursenum, + ); + unless ($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') { + 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; + } + $results{$suffix} = $result; + $msgerrs{$suffix} = $msg; + $lockerrs{$suffix} = $lockerr{$prefix}; + next; + } + if ($lockerr{$prefix}) { + $lockerrs{$suffix} = $lockerr{$prefix}; } - return ($result,undef,[$msg],$lockerr{$prefix}); - } - if ($lockerr{$prefix}) { - $lockerrors = $lockerr{$prefix}; } } - } - $title = &LONCAPA::map::qtunescape($title); - my $ext='false'; - if ($url=~m{^http(|s)://}) { $ext='true'; } - $url = &LONCAPA::map::qtunescape($url); + $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. - my $newidx; - unless ($is_map) { + unless ($is_map{$suffix}) { + my $newidx; # Now insert the URL at the bottom - $newidx = &LONCAPA::map::getresidx($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) || - ($srcdom ne $coursedom) || ($srcnum ne $coursenum) || - ($env{'form.docs.markedcopy_options'} 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') { - return (&mt('Paste failed: an error occurred saving the file.')); - } else { - my ($newsubpath) = ($newpath =~ m{^(.*/)[^/]*$}); - $newsubpath =~ s{/+$}{/}; - $docmoves{$oldurl} = $newsubpath; + $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; + } } } } - } - } -# Apply any changes to maps, or copy dependencies for uploaded HTML pages - my ($result,$save_err); - my %updated = ( - rewrites => \%rewrites, - zombies => \%zombies, - removefrommap => \%removefrommap, - removeparam => \%removeparam, - dbcopies => \%dbcopies, - retitles => \%retitles, - ); - my %info = ( - newsubdir => \%newsubdir, - params => \%params, - before => \%before, - after => \%after, - ); - my %moves = ( - copies => \%copies, - docmoves => \%docmoves, - mapmoves => \%mapmoves, - ); - ($result,my $msgsarray,my $lockerror) = - &apply_fixups($folder,$is_map,$coursedom,$coursenum,$errors, - \%updated,\%info,\%moves,$prefixchg,$oldurl,$url,'paste'); - $lockerrors .= $lockerror; - if ($result eq 'ok') { - if ($is_map) { - my ($errtext,$fatal) = &mapread($coursenum,$coursedom, - $folder.'.'.$container); - return ($errtext,$save_err,$msgsarray,$lockerrors) 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]=''; - } - $newidx = &LONCAPA::map::getresidx($url); - } - if ($env{'docs.markedcopy_supplemental'}) { - if ($folder !~ /^supplemental/) { - (undef,undef,$title) = - &Apache::loncommon::parse_supplemental_title($env{'docs.markedcopy_supplemental'}); - } - } else { - if ($folder=~/^supplemental/) { - $title=time.'___&&&___'.$env{'user.name'}.'___&&&___'. - $env{'user.domain'}.'___&&&___'.$title; + $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'; } } - $LONCAPA::map::resources[$newidx]= $title.':'.$url.':'.$ext.':normal:res'; - push(@LONCAPA::map::order, $newidx); + +# Apply any changes to maps, or copy dependencies for uploaded HTML pages + unless ($allresult eq 'fail') { + my %updated = ( + rewrites => \%rewrites, + zombies => \%zombies, + removefrommap => \%removefrommap, + removeparam => \%removeparam, + dbcopies => \%dbcopies, + 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; + 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); +} - if ($env{'form.docs.markedcopy_options'} eq 'move') { - &Apache::lonnet::delenv('docs.markedcopy'); - &Apache::lonnet::delenv('docs.markedcopy_nested'); - &Apache::lonnet::delenv('docs.markedcopy_nestednames'); +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 ($result,$save_err,$msgsarray,$lockerrors); + 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 { @@ -1266,7 +1871,7 @@ sub get_newmap_url { sub dbcopy { my ($dbref,$coursedom,$coursenum,$lockerrorsref) = @_; my ($url,$result,$errtext); - my $url = $dbref->{'src'}; + $url = $dbref->{'src'}; if (ref($dbref) eq 'HASH') { if ($url =~ m{/(smppg|bulletinboard)$}) { my $prefix = $1; @@ -1291,7 +1896,7 @@ sub dbcopy { if ($prefix eq 'smppg') { $errtext = &mt('Failed to acquire a unique timestamp-based suffix when copying a simple page [_1].',$url); } else { - $errtext = &mt('Failed to acquire a unique timestamp-based suffix when copying a bulletin board [_1].',$url); + $errtext = &mt('Failed to acquire a unique timestamp-based suffix when copying a discussion board [_1].',$url); } if ($error) { $errtext .= '
    '.$error; @@ -1327,16 +1932,18 @@ sub dbcopy { } } if (($freedlock ne 'ok') && (ref($lockerrorsref) eq 'HASH')) { - $lockerrorsref->{$prefix} = + $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.'); + $lockerrorsref->{$prefix} .= + ' '.&mt('This will prevent creation of additional simple pages in this course.'); } else { - $lockerrorsref->{$prefix} .= &mt('This will prevent creation of additional bulletin boards in this course.'); + $lockerrorsref->{$prefix} .= ' '.&mt('This will prevent creation of additional discussion boards in this course.'); } - $lockerrorsref->{$prefix} .= &mt('Please contact the domain coordinator for your LON-CAPA domain.').'
    '; + $lockerrorsref->{$prefix} .= ' '.&mt('Please contact the [_1]helpdesk[_2] for assistance.', + '',''). + ''; } } } elsif ($url =~ m{/syllabus$}) { @@ -1744,7 +2351,8 @@ sub apply_fixups { 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 $src)) { + 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}}) { @@ -1783,7 +2391,7 @@ sub apply_fixups { $changed = 1; } if ($changed) { - $LONCAPA::map::resources[$idx] = join(':',($title,$src,$ext,$type)); + $LONCAPA::map::resources[$idx] = join(':',($title,&LONCAPA::map::qtunescape($src),$ext,$type)); } } } @@ -1907,7 +2515,9 @@ sub update_parameter { } } } else { - map { $allchecked{$which}{$_} = 1; } split(/,/,$env{'form.all'.$which}); + if ($env{'form.all'.$which}) { + map { $allchecked{$which}{$_} = 1; } split(/,/,$env{'form.all'.$which}); + } } } my $haschanges = 0; @@ -1981,12 +2591,15 @@ sub update_parameter { sub handle_edit_cmd { my ($coursenum,$coursedom) =@_; + if ($env{'form.cmd'} eq '') { + return 0; + } 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 'del') { + if ($cmd eq 'remove') { if (($url=~m|/+uploaded/\Q$coursedom\E/\Q$coursenum\E/|) && ($url!~/$LONCAPA::assess_page_seq_re/)) { &Apache::lonnet::removeuploadedurl($url); @@ -2008,7 +2621,6 @@ sub handle_edit_cmd { @LONCAPA::map::order[$idx+1,$idx] = @LONCAPA::map::order[$idx,$idx+1]; } elsif ($cmd eq 'rename') { - my $comment = &LONCAPA::map::qtunescape($env{'form.title'}); if ($comment=~/\S/) { $LONCAPA::map::resources[$LONCAPA::map::order[$idx]]= @@ -2017,6 +2629,7 @@ sub handle_edit_cmd { # Devalidate title cache my $renamed_url=&LONCAPA::map::qtescape($url); &Apache::lonnet::devalidate_title_cache($renamed_url); + } else { return 0; } @@ -2041,7 +2654,7 @@ sub editor { my $jumpto; unless ($supplementalflag) { - $jumpto = "'uploaded/$coursedom/$coursenum/$folder.$container'"; + $jumpto = "uploaded/$coursedom/$coursenum/$folder.$container"; } unless ($allowed) { @@ -2085,27 +2698,33 @@ sub editor { 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 (ref($pastemsgarray) eq 'ARRAY') { + if (@{$pastemsgarray} > 0) { + $r->print('

    '. + join('
    ',@{$pastemsgarray}). '

    '); } - if ($save_error ne '') { - return $save_error; + } + 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 ($paste_res ne 'ok') { - $r->print('

    '.$paste_res.'

    '); } if (keys(%paste_errors) > 0) { - $r->print('

    '."\n". + $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"); } - } + } 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()) { my $contentchg; - if ($env{'form.cmd'} =~ /^(del|cut)_/) { + if ($env{'form.cmd'} =~ m{^(del|cut)_}) { $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 ($name && $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; @@ -2237,8 +2919,17 @@ sub editor { $r->print(''); } - my ($to_show,$output,@allidx,@allmapidx); - + 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]); @@ -2252,14 +2943,18 @@ sub editor { } $output .= &entryline($idx,$name,$url,$folder,$allowed,$res, $coursenum,$coursedom,$crstype, - $pathitem,$supplementalflag,$container); + $pathitem,$supplementalflag,$container, + \%filters,\%curr_groups); $idx++; $shown++; } &Apache::loncommon::end_data_table_count(); + my $need_save; if (($allowed) || ($supplementalflag && $folder eq 'supplemental')) { - my $toolslink = '' - .'' - .''; + .'' + .''; if ($folder !~ /^supplemental/) { $to_show .= ''; } $to_show .= &Apache::loncommon::end_data_table_header_row(); if ($folder !~ /^supplemental/) { - my $idxlist = join(',',@allidx); - my $mapidxlist = join(',',@allmapidx); + $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'},'<>&"'); } - $to_show .= - &Apache::loncommon::continue_data_table_row(). - ''. - ''. - &Apache::loncommon::end_data_table_row(); + if (@allidx > 1) { + $to_show .= + &Apache::loncommon::continue_data_table_row(). + ''. + ''. + ''. + ''. + ''. + &Apache::loncommon::end_data_table_row(); + $need_save = 1; + } } } $to_show .= $output.' ' @@ -2337,7 +3028,7 @@ sub editor { } $to_show .= &Apache::loncommon::start_scrollbox('400px','380px','200px','contentscroll') .'
    ' - .&mt('Currently no documents.') + .&mt('Currently empty') .'
    ' .&Apache::loncommon::end_scrollbox(); } @@ -2350,7 +3041,7 @@ sub editor { .''; } else { $to_show = '
    ' - .&mt('Currently no documents.') + .&mt('Currently empty') .'
    ' } } @@ -2360,8 +3051,8 @@ sub editor { } if ($allowed) { my $readfile="/uploaded/$coursedom/$coursenum/$folder.$container"; - $r->print(&generate_edit_table($tid,$orderhash,$to_show,$iconpath,$jumpto, - $readfile)); + $r->print(&generate_edit_table($tid,$orderhash,$to_show,$iconpath, + $jumpto,$readfile,$need_save,"$folder.$container")); &print_paste_buffer($r,$container,$folder,$coursedom,$coursenum); } else { $r->print($to_show); @@ -2369,9 +3060,96 @@ sub editor { return; } +sub multiple_check_form { + my ($caller,$listsref) = @_; + return unless (ref($listsref) eq 'HASH'); + my $output = + ''. + ''. + ''.(' 'x2).''. + '
    ' + my $toolslink; + if ($allowed || &Apache::lonnet::allowed('mdc',$env{'request.course.id'})) { + $toolslink = '' @@ -2269,56 +2964,52 @@ sub editor { .'class="LC_toolbarItem" ' .'title="'.&mt('Supplemental Content Editor').'">' .'
    ' .&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').''.&mt('Actions').''.&mt('Document').''.&mt('Settings').' '. - '
    '. - ''.&mt('Select:').' '. - ''.(' 'x2).'
    '. - ''. - - '
     '. + &multiple_check_form('actions',\%lists). + '  '. + &multiple_check_form('settings',\%lists). + '
    '. + ''. + ''. + ''."\n". + ''. + '
    '. + ''. + ''. + '
    '. + ''. + '
    '."\n"; + } else { + $output .= + ''. + ''. + ''."\n". + ''. + '
    '. + ''. + ''. + ''. + ''. + ''. + '
    '."\n"; + } + $output .= + ''. + ''; + if ($caller eq 'settings') { + $output .= + ''."\n". + ''."\n". + ''."\n". + ''."\n". + ''."\n"; + } elsif ($caller eq 'actions') { + $output .= + ''. + ''. + ''; + } + $output .= + ''. + ''; + return $output; +} + sub process_file_upload { - my ($upload_output,$coursenum,$coursedom,$allfiles,$codebase,$uploadcmd) = @_; + 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 ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.instcode'}) { + $quotatype = 'official'; + } + 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'); + return if ($$upload_output); + } my ($parseaction,$showupload,$nextphase,$mimetype); if ($env{'form.parserflag'}) { $parseaction = 'parse'; @@ -2528,7 +3306,7 @@ sub is_supplemental_title { sub entryline { my ($index,$title,$url,$folder,$allowed,$residx,$coursenum,$coursedom, - $crstype,$pathitem,$supplementalflag,$container)=@_; + $crstype,$pathitem,$supplementalflag,$container,$filtersref,$currgroups)=@_; my ($foldertitle,$renametitle); if (&is_supplemental_title($title)) { ($title,$foldertitle,$renametitle) = &Apache::loncommon::parse_supplemental_title($title); @@ -2544,7 +3322,7 @@ sub entryline { $renametitle=~s/\"\;/\\\"/g; $renametitle=~s/ /%20/g; my $line=&Apache::loncommon::start_data_table_row(); - my ($form_start,$form_end,$form_common); + my ($form_start,$form_end,$form_common,$form_param); # Edit commands my ($esc_path, $path, $symb); if ($env{'form.folderpath'}) { @@ -2568,8 +3346,25 @@ sub entryline { &Apache::lonnet::declutter($currurl)); } } - my ($renamelink,%lt); + my ($renamelink,%lt,$ishash); + if (ref($filtersref) eq 'HASH') { + $ishash = 1; + } + if ($allowed) { + $form_start = ' +
    +'; + $form_common=(< + +END + $form_param=(< + +END + $form_end = ''; + my $incindex=$index+1; my $selectbox=''; if (($#LONCAPA::map::order>0) && @@ -2605,66 +3400,9 @@ sub entryline { 'ul' => 'URL', 'ti' => 'Title', ); - my $nocopy=0; - my $nocut=0; - my $noremove=0; - if ($url=~ m{^/res/.+\.(page|sequence)$}) { - # no copy for published maps - $nocopy=1; - } - if ($url=~/^\/res\/lib\/templates\//) { - $nocopy=1; - $nocut=1; - } - my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; - if ($url eq "/uploaded/$cdom/$cnum/group_allfolders.sequence") { - if ($env{'form.folderpath'} =~ /^default&[^\&]+$/) { - my %curr_groups = &Apache::longroup::coursegroups(); - if (keys(%curr_groups) > 0) { - $noremove=1; - } - $nocut=1; - $nocopy=1; - } - } elsif ($url =~ m{^\Q/uploaded/$cdom/$cnum/group_folder_\E(\w+)\.sequence$}) { - my $group = $1; - if ($env{'form.folderpath'} =~ /^default&[^\&]+\&group_allfolders\&[^\&]+$/) { - my %curr_group = &Apache::longroup::coursegroups($cdom,$cnum,$group); - if (keys(%curr_group) > 0) { - $noremove=1; - } - } - $nocut=1; - $nocopy=1; - } elsif ($url =~ m{^\Q/adm/$cdom/$cnum/\E(\w+)/smppg$}) { - my $group = $1; - if ($env{'form.folderpath'} =~ /^default&[^\&]+\&group_allfolders\&[^\&]+\&\Qgroup_folder_$group\E\&[^\&]+$/) { - my %curr_group = &Apache::longroup::coursegroups($cdom,$cnum,$group); - my %groupsettings = &Apache::longroup::get_group_settings($curr_group{$group}); - if (keys(%groupsettings) > 0) { - $noremove=1; - } - $nocut=1; - $nocopy=1; - } - } elsif ($env{'form.folderpath'} =~ /^default&[^\&]+\&group_allfolders\&[^\&]+\&group_folder_(\w+)\&/) { - my $group = $1; - my %curr_group = &Apache::longroup::coursegroups($cdom,$cnum,$group); - if ($url =~ /group_boards_\Q$group\E/) { - my %curr_group = &Apache::longroup::coursegroups($cdom,$cnum,$group); - my %groupsettings = &Apache::longroup::get_group_settings($curr_group{$group}); - if (keys(%groupsettings) > 0) { - if (ref($groupsettings{'functions'}) eq 'HASH') { - if ($groupsettings{'functions'}{'discussion'} eq 'on') { - $noremove=1; - } - } - } - $nocut=1; - $nocopy=1; - } - } + my %denied = &action_restrictions($coursenum,$coursedom,$url, + $env{'form.folderpath'}, + $currgroups); my ($copylink,$cutlink,$removelink); my $skip_confirm = 0; if ( $folder =~ /^supplemental/ @@ -2678,48 +3416,62 @@ sub entryline { $skip_confirm = 1; } - if ($nocopy) { - $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=(<$lt{'cp'} +
    +$form_common +$lt{'cp'} +$form_end ENDCOPY + if (($ishash) && (ref($filtersref->{'cancopy'}) eq 'ARRAY')) { + push(@{$filtersref->{'cancopy'}},$orderidx); + } } - if ($nocut) { + 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=(<$lt{'ct'} + +$form_common + +$lt{'ct'} +$form_end ENDCUT + if (($ishash) && (ref($filtersref->{'cancut'}) eq 'ARRAY')) { + push(@{$filtersref->{'cancut'}},$orderidx); + } } - if ($noremove) { + 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);"; $removelink=(<$lt{'rm'} + +$form_common + +$lt{'rm'} +$form_end ENDREM + if (($ishash) && (ref($filtersref->{'canremove'}) eq 'ARRAY')) { + push(@{$filtersref->{'canremove'}},$orderidx); + } } - unless ($isexternal) { - $renamelink=(<$lt{'rn'} ENDREN - } - $form_start = ' - -'; - $form_common=(< - - - -END - $form_end = ''; $line.=(<
    @@ -2735,18 +3487,17 @@ END $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+)$/); @@ -2831,10 +3582,14 @@ END 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 = '
    '."\n". +$form_param."\n". $form_common."\n". ''; if ($rpicknum ne '') { @@ -2842,13 +3597,19 @@ $form_common."\n". } $rand_pick_text .= ''. $form_end; - my $ro_set= - ((&LONCAPA::map::getparameter($orderidx,'parameter_randomorder'))[0]=~/^yes$/i?' checked="checked"':''); - my $formname = 'edit_rorder_'.$orderidx; + my $ro_set; + if ((&LONCAPA::map::getparameter($orderidx,'parameter_randomorder'))[0]=~/^yes$/i) { + $ro_set = 'checked="checked"'; + if (($ishash) && (ref($filtersref->{'randomorder'}) eq 'ARRAY')) { + push(@{$filtersref->{'randomorder'}},$orderidx); + } + } + $formname = 'edit_rorder_'.$orderidx; $rand_order_text = ''."\n". +$form_param."\n". $form_common."\n". -''. +''. $form_end; } } elsif ($supplementalflag && !$allowed) { @@ -2919,22 +3680,33 @@ $form_end; my %lt=&Apache::lonlocal::texthash( 'hd' => 'Hidden', 'ec' => 'URL hidden'); - my $enctext= - ((&LONCAPA::map::getparameter($orderidx,'parameter_encrypturl'))[0]=~/^yes$/i?' checked="checked"':''); - my $hidtext= - ((&LONCAPA::map::getparameter($orderidx,'parameter_hiddenresource'))[0]=~/^yes$/i?' checked="checked"':''); + my ($enctext,$hidtext); + if ((&LONCAPA::map::getparameter($orderidx,'parameter_encrypturl'))[0]=~/^yes$/i) { + $enctext = ' checked="checked"'; + if (($ishash) && (ref($filtersref->{'encrypturl'}) eq 'ARRAY')) { + push(@{$filtersref->{'encrypturl'}},$orderidx); + } + } + if ((&LONCAPA::map::getparameter($orderidx,'parameter_hiddenresource'))[0]=~/^yes$/i) { + $hidtext = ' checked="checked"'; + if (($ishash) && (ref($filtersref->{'randomorder'}) eq 'ARRAY')) { + push(@{$filtersref->{'hiddenresource'}},$orderidx); + } + } my $formhidden = 'edit_hiddenresource_'.$orderidx; my $formurlhidden = 'edit_encrypturl_'.$orderidx; $line.=(< + $form_param $form_common - + $form_end
    + $form_param $form_common - + $form_end $rand_pick_text
    @@ -2945,10 +3717,75 @@ ENDPARMS return $line; } +sub action_restrictions { + my ($cnum,$cdom,$url,$folderpath,$currgroups) = @_; + my %denied = ( + cut => 0, + copy => 0, + remove => 0, + ); + if ($url=~ m{^/res/.+\.(page|sequence)$}) { + # no copy for published maps + $denied{'copy'} = 1; + } elsif ($url=~m{^/res/lib/templates/}) { + $denied{'copy'} = 1; + $denied{'cut'} = 1; + } elsif ($url eq "/uploaded/$cdom/$cnum/group_allfolders.sequence") { + if ($folderpath =~ /^default&[^\&]+$/) { + if ((ref($currgroups) eq 'HASH') && (keys(%{$currgroups}) > 0)) { + $denied{'remove'} = 1; + } + $denied{'cut'} = 1; + $denied{'copy'} = 1; + } + } elsif ($url =~ m{^\Q/uploaded/$cdom/$cnum/group_folder_\E(\w+)\.sequence$}) { + my $group = $1; + if ($folderpath =~ /^default&[^\&]+\&group_allfolders\&[^\&]+$/) { + if ((ref($currgroups) eq 'HASH') && (exists($currgroups->{$group}))) { + $denied{'remove'} = 1; + } + } + $denied{'cut'} = 1; + $denied{'copy'} = 1; + } elsif ($url =~ m{^\Q/adm/$cdom/$cnum/\E(\w+)/smppg$}) { + my $group = $1; + if ($folderpath =~ /^default&[^\&]+\&group_allfolders\&[^\&]+\&\Qgroup_folder_$group\E\&[^\&]+$/) { + if ((ref($currgroups) eq 'HASH') && (exists($currgroups->{$group}))) { + my %groupsettings = &Apache::longroup::get_group_settings($currgroups->{$group}); + if (keys(%groupsettings) > 0) { + $denied{'remove'} = 1; + } + $denied{'cut'} = 1; + $denied{'copy'} = 1; + } + } + } elsif ($folderpath =~ /^default&[^\&]+\&group_allfolders\&[^\&]+\&group_folder_(\w+)\&/) { + my $group = $1; + if ($url =~ /group_boards_\Q$group\E/) { + if ((ref($currgroups) eq 'HASH') && (exists($currgroups->{$group}))) { + my %groupsettings = &Apache::longroup::get_group_settings($currgroups->{$group}); + if (keys(%groupsettings) > 0) { + if (ref($groupsettings{'functions'}) eq 'HASH') { + if ($groupsettings{'functions'}{'discussion'} eq 'on') { + $denied{'remove'} = 1; + } + } + } + $denied{'cut'} = 1; + $denied{'copy'} = 1; + } + } + } + return %denied; +} + sub new_timebased_suffix { - my ($coursedom,$coursenum,$type,$area,$container) = @_; + my ($dom,$num,$type,$area,$container) = @_; my ($prefix,$namespace,$idtype,$errtext,$locknotfreed); - if ($type eq 'map') { + if ($type eq 'paste') { + $prefix = $type; + $namespace = 'courseeditor'; + } elsif ($type eq 'map') { $prefix = 'docs'; if ($area eq 'supplemental') { $prefix = 'supp'; @@ -2961,15 +3798,16 @@ sub new_timebased_suffix { } $idtype = 'concat'; my ($suffix,$freedlock,$error) = - &Apache::lonnet::get_timebased_id($prefix,'num',$namespace, - $coursedom,$coursenum); + &Apache::lonnet::get_timebased_id($prefix,'num',$namespace,$dom,$num); if (!$suffix) { - if ($type eq 'map') { + if ($type eq 'paste') { + $errtext = &mt('Failed to acquire a unique timestamp-based suffix when adding to the paste buffer.'); + } elsif ($type eq 'map') { $errtext = &mt('Failed to acquire a unique timestamp-based suffix for the new folder/page.'); } elsif ($type eq 'smppg') { $errtext = &mt('Failed to acquire a unique timestamp-based suffix for the new simple page.'); } else { - $errtext = &mt('Failed to acquire a unique timestamp-based suffix for the new bulletin board.'); + $errtext = &mt('Failed to acquire a unique timestamp-based suffix for the new discussion board.'); } if ($error) { $errtext .= '
    '.$error; @@ -2979,18 +3817,23 @@ sub new_timebased_suffix { $locknotfreed = '
    '. &mt('There was a problem removing a lockfile.').' '; - if ($type eq 'map') { + if ($type eq 'paste') { + &mt('This will prevent use of the paste buffer until th next log-in.'); + } elsif ($type eq 'map') { &mt('This will prevent creation of additional folders or composite pages in this course.'); } elsif ($type eq 'smppg') { $locknotfreed .= &mt('This will prevent creation of additional simple pages in this course.'); } else { $locknotfreed .= - &mt('This will prevent creation of additional bulletin boards in this course.'); + &mt('This will prevent creation of additional discussion boards in this course.'); + } + unless ($type eq 'paste') { + $locknotfreed .= + ' '.&mt('Please contact the [_1]helpdesk[_2] for assistance.', + '',''); } - $locknotfreed .= - ' '.&mt('Please contact the domain coordinator for your LON-CAPA domain.'). - '
    '; + $locknotfreed .= '
    '; } return ($suffix,$errtext,$locknotfreed); } @@ -3153,8 +3996,8 @@ sub list_symbs { sub verifycontent { my ($r) = @_; my $crstype = &Apache::loncommon::course_type(); - $r->print(&Apache::loncommon::start_page('Verify '.$crstype.' Documents')); - $r->print(&Apache::lonhtmlcommon::breadcrumbs('Verify '.$crstype.' Documents')); + $r->print(&Apache::loncommon::start_page('Verify '.$crstype.' Content')); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Verify '.$crstype.' Content')); $r->print(&startContentScreen('tools')); $r->print('

    '.&mt($crstype.' content verification').'

    '); $hashtied=0; @@ -3190,8 +4033,8 @@ sub devalidateversioncache { sub checkversions { my ($r) = @_; my $crstype = &Apache::loncommon::course_type(); - $r->print(&Apache::loncommon::start_page("Check $crstype Document Versions")); - $r->print(&Apache::lonhtmlcommon::breadcrumbs("Check $crstype Document Versions")); + $r->print(&Apache::loncommon::start_page("Check $crstype Resource Versions")); + $r->print(&Apache::lonhtmlcommon::breadcrumbs("Check $crstype Resource Versions")); $r->print(&startContentScreen('tools')); my $header=''; @@ -3256,7 +4099,7 @@ sub checkversions { &changewarning($r,''); if ($env{'form.timerange'} eq 'all') { # show all documents - $header=&mt('All Documents in '.$crstype); + $header=&mt('All content in '.$crstype); $allsel=' selected="selected"'; foreach my $key (keys(%hash)) { if ($key=~/^ids\_(\/res\/.+)$/) { @@ -3347,10 +4190,30 @@ $lt{'sc'}:

    $lt{'vers'}

    - ENDHEADERS #number of columns for version history + my %changedbytime; + foreach my $key (keys(%changes)) { + #excludes not versionable problems from resource version history: + next if ($key =~ /^\/res\/lib\/templates/); + my $chg; + if ($env{'form.timerange'} eq 'all') { + my ($root,$extension)=($key=~/^(.*)\.(\w+)$/); + $chg = &Apache::lonnet::metadata($root.'.'.$extension,'lastrevisiondate'); + } else { + $chg = $changes{$key}; + next if ($chg < $starttime); + } + push(@{$changedbytime{$chg}},$key); + } + if (keys(%changedbytime) == 0) { + &untiehash(); + $r->print(&mt('No content changes in imported content in specified time frame'). + &endContentScreen()); + return; + } $r->print( + ''. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). ''.&mt('Resources').''. @@ -3360,26 +4223,25 @@ ENDHEADERS ''.&mt('History').''. &Apache::loncommon::end_data_table_header_row() ); - foreach my $key (sort(keys(%changes))) { - #excludes not versionable problems from resource version history: - next unless ($changes{$key}>$starttime && $key !~ /^\/res\/lib\/templates/); - my ($root,$extension)=($key=~/^(.*)\.(\w+)$/); - my $currentversion=&Apache::lonnet::getversion($key); - if ($currentversion<0) { - $currentversion=''.&mt('Could not be determined.').''; - } - my $linkurl=&Apache::lonnet::clutter($key); - $r->print( - &Apache::loncommon::start_data_table_row(). - ''.&Apache::lonnet::gettitle($linkurl).'
    '. - ''.$linkurl.''. - ''.$currentversion.'
    ('. - &Apache::lonlocal::locallocaltime(&Apache::lonnet::metadata($root.'.'.$extension,'lastrevisiondate')).')
    '. - '' - ); - # Used in course - my $usedversion=$hash{'version_'.$linkurl}; - if (($usedversion) && ($usedversion ne 'mostrecent')) { + foreach my $chg (sort {$b <=> $a } keys(%changedbytime)) { + foreach my $key (sort(@{$changedbytime{$chg}})) { + my ($root,$extension)=($key=~/^(.*)\.(\w+)$/); + my $currentversion=&Apache::lonnet::getversion($key); + if ($currentversion<0) { + $currentversion=''.&mt('Could not be determined.').''; + } + my $linkurl=&Apache::lonnet::clutter($key); + $r->print( + &Apache::loncommon::start_data_table_row(). + ''.&Apache::lonnet::gettitle($linkurl).'
    '. + ''.$linkurl.''. + ''.$currentversion.'
    ('. + &Apache::lonlocal::locallocaltime($chg).')
    '. + '' + ); + # Used in course + my $usedversion=$hash{'version_'.$linkurl}; + if (($usedversion) && ($usedversion ne 'mostrecent')) { if ($usedversion != $currentversion) { $r->print(''.$usedversion.''); } else { @@ -3388,44 +4250,45 @@ ENDHEADERS } else { $r->print($currentversion); } - $r->print(''); - # Set version - $r->print(&Apache::loncommon::select_form( - $setversions{$linkurl}, - 'set_version_'.$linkurl, - {'select_form_order' => ['',1..$currentversion,'mostrecent'], - '' => '', - 'mostrecent' => &mt('most recent'), - map {$_,$_} (1..$currentversion)})); - my $lastold=1; - for (my $prevvers=1;$prevvers<$currentversion;$prevvers++) { - my $url=$root.'.'.$prevvers.'.'.$extension; - if (&Apache::lonnet::metadata($url,'lastrevisiondate')<$starttime) { - $lastold=$prevvers; - } - } - $r->print(''); - # List all available versions - $r->print(''); - for (my $prevvers=$lastold;$prevvers<$currentversion;$prevvers++) { - my $url=$root.'.'.$prevvers.'.'.$extension; - $r->print( - '' - .'' - .&mt('Version [_1]',$prevvers).'' - .' ('.&Apache::lonlocal::locallocaltime( - &Apache::lonnet::metadata($url,'lastrevisiondate')) - .')'); - if (&Apache::loncommon::fileembstyle($extension) eq 'ssi') { + $r->print(''); + # Set version + $r->print(&Apache::loncommon::select_form( + $setversions{$linkurl}, + 'set_version_'.$linkurl, + {'select_form_order' => ['',1..$currentversion,'mostrecent'], + '' => '', + 'mostrecent' => &mt('most recent'), + map {$_,$_} (1..$currentversion)})); + my $lastold=1; + for (my $prevvers=1;$prevvers<$currentversion;$prevvers++) { + my $url=$root.'.'.$prevvers.'.'.$extension; + if (&Apache::lonnet::metadata($url,'lastrevisiondate')<$starttime) { + $lastold=$prevvers; + } + } + $r->print(''); + # List all available versions + $r->print(''); + for (my $prevvers=$lastold;$prevvers<$currentversion;$prevvers++) { + my $url=$root.'.'.$prevvers.'.'.$extension; $r->print( - ' &'). - '" target="diffs">'.&mt('Diffs').''); + '' + .'' + .&mt('Version [_1]',$prevvers).'' + .' ('.&Apache::lonlocal::locallocaltime( + &Apache::lonnet::metadata($url,'lastrevisiondate')) + .')'); + if (&Apache::loncommon::fileembstyle($extension) eq 'ssi') { + $r->print( + ' &'). + '" target="diffs">'.&mt('Diffs').''); + } + $r->print('
    '); } - $r->print('

    '); + $r->print('
    '.&Apache::loncommon::end_data_table_row()); } - $r->print('
    '.&Apache::loncommon::end_data_table_row()); } $r->print( &Apache::loncommon::end_data_table(). @@ -3435,6 +4298,7 @@ ENDHEADERS &untiehash(); $r->print(&endContentScreen()); + return; } sub mark_hash_old { @@ -3489,13 +4353,13 @@ $help{'Caching'}.'

    '."\n\n"); sub init_breadcrumbs { - my ($form,$text)=@_; + my ($form,$text,$help)=@_; &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/coursedocs?tools=1", text=>&Apache::loncommon::course_type().' Editor', faq=>273, bug=>'Instructor Interface', - help => 'Docs_Adding_Course_Doc'}); + help => $help}); &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/coursedocs?".$form.'=1', text=>$text, faq=>273, @@ -3534,7 +4398,7 @@ sub startContentScreen { $output .= '      '.&mt('Content Index').'      '."\n"; $output .= '
  • '.&mt('Supplemental Content').'
  • '; } else { - $output .= '
  •       '.&mt('Content Editor').'      
  • '."\n"; + $output .= '
  •       '.&mt('Main Content Editor').'      
  • '."\n"; $output .= '
  • '.&mt('Supplemental Content Editor').'
  • '."\n"; $output .= '
  •       '.&mt('Content Utilities').'      
  • '."\n"; '>      '.&mt('Content Utilities').'      '; @@ -3555,7 +4419,7 @@ sub endContentScreen { } sub supplemental_base { - return 'supplemental&'.&escape(&mt('Supplemental '.&Apache::loncommon::course_type().' Content')); + return 'supplemental&'.&escape(&mt('Supplemental Content')); } sub handler { @@ -3579,8 +4443,8 @@ sub handler { 'Adding_Folders','Docs_Overview', 'Load_Map', 'Supplemental','Score_Upload_Form','Adding_Pages', 'Importing_LON-CAPA_Resource','Importing_IMS_Course', - 'Uploading_From_Harddrive', - 'Check_Resource_Versions','Verify_Content') { + 'Uploading_From_Harddrive','Course_Roster','Web_Page', + 'Dropbox','Simple_Problem') { $help{$topic}=&Apache::loncommon::help_open_topic('Docs_'.$topic); } # Composite help files @@ -3588,16 +4452,12 @@ sub handler { 'Docs_About_Syllabus,Docs_Editing_Templated_Pages'); $help{'Simple Page'} = &Apache::loncommon::help_open_topic( 'Docs_About_Simple_Page,Docs_Editing_Templated_Pages'); - $help{'Simple Problem'} = &Apache::loncommon::help_open_topic( - 'Option_Response_Simple'); $help{'Bulletin Board'} = &Apache::loncommon::help_open_topic( 'Docs_About_Bulletin_Board,Docs_Editing_Templated_Pages'); $help{'My Personal Information Page'} = &Apache::loncommon::help_open_topic( 'Docs_About_My_Personal_Info,Docs_Editing_Templated_Pages'); $help{'Group Portfolio'} = &Apache::loncommon::help_open_topic('Docs_About_Group_Files'); $help{'Caching'} = &Apache::loncommon::help_open_topic('Caching'); - $help{'Course Roster'} = &Apache::loncommon::help_open_topic('Docs_Course_Roster'); - $help{'Web Page'} = &Apache::loncommon::help_open_topic('Docs_Web_Page'); my $allowed; # URI is /adm/supplemental when viewing supplemental docs in non-edit mode. @@ -3612,7 +4472,7 @@ sub handler { &choose_dump_server($r); return OK; } elsif ($allowed && $env{'form.verify'}) { - &init_breadcrumbs('verify','Verify Content'); + &init_breadcrumbs('verify','Verify Content','Docs_Verify_Content'); &verifycontent($r); } elsif ($allowed && $env{'form.listsymbs'}) { &init_breadcrumbs('listsymbs','List Content IDs'); @@ -3625,10 +4485,10 @@ sub handler { } &docs_change_log($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath); } elsif ($allowed && $env{'form.versions'}) { - &init_breadcrumbs('versions','Check/Set Resource Versions'); + &init_breadcrumbs('versions','Check/Set Resource Versions','Docs_Check_Resource_Versions'); &checkversions($r); } elsif ($allowed && $env{'form.dumpcourse'}) { - &init_breadcrumbs('dumpcourse','Dump '.&Apache::loncommon::course_type().' Content to Authoring Space'); + &init_breadcrumbs('dumpcourse','Copy '.&Apache::loncommon::course_type().' Content to Authoring Space'); &dumpcourse($r); } elsif ($allowed && $env{'form.exportcourse'}) { &init_breadcrumbs('exportcourse','IMS Export'); @@ -3654,9 +4514,9 @@ sub handler { # supplementalflag=0: show standard documents # toolsflag=1: show utilities - - my $supplementalflag=($env{'form.folderpath'}=~/^supplemental/); - if (($env{'form.folderpath'}=~/^default/) || ($env{'form.folderpath'} eq "")) { + my $unesc_folderpath = &unescape($env{'form.folderpath'}); + my $supplementalflag=($unesc_folderpath=~/^supplemental/); + if (($unesc_folderpath=~/^default/) || ($unesc_folderpath eq "")) { $supplementalflag=0; } if ($env{'form.forcesupplement'}) { $supplementalflag=1; } @@ -3688,12 +4548,11 @@ sub handler { } } elsif ($env{'form.command'} eq 'editdocs') { $env{'form.folderpath'} = 'default&'. - &Apache::lonhtmlcommon::entity_encode('Main Course Content'). - ':::::'; + &escape(&mt('Main Content').':::::'); &Apache::lonnet::appenv({'docs.exit.'.$env{'request.course.id'} => $env{'form.command'}}); } elsif ($env{'form.command'} eq 'editsupp') { $env{'form.folderpath'} = 'supplemental&'. - &Apache::lonhtmlcommon::entity_encode('Supplemental Content'); + &escape('Supplemental Content'); &Apache::lonnet::appenv({'docs.exit.'.$env{'request.course.id'} => '/adm/supplemental'}); } elsif ($env{'form.command'} eq 'contents') { &Apache::lonnet::appenv({'docs.exit.'.$env{'request.course.id'} => '/adm/navmaps'}); @@ -3734,8 +4593,8 @@ sub handler { if ($supplementalflag) { $env{'form.folderpath'}=&supplemental_base(); } else { - $env{'form.folderpath'}='default'.&escape(&mt('Main '.$crstype.' Documents')). - ':::::'; + $env{'form.folderpath'}='default&'.&escape(&mt('Main Content'). + ':::::'); } } @@ -3760,8 +4619,7 @@ sub handler { if ($env{'form.folder'} eq '' || $env{'form.folder'} eq 'supplemental') { $folderpath='default&'. - &escape(&mt('Main '.$crstype.' Documents')). - ':::::'; + &escape(&mt('Main Content').':::::'); } } $containertag = ''; @@ -3798,9 +4656,11 @@ sub handler { $script .= &dump_switchserver_js(@hosts); } } else { + my $tid = 1; my @tabids; if ($supplementalflag) { @tabids = ('002','ee2','ff2'); + $tid = 2; } else { @tabids = ('aa1','bb1','cc1','ff1'); unless ($env{'form.folderpath'} =~ /\:1$/) { @@ -3812,15 +4672,13 @@ sub handler { $script .= &editing_js($udom,$uname,$supplementalflag). &history_tab_js(). &inject_data_js(). - &Apache::lonhtmlcommon::resize_scrollbox_js('docs',$tabidstr). + &Apache::lonhtmlcommon::resize_scrollbox_js('docs',$tabidstr,$tid). &Apache::lonextresedit::extedit_javascript(); $addentries = { onload => "javascript:resize_scrollbox('contentscroll','1','1');", }; } - if ($env{'docs.markedcopy_url'}) { - $script .= &paste_popup_js(); - } + $script .= &paste_popup_js(); my $confirm_switch = &mt("Editing requires switching to the resource's home server.").'\n'. &mt('Switch server?'); @@ -3839,6 +4697,14 @@ sub handler { if ($showdoc) { $r->print(&Apache::loncommon::start_page("$crstype documents",undef, {'force_register' => $showdoc,})); + } elsif ($toolsflag) { + &Apache::lonhtmlcommon::add_breadcrumb({ + href=>"/adm/coursedocs",text=>"$crstype Contents"}); + $r->print(&Apache::loncommon::start_page("$crstype Contents", $script) + .&Apache::loncommon::help_open_menu('','',273,'RAT') + .&Apache::lonhtmlcommon::breadcrumbs( + 'Editing Course Contents') + ); } elsif ($r->uri eq '/adm/supplemental') { my $brcrum = &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype); $r->print(&Apache::loncommon::start_page("Supplemental $crstype Content",undef, @@ -3846,7 +4712,6 @@ sub handler { } else { &Apache::lonhtmlcommon::add_breadcrumb({ href=>"/adm/coursedocs",text=>"$crstype Contents"}); - $r->print(&Apache::loncommon::start_page("$crstype Contents", $script, {'add_entries' => $addentries} ) @@ -3867,7 +4732,7 @@ sub handler { # Process file upload - phase one - upload and parse primary file. undef($hadchanges); $uploadphase = &process_file_upload(\$upload_output,$coursenum,$coursedom, - \%allfiles,\%codebase,$context); + \%allfiles,\%codebase,$context,$crstype); if ($hadchanges) { &mark_hash_old(); } @@ -3919,7 +4784,7 @@ sub handler { my %lt=&Apache::lonlocal::texthash( 'copm' => 'All documents out of a published map into this folder', 'upfi' => 'Upload File', - 'upld' => 'Import Content', + 'upld' => 'Upload Content', 'srch' => 'Search', 'impo' => 'Import', 'lnks' => 'Import from Stored Links', @@ -3940,8 +4805,8 @@ sub handler { 'grpo' => 'Group Portfolio', 'rost' => 'Course Roster', 'abou' => 'Personal Information Page for a User', - 'imsf' => 'IMS Import', - 'imsl' => 'Import IMS package', + 'imsf' => 'IMS Upload', + 'imsl' => 'Upload IMS package', 'cms' => 'Origin of IMS package', 'se' => 'Select', 'file' => 'File', @@ -4047,7 +4912,10 @@ SEDFFORM if ($folder eq '') { $folder='default'; } - &update_paste_buffer($coursenum,$coursedom,$folder); + my $output = &update_paste_buffer($coursenum,$coursedom,$folder); + if ($output) { + $r->print($output); + } $r->print(< @@ -4086,7 +4954,7 @@ HIDDENFORM if ($folder eq '' || $supplementalflag) { $folder='default'; $savefolderpath = $env{'form.folderpath'}; - $env{'form.folderpath'}='default&'.&escape(&mt('Content')); + $env{'form.folderpath'}='default&'.&escape(&mt('Main Content')); $pathitem = ''; } @@ -4107,7 +4975,7 @@ HIDDENFORM my $newnavform=(< - + $pathitem @@ -4117,7 +4985,7 @@ HIDDENFORM NNFORM my $newsmppageform=(< - + $pathitem $lt{'sipa'} @@ -4131,7 +4999,7 @@ NSPFORM $pathitem $lt{'sipr'} - $help{'Simple Problem'} + $help{'Simple_Problem'} NSPROBFORM @@ -4142,6 +5010,7 @@ NSPROBFORM $pathitem $lt{'drbx'} + $help{'Dropbox'} NDBFORM @@ -4157,7 +5026,7 @@ NEXUFORM my $newbulform=(< - + $pathitem $lt{'bull'} @@ -4167,7 +5036,7 @@ NBFORM my $newaboutmeform=(< - + $pathitem @@ -4178,7 +5047,7 @@ NAMFORM my $newaboutsomeoneform=(< - + $pathitem $lt{'abou'} @@ -4187,12 +5056,12 @@ NASOFORM my $newrosterform=(< - + $pathitem $lt{'rost'} - $help{'Course Roster'} + $help{'Course_Roster'} NROSTFORM @@ -4208,11 +5077,11 @@ NROSTFORM } my $newwebpageform =(< - + $pathitem $lt{'webp'} - $help{'Web Page'} + $help{'Web_Page'} NWEBFORM @@ -4232,7 +5101,7 @@ my $newfolderb;
    - + $lt{'newp'} $help{'Adding_Pages'}
    @@ -4243,14 +5112,14 @@ NPFORM
    $pathitem - + $lt{'newf'}$help{'Adding_Folders'}
    NFFORM my $newsylform=(< - + $pathitem @@ -4262,7 +5131,7 @@ NSYLFORM my $newgroupfileform=(< - + $pathitem @@ -4289,7 +5158,7 @@ NGFFORM ); } push(@importdoc, - {''.$lt{upl}.''=>$fileuploadform} + {''.$lt{upl}.''=>$fileuploadform} ); $fileuploadform = &create_form_ul(&create_list_elements(@importdoc)); @@ -4311,14 +5180,14 @@ NGFFORM $communityform = &create_form_ul(&create_list_elements(@communityforma)); my %orderhash = ( - 'aa' => ['Import Content',$fileuploadform], - 'bb' => ['Published Content',$importpubform], - 'cc' => ['Grading Resources',$gradingform], + 'aa' => ['Upload',$fileuploadform], + 'bb' => ['Import',$importpubform], + 'cc' => ['Grading',$gradingform], ); unless ($container eq 'page') { $orderhash{'00'} = ['Newfolder',$newfolderform]; $orderhash{'dd'} = ['Collaboration',$communityform]; - $orderhash{'ee'} = ['Special Pages',$specialdocumentsform]; + $orderhash{'ee'} = ['Other',$specialdocumentsform]; } $hadchanges=0; @@ -4379,7 +5248,7 @@ SUPDOCFORM my $supnewfolderform=(< - + $pathitem $lt{'newf'} @@ -4429,7 +5298,7 @@ SNAMFORM $pathitem $lt{'webp'} - $help{'Web Page'} + $help{'Web_Page'} SWEBFORM @@ -4452,15 +5321,28 @@ my @supimportdoc = ( $supupdocform = &create_form_ul(&create_list_elements(@supimportdoc)); my %suporderhash = ( '00' => ['Supnewfolder', $supnewfolderform], - 'ee' => ['Import Content',$supupdocform], - 'ff' => ['Special Pages',&create_form_ul(&create_list_elements(@specialdocs))] + 'ee' => ['Upload',$supupdocform], + 'ff' => ['Other',&create_form_ul(&create_list_elements(@specialdocs))] ); if ($supplementalflag) { my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, $supplementalflag,\%suporderhash,$iconpath,$pathitem); if ($error) { $r->print('

    '.$error.'

    '); - } + } else { + if ($suppchanges) { + my %servers = &Apache::lonnet::internet_dom_servers($coursedom); + my @ids=&Apache::lonnet::current_machine_ids(); + foreach my $server (keys(%servers)) { + next if (grep(/^\Q$server\E$/,@ids)); + my $hashid=$coursenum.':'.$coursedom; + my $cachekey = &escape('suppcount').':'.&escape($hashid); + &Apache::lonnet::remote_devalidate_cache($server,[$cachekey]); + } + &Apache::lonnet::get_numsuppfiles($coursenum,$coursedom,1); + undef($suppchanges); + } + } } } elsif ($supplementalflag) { my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, @@ -4607,8 +5489,8 @@ sub remove_archive { } else { my $currcmd = $env{'form.cmd'}; my $position = $env{'form.position'}; - if ($position > 0) { - $env{'form.cmd'} = 'del_'.$position; + if ($position > 0) { + $env{'form.cmd'} = 'remove_'.$position; my ($title,$url,@rrest) = split(/:/,$LONCAPA::map::resources[$LONCAPA::map::order[$position]]); if (&handle_edit_cmd($docuname,$docudom)) { @@ -4643,13 +5525,13 @@ sub generate_admin_menu { my ($crstype) = @_; my $lc_crstype = lc($crstype); my ($home,$other,%outhash)=&authorhosts(); - my %lt=&Apache::lonlocal::texthash ( + my %lt= ( # do not translate here 'vc' => 'Verify Content', 'cv' => 'Check/Set Resource Versions', 'ls' => 'List Resource Identifiers', 'imse' => 'Export contents to IMS Archive', - 'dcd' => "Dump $crstype Content to Authoring Space", - ); + 'dcd' => "Copy $crstype Content to Authoring Space", + ); my ($candump,$dumpurl); if ($home + $other > 0) { $candump = 'F'; @@ -4660,7 +5542,7 @@ sub generate_admin_menu { foreach my $aurole (keys(%outhash)) { unless(grep(/^\Q$outhash{$aurole}\E/,@hosts)) { push(@hosts,$outhash{$aurole}); - } + } } if (@hosts == 1) { my $switchto = '/adm/switchserver?otherserver='.$hosts[0]. @@ -4679,14 +5561,14 @@ sub generate_admin_menu { { linktext => $lt{'vc'}, url => "javascript:injectData(document.courseverify,'dummy','verify','$lt{'vc'}')", permission => 'F', - help => 'Verify_Content', + help => 'Docs_Verify_Content', icon => 'verify.png', linktitle => 'Verify contents can be retrieved/rendered', }, { linktext => $lt{'cv'}, url => "javascript:injectData(document.courseverify,'dummy','versions','$lt{'cv'}')", permission => 'F', - help => 'Check_Resource_Versions', + help => 'Docs_Check_Resource_Versions', icon => 'resversion.png', linktitle => "View version information for resources in your $lc_crstype, and fix/unfix use of specific versions", }, @@ -4711,7 +5593,7 @@ sub generate_admin_menu { { linktext => $lt{'dcd'}, url => $dumpurl, permission => $candump, - #help => '', + help => 'Docs_Dump_Course_Docs', icon => 'dump.png', linktitle => $lt{'dcd'}, }, @@ -4724,13 +5606,14 @@ sub generate_admin_menu { } sub generate_edit_table { - my ($tid,$orderhash_ref,$to_show,$iconpath,$jumpto,$readfile) = @_; + my ($tid,$orderhash_ref,$to_show,$iconpath,$jumpto,$readfile, + $need_save,$copyfolder) = @_; return unless(ref($orderhash_ref) eq 'HASH'); my %orderhash = %{$orderhash_ref}; my $form; my $activetab; my $active; - if (($env{'form.active'} ne '') && ($env{'form.active'} ne 'aa')) { + if (($env{'form.active'} ne '') && ($env{'form.active'} ne '00')) { $activetab = $env{'form.active'}; } my $backicon = $iconpath.'clickhere.gif'; @@ -4738,7 +5621,7 @@ sub generate_edit_table { $form = '
    '. '