--- loncom/interface/londocs.pm 2012/01/31 23:47:15 1.476 +++ loncom/interface/londocs.pm 2012/12/13 02:35:08 1.484.2.14 @@ -1,7 +1,7 @@ # The LearningOnline Network # Documents # -# $Id: londocs.pm,v 1.476 2012/01/31 23:47:15 raeburn Exp $ +# $Id: londocs.pm,v 1.484.2.14 2012/12/13 02:35:08 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,8 +26,6 @@ # http://www.lon-capa.org/ # - - package Apache::londocs; use strict; @@ -42,7 +40,9 @@ use Apache::lonxml; use Apache::lonclonecourse; use Apache::lonnavmaps; use Apache::lonnavdisplay(); +use Apache::lonuserstate(); use HTML::Entities; +use HTML::TokeParser; use GDBM_File; use Apache::lonlocal; use Cwd; @@ -69,10 +69,14 @@ sub mapread { } sub storemap { - my ($coursenum,$coursedom,$map)=@_; + my ($coursenum,$coursedom,$map,$contentchg)=@_; + my $report; + if (($contentchg) && ($map =~ /^default/)) { + $report = 1; + } my ($outtext,$errtext)= &LONCAPA::map::storemap('/uploaded/'.$coursedom.'/'.$coursenum.'/'. - $map,1); + $map,1,$report); if ($errtext) { return ($errtext,2); } $hadchanges=1; @@ -102,12 +106,17 @@ sub authorhosts { my $allowed=0; my $myhome=&Apache::lonnet::homeserver($ca,$cd); my @ids=&Apache::lonnet::current_machine_ids(); - foreach my $id (@ids) { if ($id eq $myhome) { $allowed=1; } } + foreach my $id (@ids) { + if ($id eq $myhome) { + $allowed=1; + last; + } + } if ($allowed) { $home++; - $outhash{'home_'.$ca.'@'.$cd}=1; + $outhash{'home_'.$ca.':'.$cd}=1; } else { - $outhash{'otherhome_'.$ca.'@'.$cd}=$myhome; + $outhash{'otherhome_'.$ca.':'.$cd}=$myhome; $other++; } } @@ -116,27 +125,6 @@ sub authorhosts { } -sub dumpbutton { - my ($home,$other,%outhash)=&authorhosts(); - my $crstype = &Apache::loncommon::course_type(); - if ($home+$other==0) { return ''; } - if ($home) { - my $link = - "" - .&mt('Dump '.$crstype.' Documents to Construction Space') - .''; - return - $link.' ' - .&Apache::loncommon::help_open_topic('Docs_Dump_Course_Docs') - .'
'; - } else { - return - &mt('Dump '.$crstype.' Documents to Construction Space: available on other servers'); - } -} - sub clean { my ($title)=@_; $title=~s/[^\w\/\!\$\%\^\*\-\_\=\+\;\:\,\\\|\`\~]+/\_/gs; @@ -148,16 +136,22 @@ sub clean { sub dumpcourse { my ($r) = @_; my $crstype = &Apache::loncommon::course_type(); - $r->print(&Apache::loncommon::start_page('Dump '.$crstype.' Documents to Construction Space'). - '
'); - $r->print(&Apache::lonhtmlcommon::breadcrumbs('Dump '.$crstype.' Documents to Construction Space')); + $r->print(&Apache::loncommon::start_page('Dump '.$crstype.' Content to Authoring Space')."\n". + &Apache::lonhtmlcommon::breadcrumbs('Dump '.$crstype.' Content to Authoring Space')."\n"); + $r->print(&startContentScreen('tools')); my ($home,$other,%outhash)=&authorhosts(); - unless ($home) { return ''; } + unless ($home) { + $r->print(&endContentScreen()); + return ''; + } my $origcrsid=$env{'request.course.id'}; my %origcrsdata=&Apache::lonnet::coursedescription($origcrsid); if (($env{'form.authorspace'}) && ($env{'form.authorfolder'}=~/\w/)) { # Do the dumping - unless ($outhash{'home_'.$env{'form.authorspace'}}) { return ''; } + unless ($outhash{'home_'.$env{'form.authorspace'}}) { + $r->print(&endContentScreen()); + return ''; + } my ($ca,$cd)=split(/\@/,$env{'form.authorspace'}); $r->print('

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

'); my $title=$env{'form.authorfolder'}; @@ -208,10 +202,15 @@ sub dumpcourse { } } } else { + $r->print(&mt('Searching ...').'
'); + $r->rflush(); # Input form + $r->print(''."\n"); unless ($home==1) { - $r->print( - '

'.&mt('Select the Construction Space').'

'); } foreach my $key (sort(keys(%outhash))) { if ($key=~/^home_(.+)$/) { @@ -220,20 +219,23 @@ sub dumpcourse { ''); } else { $r->print(''); + &Apache::loncommon::plainname(split(/\:/,$1)).''); } } } unless ($home==1) { - $r->print(''); + $r->print(''."\n"); } my $title=$origcrsdata{'description'}; $title=~s/[\/\s]+/\_/gs; $title=&clean($title); - $r->print('

'.&mt('Folder in Construction Space').'

' - .'
'); + $r->print('
'. + '
'.&mt('Folder in Authoring Space').''. + ''. + '

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

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

' + $r->print('

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

' .&Apache::loncommon::start_data_table() .&Apache::loncommon::start_data_table_header_row() .''.&mt('Internal Filename').'' @@ -261,14 +263,9 @@ sub dumpcourse { $r->print(&Apache::loncommon::end_data_table()); &untiehash(); $r->print( - '

'); + '

'); } -} - -sub exportbutton { - my $crstype = &Apache::loncommon::course_type(); - return "".&mt('IMS Export')."". - &Apache::loncommon::help_open_topic('Docs_Export_Course_Docs').'
'; + $r->print(&endContentScreen()); } sub group_import { @@ -312,7 +309,7 @@ sub group_import { join(':', ($name, $url, $ext, 'normal', 'res')); } } - return &storemap($coursenum, $coursedom, $folder.'.'.$container); + return &storemap($coursenum, $coursedom, $folder.'.'.$container,1); } sub breadcrumbs { @@ -325,7 +322,6 @@ sub breadcrumbs { @folders=split('&',$env{'form.folderpath'}); } my $folderpath; - my $cpinfo=''; my $plain=''; my $randompick=-1; my $isencrypted=0; @@ -358,7 +354,7 @@ sub breadcrumbs { $name = &mt('Supplemental '.$crstype.' Content'); } &Apache::lonhtmlcommon::add_breadcrumb( - {'href'=>$url.$cpinfo, + {'href'=>$url, 'title'=>$name, 'text'=>$name, 'no_mt'=>1, @@ -372,7 +368,7 @@ sub breadcrumbs { } sub log_docs { - return &Apache::lonnet::instructor_log('docslog',@_); + return &Apache::lonnet::write_log('course','docslog',@_); } { @@ -432,24 +428,51 @@ sub log_docs { } } - - - - sub docs_change_log { - my ($r)=@_; - my $folder=$env{'form.folder'}; - $r->print(&Apache::loncommon::start_page('Course Document Change Log')); - $r->print(&Apache::lonhtmlcommon::breadcrumbs('Course Document Change Log')); + my ($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath)=@_; + my $supplementalflag=($env{'form.folderpath'}=~/^supplemental/); + my $js = ''."\n"; + $r->print(&Apache::loncommon::start_page('Content Change Log',$js)); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Content Change Log')); + $r->print(&startContentScreen(($supplementalflag?'suppdocs':'docs'))); + my %orderhash; + my $container='sequence'; + my $pathitem; + if ($env{'form.pagepath'}) { + $container='page'; + $pathitem = ''; + } else { + my $folderpath=$env{'form.folderpath'}; + if ($folderpath eq '') { + $folderpath = 'default&'.&escape(&mt('Main '.$crstype.' Documents')); + } + $pathitem = ''; + } + my $readfile="/uploaded/$coursedom/$coursenum/$folder.$container"; + my $jumpto = $readfile; + $jumpto =~ s{^/}{}; + my $tid = 1; + if ($supplementalflag) { + $tid = 2; + } + my ($breadcrumbtrail) = &breadcrumbs($allowed,$crstype); + $r->print($breadcrumbtrail. + &generate_edit_table($tid,\%orderhash,undef,$iconpath,$jumpto, + $readfile)); my %docslog=&Apache::lonnet::dump('nohist_docslog', $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'}); if ((keys(%docslog))[0]=~/^error\:/) { undef(%docslog); } - $r->print('
'. - ''); - my %saveable_parameters = ('show' => 'scalar',); &Apache::loncommon::store_course_settings('docs_log', \%saveable_parameters); @@ -463,9 +486,14 @@ sub docs_change_log { 'randomorder' => 'Randomly ordered', 'set' => 'set to', 'del' => 'deleted'); - $r->print(&Apache::loncommon::display_filter(). - ''. - '
'); + my $filter = &Apache::loncommon::display_filter('docslog')."\n". + $pathitem."\n". + ''. + (' 'x2).''; + $r->print('
'. + '
'.&mt('Display of Content Changes').''."\n". + &makedocslogform($filter,1). + '

'); $r->print(&Apache::loncommon::start_data_table().&Apache::loncommon::start_data_table_header_row(). ''.&mt('Time').''.&mt('User').''.&mt('Folder').''.&mt('Before').''. &mt('After').''. @@ -509,18 +537,30 @@ sub docs_change_log { ':'.$docslog{$id}{'exe_udom'}.''. $send_msg_link.''. $docslog{$id}{'logentry'}{'folder'}.''); + my $is_supp = 0; + if ($docslog{$id}{'logentry'}{'currentfolder'} =~ /^supplemental/) { + $is_supp = 1; + } # Before for (my $idx=0;$idx<=$docslog{$id}{'logentry'}{'maxidx'};$idx++) { my $oldname=(split(/\:/,$docslog{$id}{'logentry'}{'before_resources_'.$idx}))[0]; my $newname=(split(/\:/,$docslog{$id}{'logentry'}{'after_resources_'.$idx}))[0]; if ($oldname ne $newname) { - $r->print(&LONCAPA::map::qtescape($oldname)); + my $shown = &LONCAPA::map::qtescape($oldname); + if ($is_supp) { + $shown = &Apache::loncommon::parse_supplemental_title($shown); + } + $r->print($shown); } } $r->print(''); @@ -531,13 +571,21 @@ sub docs_change_log { my $oldname=(split(/\:/,$docslog{$id}{'logentry'}{'before_resources_'.$idx}))[0]; my $newname=(split(/\:/,$docslog{$id}{'logentry'}{'after_resources_'.$idx}))[0]; if ($oldname ne '' && $oldname ne $newname) { - $r->print(&LONCAPA::map::qtescape($newname)); + my $shown = &LONCAPA::map::qtescape($newname); + if ($is_supp) { + $shown = &Apache::loncommon::parse_supplemental_title(&LONCAPA::map::qtescape($newname)); + } + $r->print($shown); } } $r->print(''); @@ -560,11 +608,14 @@ sub docs_change_log { if (!($env{'form.show'} eq &mt('all') || $shown<=$env{'form.show'})) { last; } } - $r->print(&Apache::loncommon::end_data_table()); + $r->print(&Apache::loncommon::end_data_table()."\n". + &makesimpleeditform($pathitem)."\n". + ''); + $r->print(&endContentScreen()); } sub update_paste_buffer { - my ($coursenum,$coursedom) = @_; + my ($coursenum,$coursedom,$folder) = @_; return if (!defined($env{'form.markcopy'})); return if (!defined($env{'form.copyfolder'})); @@ -579,35 +630,128 @@ sub update_paste_buffer { 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) = &parse_supplemental_title($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//}; - &Apache::lonnet::appenv({'docs.markedcopy_title' => $title, - 'docs.markedcopy_url' => $url}); + (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; + } + if ($nestednames ne '') { + $addtoenv{'docs.markedcopy_nestednames'} = $nestednames; + } + } + } + &Apache::lonnet::appenv(\%addtoenv); delete($env{'form.markcopy'}); } +sub recurse_uploaded_maps { + my ($url,$dir,$hierarchy,$titlesref,$nestref,$namesref) = @_; + if (ref($hierarchy->{$url}) eq 'HASH') { + my @maps = map { $hierarchy->{$url}{$_}; } sort { $a <=> $b } (keys(%{$hierarchy->{$url}})); + my @titles = map { $titlesref->{$url}{$_}; } sort { $a <=> $b } (keys(%{$titlesref->{$url}})); + my (@uploaded,@names,%shorter); + for (my $i=0; $i<@maps; $i++) { + my ($inner) = ($maps[$i] =~ m{^/uploaded/$match_domain/$match_courseid/(?:default|supplemental)_(\d+)\.(?:page|sequence)$}); + if ($inner ne '') { + push(@uploaded,$inner); + push(@names,&escape($titles[$i])); + $shorter{$maps[$i]} = $inner; + } + } + $$nestref .= "$dir:".join(',',@uploaded).'&'; + $$namesref .= "$dir:".(join(',',@names)).'___&&&___'; + foreach my $map (@maps) { + if ($shorter{$map} ne '') { + &recurse_uploaded_maps($map,$shorter{$map},$hierarchy,$titlesref,$nestref,$namesref); + } + } + } + return; +} + sub print_paste_buffer { - my ($r,$container) = @_; + my ($r,$container,$folder,$coursedom,$coursenum) = @_; return if (!defined($env{'docs.markedcopy_url'})); - $r->print('
' - .''.&mt('Clipboard').'' - .'
' - .' ' - ); - - my $type; + 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; + } + + my ($canpaste,$nopaste,$othercrs,$areachange,$is_uploaded_map); + 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; + } + + 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).'); + } + } 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; + } + } + } + + $r->print('
' + .''.&mt('Clipboard').''); + my ($type,$buffer); + if ($is_external) { $type = &mt('External Resource'); - $r->print($type.': '. + $buffer = $type.': '. &LONCAPA::map::qtescape($env{'docs.markedcopy_title'}).' ('. - &LONCAPA::map::qtescape($env{'docs.markedcopy_url'}).')'); + &LONCAPA::map::qtescape($env{'docs.markedcopy_url'}).')'; } else { - my $extension = (split(/\./,$env{'docs.markedcopy_url'}))[-1]; my $icon = &Apache::loncommon::icon($extension); if ($extension eq 'sequence' && $env{'docs.markedcopy_url'} =~ m{/default_\d+\.sequence$ }x) { @@ -615,116 +759,449 @@ sub print_paste_buffer { $icon .= '/navmap.folder.closed.gif'; } $icon = ''; - $r->print($icon.$type.': '. &parse_supplemental_title(&LONCAPA::map::qtescape($env{'docs.markedcopy_title'}))); + $buffer = $icon.$type.': '. &Apache::loncommon::parse_supplemental_title(&LONCAPA::map::qtescape($env{'docs.markedcopy_title'})); } - if ($container eq 'page') { - $r->print(' + if ($canpaste) { + $r->print(''.$buffer); + if (($is_uploaded_map) && (!$areachange)) { + if ((!$othercourse) && ($env{'docs.markedcopy_cmd'} eq 'cut')) { + $r->print((' 'x 4).''. + ''. + &mt('Show Paste Options').'
'. + '
'.(' 'x 4). + ''.(' ' x2). + '
'); + if ($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); + if ($container eq 'page') { + $r->print(' '); - } else { - $r->print(' + } else { + $r->print(' '); + } + $r->print(''); + } else { + $r->print(&mt('Paste buffer contains:').' '.$buffer. + '

'.$nopaste.'

'); + } + $r->print('
'); +} + +sub recurse_print { + my ($r,$dir,$deps,$display) = @_; + $r->print($display->{$dir}."\n"); + if (ref($deps->{$dir}) eq 'ARRAY') { + foreach my $subdir (@{$deps->{$dir}}) { + &recurse_print($r,$subdir,$deps,$display); + } + } +} + +sub supp_pasteable { + my ($url) = @_; + if (($url =~ m{^(?:/adm/wrapper/ext|(?:http|https)(?::|:))//}) || + (($url =~ /\.sequence$/) && ($url =~ m{^/uploaded/})) || + ($url =~ m{^/uploaded/$match_domain/$match_courseid/(docs|supplemental)/(default|\d+)/\d+/}) || + ($url =~ m{^/adm/$match_domain/$match_username/aboutme}) || + ($url =~ m{^/public/$match_domain/$match_courseid/syllabus})) { + return 1; } - $r->print('
'); + return; +} + +sub paste_popup_js { + my %lt = &Apache::lonlocal::texthash( + show => 'Show Paste Options', + hide => 'Hide Paste Options', + ); + 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'}
'; + return; +} + +function hidePasteOptions() { + document.getElementById('pasteoptions').style.display='none'; + document.getElementById('pasteoptionstext').innerHTML ='$lt{'show'}'; + return; } +END + +} + + sub do_paste_from_buffer { - my ($coursenum,$coursedom,$folder) = @_; + my ($coursenum,$coursedom,$folder,$container,$errors) = @_; +# Early out if paste buffer is empty if (!$env{'form.pastemarked'}) { - return; + return (); + } + +# 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')); + } } -# paste resource to end of list +# 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'}); -# Maps need to be copied first - if (($url=~/\.(page|sequence)$/) && ($url=~/^\/uploaded\//)) { - $title=&mt('Copy of').' '.$title; - my $newid=$$.int(rand(100)).time; - my ($oldid,$ext) = ($url=~/^(.+)\.(\w+)$/); - if ($oldid =~ m{^(/uploaded/\Q$coursedom\E/\Q$coursenum\E/)(\D+)(\d+)$}) { - my $path = $1; - my $prefix = $2; - my $ancestor = $3; - if (length($ancestor) > 10) { - $ancestor = substr($ancestor,-10,10); + + 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; +# 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.')); } - $oldid = $path.$prefix.$ancestor; } - my $counter = 0; - my $newurl=$oldid.$newid.'.'.$ext; - my $is_unique = &uniqueness_check($newurl); - while (!$is_unique && $counter < 100) { - $counter ++; - $newid ++; - $newurl = $oldid.$newid; - $is_unique = &uniqueness_check($newurl); +# 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 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); + } + } + foreach my $item (@nested) { + if ($env{'form.docs.markedcopy_'.$item} eq 'move') { + $tomove{$type.'_'.$item} = 1; + } + } } - if (!$is_unique) { - if ($url=~/\.page$/) { - return &mt('Paste failed: an error occurred creating a unique URL for the composite page'); - } else { - return &mt('Paste failed: an error occurred creating a unique URL for the folder'); + } + +# 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) { + if ($folder =~ /^default/) { + my $lastchange = &Apache::lonnet::get_coursechange($coursedom,$coursenum); + if ($lastchange > $env{'request.course.tied'}) { + &reinit_role($coursedom,$coursenum,$env{"course.$env{'request.course.id'}.home"}); } } - my $storefn=$newurl; - $storefn=~s{^/\w+/$match_domain/$match_username/}{}; - my $paste_map_result = - &Apache::lonclonecourse::writefile($env{'request.course.id'},$storefn, - &Apache::lonnet::getfile($url)); - if ($paste_map_result eq '/adm/notfound.html') { - if ($url=~/\.page$/) { - return &mt('Paste failed: an error occurred saving the composite page'); - } else { - return &mt('Paste failed: an error occurred saving the folder'); +# If pasting a map, check if map contains other maps + my (%allmaps,%hierarchy,%titles); + if ($folder =~ /^default/) { + my $navmap = Apache::lonnavmaps::navmap->new(); + if (defined($navmap)) { + foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) { + $allmaps{$res->src()} = 1; + } } } - $url = $newurl; - } + &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; + } + } + } + if (($srcdom ne $coursedom) || ($srcnum ne $coursenum) || ($prefixchg) || + (($newurl ne '') && ($newurl ne $url))) { + unless (&url_paste_fixups($url,$folder,$prefixchg,$coursedom,$coursenum, + \%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,\%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 - if (($url=~/\.(page|sequence)$/) && ($url=~m {^/res/})) { - &Apache::lonnet::delenv('docs.markedcopy'); + &Apache::lonnet::delenv('docs.markedcopy'); +# if pasting published map (main content are only) check map is not already in course + if ($folder =~ /^default/) { + if ($allmaps{$url}) { + return (&mt('Paste failed: only one instance of a particular published sequence or page is allowed within each course.')); + } + } + } } if ($url=~ m{/smppg$}) { - my $db_name = &Apache::lonsimplepage::get_db_name($url); - if ($db_name =~ /^smppage_/) { - #simple pages, need to copy the db contents to a new one. - my %contents=&Apache::lonnet::dump($db_name,$coursedom,$coursenum); - my $now = time(); - $db_name =~ s{_\d*$ }{_$now}x; - my $result=&Apache::lonnet::put($db_name,\%contents, - $coursedom,$coursenum); - $url =~ s{/(\d*)/smppg$ }{/$now/smppg}x; - $title=&mt('Copy of').' '.$title; - } + my $db_name = &Apache::lonsimplepage::get_db_name($url); + if ($db_name =~ /^smppage_/) { + #simple pages, need to copy the db contents to a new one. + my %contents=&Apache::lonnet::dump($db_name,$coursedom,$coursenum); + my $now = time(); + $db_name =~ s{_\d*$ }{_$now}x; + my $dbresult=&Apache::lonnet::put($db_name,\%contents, + $coursedom,$coursenum); + if ($dbresult eq 'ok') { + $url =~ s{/(\d*)/smppg$ }{/$now/smppg}x; + $title=&mt('Copy of').' '.$title; + } else { + return (&mt('Paste failed: An error occurred when copying the simple page.')); + } + } } $title = &LONCAPA::map::qtunescape($title); my $ext='false'; if ($url=~m{^http(|s)://}) { $ext='true'; } $url = &LONCAPA::map::qtunescape($url); + +# 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) { # Now insert the URL at the bottom - my $newidx = &LONCAPA::map::getresidx($url); - if ($env{'docs.markedcopy_supplemental'}) { - if ($folder =~ /^supplemental/) { - $title = $env{'docs.markedcopy_supplemental'}; + $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)) { + 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; + } + } + } + } + } +# Apply any changes to maps, or copy dependencies for uploaded HTML pages + my ($result,$save_err); + $result = + &apply_fixups($folder,$is_map,$prefixchg,$coursedom,$coursenum,$oldurl, + $url,\%removefrommap,\%removeparam,\%rewrites,\%retitles, + \%copies,\%dbcopies,\%zombies,\%params,\%docmoves, + \%mapmoves,\%newsubdir,$errors,\%before,\%after); + if ($result eq 'ok') { + if ($is_map) { + my ($errtext,$fatal) = &mapread($coursenum,$coursedom, + $folder.'.'.$container); + return $errtext if ($fatal); + + if ($#LONCAPA::map::order<1) { + my $idx=&LONCAPA::map::getresidx(); + if ($idx<=0) { $idx=1; } + $LONCAPA::map::order[0]=$idx; + $LONCAPA::map::resources[$idx]=''; + } + $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 { - (undef,undef,$title) = - &parse_supplemental_title($env{'docs.markedcopy_supplemental'}); + if ($folder=~/^supplemental/) { + $title=time.'___&&&___'.$env{'user.name'}.'___&&&___'. + $env{'user.domain'}.'___&&&___'.$title; + } } - } else { - if ($folder=~/^supplemental/) { - $title=time.'___&&&___'.$env{'user.name'}.'___&&&___'. - $env{'user.domain'}.'___&&&___'.$title; + $LONCAPA::map::resources[$newidx]= $title.':'.$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; } } - $LONCAPA::map::resources[$newidx]= $title.':'.$url.':'.$ext.':normal:res'; - push(@LONCAPA::map::order, $newidx); - return 'ok'; -# Store the result + 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'); + } + return ($result,$save_err); +} + +sub get_newmap_url { + my ($url,$folder,$prefixchg,$coursedom,$coursenum,$srcdom,$srcnum, + $titleref,$allmaps,$newurls) = @_; + my $newurl; + if ($url=~ m{^/uploaded/}) { + $$titleref=&mt('Copy of').' '.$$titleref; + } + my $now = time; + my $suffix=$$.int(rand(100)).$now; + my ($oldid,$ext) = ($url=~/^(.+)\.(\w+)$/); + if ($oldid =~ m{^(/uploaded/$match_domain/$match_courseid/)(\D+)(\d+)$}) { + my $path = $1; + my $prefix = $2; + my $ancestor = $3; + if (length($ancestor) > 10) { + $ancestor = substr($ancestor,-10,10); + } + my $newid; + if ($prefixchg) { + if ($folder =~ /^supplemental/) { + $prefix =~ s/^default/supplemental/; + } else { + $prefix =~ s/^supplemental/default/; + } + } + if (($srcdom eq $coursedom) && ($srcnum eq $coursenum)) { + $newurl = $path.$prefix.$ancestor.$suffix.'.'.$ext; + } else { + $newurl = "/uploaded/$coursedom/$coursenum/$prefix".$now.'.'.$ext; + } + my $counter = 0; + my $is_unique = &uniqueness_check($newurl); + if ($folder =~ /^default/) { + if ($allmaps->{$newurl}) { + $is_unique = 0; + } + } + while ((!$is_unique || $allmaps->{$newurl} || $newurls->{$newurl}) && ($counter < 100)) { + $counter ++; + $suffix ++; + if (($srcdom eq $coursedom) && ($srcnum eq $coursenum)) { + $newurl = $path.$prefix.$ancestor.$suffix.'.'.$ext; + } else { + $newurl = "/uploaded/$coursedom/$coursenum/$prefix".$ancestor.$suffix.'.'.$ext; + } + $is_unique = &uniqueness_check($newurl); + } + if ($is_unique) { + $newurls->{$newurl} = 1; + } else { + if ($url=~/\.page$/) { + return (undef,&mt('Paste failed: an error occurred creating a unique URL for the composite page')); + } else { + return (undef,&mt('Paste failed: an error occurred creating a unique URL for the folder')); + } + } + } + return ($newurl); +} + +sub dbcopy { + my ($url,$coursedom,$coursenum) = @_; + if ($url=~ m{/smppg$}) { + my $db_name = &Apache::lonsimplepage::get_db_name($url); + if ($db_name =~ /^smppage_/) { + #simple pages, need to copy the db contents to a new one. + my %contents=&Apache::lonnet::dump($db_name,$coursedom,$coursenum); + my $now = time(); + $db_name =~ s{_\d*$ }{_$now}x; + my $result=&Apache::lonnet::put($db_name,\%contents, + $coursedom,$coursenum); + $url =~ s{/(\d*)/smppg$ }{/$now/smppg}x; + } + } + return $url; } sub uniqueness_check { @@ -741,6 +1218,458 @@ sub uniqueness_check { return $unique; } +sub contained_map_check { + my ($url,$folder,$removefrommap,$removeparam,$addedmaps,$hierarchy,$titles, + $allmaps) = @_; + my $content = &Apache::lonnet::getfile($url); + unless ($content eq '-1') { + my $parser = HTML::TokeParser->new(\$content); + $parser->attr_encoded(1); + while (my $token = $parser->get_token) { + next if ($token->[0] ne 'S'); + if ($token->[1] eq 'resource') { + next if ($token->[2]->{'type'} eq 'zombie'); + my $ressrc = $token->[2]->{'src'}; + if ($folder =~ /^supplemental/) { + unless (&supp_pasteable($ressrc)) { + $removefrommap->{$url}{$token->[2]->{'id'}} = $ressrc; + next; + } + } + if ($ressrc =~ m{^/(res|uploaded)/.+\.(sequence|page)$}) { + if ($1 eq 'uploaded') { + $hierarchy->{$url}{$token->[2]->{'id'}} = $ressrc; + $titles->{$url}{$token->[2]->{'id'}} = $token->[2]->{'title'}; + } else { + if ($allmaps->{$ressrc}) { + $removefrommap->{$url}{$token->[2]->{'id'}} = $ressrc; + } elsif (ref($addedmaps->{$ressrc}) eq 'ARRAY') { + $removefrommap->{$url}{$token->[2]->{'id'}} = $ressrc; + } else { + $addedmaps->{$ressrc} = [$url]; + } + } + &contained_map_check($ressrc,$folder,$removefrommap,$removeparam, + $addedmaps,$hierarchy,$titles,$allmaps); + } + } elsif ($token->[1] eq 'param') { + if ($folder =~ /^supplemental/) { + if (ref($removeparam->{$url}{$token->[2]->{'to'}}) eq 'ARRAY') { + push(@{$removeparam->{$url}{$token->[2]->{'to'}}},$token->[2]->{'name'}); + } else { + $removeparam->{$url}{$token->[2]->{'to'}} = [$token->[2]->{'name'}]; + } + } + } + } + } + return; +} + +sub reinit_role { + my ($cdom,$cnum,$chome) = @_; + my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum"); + unless ($ferr) { + &Apache::loncommon::update_content_constraints($cdom,$cnum,$chome,$cdom.'_'.$cnum); + } + return; +} + +sub url_paste_fixups { + my ($oldurl,$folder,$prefixchg,$cdom,$cnum,$allmaps,$rewrites,$retitles,$copies, + $dbcopies,$zombies,$params,$mapmoves,$mapchanges,$tomove,$newsubdir,$newurls) = @_; + my $checktitle; + if (($prefixchg) && + ($oldurl =~ m{^/uploaded/$match_domain/$match_courseid/supplemental})) { + $checktitle = 1; + } + my $skip; + if ($oldurl =~ m{^\Q/uploaded/$cdom/$cnum/\E(default|supplemental)(_?\d*)\.(?:page|sequence)$}) { + my $mapid = $1.$2; + if ($tomove->{$mapid}) { + $skip = 1; + } + } + my $file = &Apache::lonnet::getfile($oldurl); + return if ($file eq '-1'); + my $parser = HTML::TokeParser->new(\$file); + $parser->attr_encoded(1); + my $changed = 0; + while (my $token = $parser->get_token) { + next if ($token->[0] ne 'S'); + if ($token->[1] eq 'resource') { + my $ressrc = $token->[2]->{'src'}; + next if ($ressrc eq ''); + my $id = $token->[2]->{'id'}; + my $title = $token->[2]->{'title'}; + if ($checktitle) { + if ($title =~ m{\d+\Q___&&&___\E$match_username\Q___&&&___\E$match_domain\Q___&&&___\E(.+)$}) { + $retitles->{$oldurl}{$ressrc} = $id; + } + } + next if ($token->[2]->{'type'} eq 'external'); + if ($token->[2]->{'type'} eq 'zombie') { + next if ($skip); + $zombies->{$oldurl}{$ressrc} = $id; + $changed = 1; + } elsif ($ressrc =~ m{^/uploaded/($match_domain)/($match_courseid)/(.+)$}) { + my $srcdom = $1; + my $srcnum = $2; + my $rem = $3; + my $newurl; + my $mapname; + if ($rem =~ /^(default|supplemental)(_?\d*).(sequence|page)$/) { + my $prefix = $1; + $mapname = $prefix.$2; + if ($tomove->{$mapname}) { + &url_paste_fixups($ressrc,$folder,$prefixchg,$cdom,$cnum,$allmaps, + $rewrites,$retitles,$copies,$dbcopies,$zombies, + $params,$mapmoves,$mapchanges,$tomove,$newsubdir, + $newurls); + next; + } else { + ($newurl,my $error) = + &get_newmap_url($ressrc,$folder,$prefixchg,$cdom,$cnum, + $srcdom,$srcnum,\$title,$allmaps,$newurls); + if ($newurl =~ /(?:default|supplemental)_(\d+)\.(?:sequence|page)$/) { + $newsubdir->{$ressrc} = $1; + } + if ($error) { + next; + } + } + } + if (($srcdom ne $cdom) || ($srcnum ne $cnum) || ($prefixchg) || + ($mapchanges->{$oldurl}) || (($newurl ne '') && ($newurl ne $oldurl))) { + + if ($rem =~ /^(default|supplemental)(_?\d*).(sequence|page)$/) { + $rewrites->{$oldurl}{$ressrc} = $id; + $mapchanges->{$ressrc} = 1; + unless (&url_paste_fixups($ressrc,$folder,$prefixchg,$cdom,$cnum,$allmaps, + $rewrites,$retitles,$copies,$dbcopies,$zombies, + $params,$mapmoves,$mapchanges,$tomove,$newsubdir, + $newurls)) { + $mapmoves->{$ressrc} = 1; + } + $changed = 1; + } else { + $rewrites->{$oldurl}{$ressrc} = $id; + $copies->{$oldurl}{$ressrc} = $id; + $changed = 1; + } + } + } elsif ($ressrc =~ m{^/adm/($match_domain)/($match_courseid)/(.+)$}) { + next if ($skip); + my $srcdom = $1; + my $srcnum = $2; + if (($srcdom ne $cdom) || ($srcnum ne $cnum)) { + $rewrites->{$oldurl}{$ressrc} = $id; + $dbcopies->{$oldurl}{$ressrc} = $id; + $changed = 1; + } + } elsif ($ressrc =~ m{^/public/($match_domain)/($match_courseid)/(.+)$}) { + next if ($skip); + my $srcdom = $1; + my $srcnum = $2; + if (($srcdom ne $cdom) || ($srcnum ne $cnum)) { + $rewrites->{$oldurl}{$ressrc} = $id; + $dbcopies->{$oldurl}{$ressrc} = $id; + $changed = 1; + } + } + } elsif ($token->[1] eq 'param') { + next if ($skip); + my $to = $token->[2]->{'to'}; + if ($to ne '') { + if (ref($params->{$oldurl}{$to}) eq 'ARRAY') { + push(@{$params->{$oldurl}{$to}},$token->[2]->{'name'}); + } else { + @{$params->{$oldurl}{$to}} = ($token->[2]->{'name'}); + } + } + } + } + return $changed; +} + +sub apply_fixups { + my ($folder,$is_map,$prefixchg,$cdom,$cnum,$oldurl,$url,$removefrommap, + $removeparam,$rewrites,$retitles,$copies,$dbcopies,$zombies,$params, + $docmoves,$mapmoves,$newsubdir,$errors,$before,$after) = @_; + foreach my $key (keys(%{$copies}),keys(%{$docmoves})) { + my @allcopies; + if (ref($copies->{$key}) eq 'HASH') { + my %added; + foreach my $innerkey (keys(%{$copies->{$key}})) { + if (($innerkey ne '') && (!$added{$innerkey})) { + push(@allcopies,$innerkey); + $added{$innerkey} = 1; + } + } + undef(%added); + } + if ($key eq $oldurl) { + if ((exists($docmoves->{$key}))) { + unless (grep(/^\Q$oldurl\E/,@allcopies)) { + push(@allcopies,$oldurl); + } + } + } + if (@allcopies > 0) { + foreach my $item (@allcopies) { + my ($relpath,$oldsubdir,$fname) = + ($item =~ m{^(/uploaded/$match_domain/$match_courseid/(?:docs|supplemental)/(default|\d+)/.*/)([^/]+)$}); + if ($fname ne '') { + my $content = &Apache::lonnet::getfile($item); + unless ($content eq '-1') { + my $storefn; + if (($key eq $oldurl) && (ref($docmoves) eq 'HASH') && (exists($docmoves->{$key}))) { + $storefn = $docmoves->{$key}; + } else { + $storefn = $relpath; + $storefn =~s{^/uploaded/$match_domain/$match_courseid/}{}; + if ($prefixchg) { + $storefn =~ s/^\Q$before->{'doc'}\E/$after->{'doc'}/; + } + if ($newsubdir->{$key}) { + $storefn =~ s#^(docs|supplemental)/\Q$oldsubdir\E/#$1/$newsubdir->{$key}#; + } + } + ©_dependencies($item,$storefn,$relpath,$errors,\$content); + my $copyurl = + &Apache::lonclonecourse::writefile($env{'request.course.id'}, + $storefn.$fname,$content); + if ($copyurl eq '/adm/notfound.html') { + if ((ref($docmoves) eq 'HASH') && (exists($docmoves->{$oldurl}))) { + return &mt('Paste failed: an error occurred copying the file.'); + } elsif (ref($errors) eq 'HASH') { + $errors->{$item} = 1; + } + } + } + } + } + } + } + foreach my $key (keys(%{$mapmoves})) { + my $storefn=$key; + $storefn=~s{^/uploaded/$match_domain/$match_courseid/}{}; + if ($prefixchg) { + $storefn =~ s/^\Q$before->{'map'}\E/$after->{'map'}/; + } + if ($newsubdir->{$key}) { + $storefn =~ s/^((?:default|supplemental)_)(\d+)/$1$newsubdir->{$key}/; + } + my $mapcontent = &Apache::lonnet::getfile($key); + if ($mapcontent eq '-1') { + if (ref($errors) eq 'HASH') { + $errors->{$key} = 1; + } + } else { + my $newmap = + &Apache::lonclonecourse::writefile($env{'request.course.id'},$storefn, + $mapcontent); + if ($newmap eq '/adm/notfound.html') { + if (ref($errors) eq 'HASH') { + $errors->{$key} = 1; + } + } + } + } + my %updates; + if ($is_map) { + foreach my $key (keys(%{$rewrites})) { + $updates{$key} = 1; + } + foreach my $key (keys(%{$zombies})) { + $updates{$key} = 1; + } + foreach my $key (keys(%{$removefrommap})) { + $updates{$key} = 1; + } + foreach my $key (keys(%{$removeparam})) { + $updates{$key} = 1; + } + foreach my $key (keys(%{$dbcopies})) { + $updates{$key} = 1; + } + foreach my $key (keys(%{$retitles})) { + $updates{$key} = 1; + } + foreach my $key (keys(%updates)) { + my (%torewrite,%toretitle,%toremove,%remparam,%currparam,%zombie,%newdb); + if (ref($rewrites->{$key}) eq 'HASH') { + %torewrite = %{$rewrites->{$key}}; + } + if (ref($retitles->{$key}) eq 'HASH') { + %toretitle = %{$retitles->{$key}}; + } + if (ref($removefrommap->{$key}) eq 'HASH') { + %toremove = %{$removefrommap->{$key}}; + } + if (ref($removeparam->{$key}) eq 'HASH') { + %remparam = %{$removeparam->{$key}}; + } + if (ref($zombies->{$key}) eq 'HASH') { + %zombie = %{$zombies->{$key}}; + } + if (ref($dbcopies->{$key}) eq 'HASH') { + foreach my $item (keys(%{$dbcopies->{$key}})) { + $newdb{$item} = &dbcopy($item); + } + } + if (ref($params->{$key}) eq 'HASH') { + %currparam = %{$params->{$key}}; + } + my ($errtext,$fatal) = &LONCAPA::map::mapread($key); + if ($fatal) { + return $errtext; + } + for (my $i=0; $i<@LONCAPA::map::zombies; $i++) { + if (defined($LONCAPA::map::zombies[$i])) { + my ($title,$src,$ext,$type)=split(/\:/,$LONCAPA::map::zombies[$i]); + if ($zombie{$src} eq $i) { + undef($LONCAPA::map::zombies[$i]); + } + } + } + for (my $i=0; $i<@LONCAPA::map::resources; $i++) { + if (defined($LONCAPA::map::resources[$i])) { + my $changed; + my ($title,$src,$ext,$type)=split(/\:/,$LONCAPA::map::resources[$i]); + if ($toremove{$src} eq $i) { + splice(@LONCAPA::map::order,$i,1); + if (ref($currparam{$i}) eq 'ARRAY') { + foreach my $name (@{$currparam{$i}}) { + &LONCAPA::map::delparameter($i,'parameter_'.$name); + } + } + next; + } + my $origsrc = $src; + if ((exists($toretitle{$src})) && ($toretitle{$src} eq $i)) { + if ($title =~ m{^\d+\Q___&&&___\E$match_username\Q___&&&___\E$match_domain\Q___&&&___\E(.+)$}) { + $changed = 1; + } + } + if ((exists($torewrite{$src})) && ($torewrite{$src} eq $i)) { + $src =~ s{^/(uploaded|adm|public)/$match_domain/$match_courseid/}{/$1/$cdom/$cnum/}; + if ($origsrc =~ m{^/uploaded/}) { + if ($prefixchg) { + if ($src =~ /\.(page|sequence)$/) { + $src =~ s#^(/uploaded/$match_domain/$match_courseid/)\Q$before->{'map'}\E#$1$after->{'map'}#; + } else { + $src =~ s#^(/uploaded/$match_domain/$match_courseid/)\Q$before->{'doc'}\E#$1$after->{'doc'}#; + } + } + if ($newsubdir->{$origsrc}) { + if ($src =~ /\.(page|sequence)$/) { + $src =~ s#^(/uploaded/$match_domain/$match_courseid/(?:default|supplemental)_)(\d+)#$1$newsubdir->{$origsrc}#; + } else { + $src =~ s#^(/uploaded/$match_domain/$match_courseid/\w+/)(\d+)#$1$newsubdir->{$origsrc}#; + } + } + } + $changed = 1; + } elsif ($newdb{$src} ne '') { + $src = $newdb{$src}; + $changed = 1; + } + if ($changed) { + $LONCAPA::map::resources[$i] = join(':',($title,$src,$ext,$type)); + } + } + } + foreach my $idx (keys(%remparam)) { + if (ref($remparam{$idx}) eq 'ARRAY') { + foreach my $name (@{$remparam{$idx}}) { + &LONCAPA::map::delparameter($idx,'parameter_'.$name); + } + } + } + my $storefn; + if ($key eq $oldurl) { + $storefn = $url; + $storefn=~s{^/uploaded/$match_domain/$match_courseid/}{}; + } else { + $storefn = $key; + $storefn=~s{^/uploaded/$match_domain/$match_courseid/}{}; + if ($prefixchg) { + $storefn =~ s/^\Q$before->{'map'}\E/$after->{'map'}/; + } + if ($newsubdir->{$key}) { + $storefn =~ s/^((?:default|supplemental)_)(\d+)/$1$newsubdir->{$key}/; + } + } + my $report; + if ($folder !~ /^supplemental/) { + $report = 1; + } + my ($outtext,$errtext) = + &LONCAPA::map::storemap("/uploaded/$cdom/$cnum/$storefn",1,$report); + if ($errtext) { + return &mt('Paste failed: an error occurred saving the folder or page.'); + } + } + } + return 'ok'; +} + +sub copy_dependencies { + my ($item,$storefn,$relpath,$errors,$contentref) = @_; + my $content; + if (ref($contentref)) { + $content = $$contentref; + } else { + $content = &Apache::lonnet::getfile($item); + } + unless ($content eq '-1') { + my $mm = new File::MMagic; + my $mimetype = $mm->checktype_contents($content); + if ($mimetype eq 'text/html') { + my (%allfiles,%codebase,$state); + my $res = &Apache::lonnet::extract_embedded_items(undef,\%allfiles,\%codebase,\$content); + if ($res eq 'ok') { + my ($numexisting,$numpathchanges,$existing); + (undef,$numexisting,$numpathchanges,$existing) = + &Apache::loncommon::ask_for_embedded_content( + '/adm/coursedocs',$state,\%allfiles,\%codebase, + {'error_on_invalid_names' => 1, + 'ignore_remote_references' => 1, + 'docs_url' => $item, + 'context' => 'paste'}); + if ($numexisting > 0) { + if (ref($existing) eq 'HASH') { + foreach my $dep (keys(%{$existing})) { + my $depfile = $dep; + unless ($depfile =~ m{^\Q$relpath\E}) { + $depfile = $relpath.$dep; + } + my $depcontent = &Apache::lonnet::getfile($depfile); + unless ($depcontent eq '-1') { + my $storedep = $dep; + $storedep =~ s{^\Q$relpath\E}{}; + my $dep_url = + &Apache::lonclonecourse::writefile( + $env{'request.course.id'}, + $storefn.$storedep,$depcontent); + if ($dep_url eq '/adm/notfound.html') { + if (ref($errors) eq 'HASH') { + $errors->{$depfile} = 1; + } + } else { + ©_dependencies($depfile,$storefn,$relpath,$errors,\$depcontent); + } + } + } + } + } + } + } + } + return; +} + my %parameter_type = ( 'randompick' => 'int_pos', 'hiddenresource' => 'string_yesno', 'encrypturl' => 'string_yesno', @@ -814,10 +1743,20 @@ sub handle_edit_cmd { sub editor { my ($r,$coursenum,$coursedom,$folder,$allowed,$upload_output,$crstype, - $supplementalflag,$orderhash,$iconpath)=@_; + $supplementalflag,$orderhash,$iconpath,$uploadtag)=@_; my $container= ($env{'form.pagepath'}) ? 'page' : 'sequence'; + my ($breadcrumbtrail,$randompick,$ishidden,$isencrypted,$plain,$is_random_order) = + &breadcrumbs($allowed,$crstype); + $r->print($breadcrumbtrail); + + my $jumpto = "uploaded/$coursedom/$coursenum/$folder.$container"; + + unless ($allowed) { + $randompick = -1; + } + my ($errtext,$fatal) = &mapread($coursenum,$coursedom, $folder.'.'.$container); return $errtext if ($fatal); @@ -829,16 +1768,6 @@ sub editor { $LONCAPA::map::resources[$idx]=''; } - my ($breadcrumbtrail,$randompick,$ishidden,$isencrypted,$plain,$is_random_order) = - &breadcrumbs($allowed,$crstype); - $r->print($breadcrumbtrail); - - my $jumpto = "uploaded/$coursedom/$coursenum/$folder.$container"; - - unless ($allowed) { - $randompick = -1; - } - # ------------------------------------------------------------ Process commands # ---------------- if they are for this folder and user allowed to make changes @@ -861,20 +1790,35 @@ sub editor { } if ($env{'form.pastemarked'}) { - my $paste_res = - &do_paste_from_buffer($coursenum,$coursedom,$folder); - if ($paste_res eq 'ok') { - ($errtext,$fatal) = &storemap($coursenum,$coursedom,$folder.'.'.$container); - return $errtext if ($fatal); - } elsif ($paste_res ne '') { + my %paste_errors; + my ($paste_res,$save_error) = + &do_paste_from_buffer($coursenum,$coursedom,$folder,$container, + \%paste_errors); + if ($save_error ne '') { + return $save_error; + } + if ($paste_res ne 'ok') { $r->print('

'.$paste_res.'

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

'."\n". + &mt('The following files are either dependencies of a web page or references within a folder and/or composite page which could not be copied during the paste operation:')."\n". + '

'."\n"); + } } $r->print($upload_output); if (&handle_edit_cmd()) { - ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container); + my $contentchg; + if ($env{'form.cmd'} =~ /^(del|cut)_/) { + $contentchg = 1; + } + ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container,$contentchg); return $errtext if ($fatal); } # Group import/search @@ -901,7 +1845,7 @@ sub editor { $LONCAPA::map::order[$#LONCAPA::map::order+1]=$idx; } ($errtext,$fatal)=&storemap($coursenum,$coursedom, - $folder.'.'.$container); + $folder.'.'.$container,1); return $errtext if ($fatal); } else { $r->print('

'.&mt('No map selected.').'

'); @@ -955,7 +1899,8 @@ sub editor { unless ($name) { $name=(split(/\//,$url))[-1]; } unless ($name) { $idx++; next; } $output .= &entryline($idx,$name,$url,$folder,$allowed,$res, - $coursenum,$crstype); + $coursenum,$coursedom,$crstype, + $uploadtag,$supplementalflag); $idx++; $shown++; } @@ -990,8 +1935,10 @@ sub editor { $tid = 2; } if ($allowed) { - $r->print(&generate_edit_table($tid,$orderhash,$to_show,$iconpath,$jumpto)); - &print_paste_buffer($r,$container); + my $readfile="/uploaded/$coursedom/$coursenum/$folder.$container"; + $r->print(&generate_edit_table($tid,$orderhash,$to_show,$iconpath,$jumpto, + $readfile)); + &print_paste_buffer($r,$container,$folder,$coursedom,$coursenum); } else { if (&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) { #Function Box for Supplemental Content for users with mdc priv. @@ -1079,7 +2026,7 @@ sub process_file_upload { $comment.':'.$url.':'.$ext.':normal:res'; $LONCAPA::map::order[$#LONCAPA::map::order+1]= $newidx; ($errtext,$fatal)=&storemap($coursenum,$coursedom, - $folder.'.'.$container); + $folder.'.'.$container,1); if ($fatal) { $$upload_output = '
'.$errtext.'
'; return; @@ -1121,52 +2068,60 @@ sub process_file_upload { newidx => $newidx, position => $position, phase => $nextphase, - ); + comment => $comment, + ); + my ($destination,$dir_root) = &embedded_destination($coursenum,$coursedom); + my @current = &get_dir_list($url,$coursenum,$coursedom,$newidx); $$upload_output = $showupload. &Apache::loncommon::decompress_form($mimetype, $archiveurl,'/adm/coursedocs',$noextract, - \%archiveitems); + \%archiveitems,\@current); } } } return $nextphase; } -sub is_supplemental_title { - my ($title) = @_; - return scalar($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/); +sub get_dir_list { + my ($url,$coursenum,$coursedom,$newidx) = @_; + my ($destination,$dir_root) = &embedded_destination(); + my ($dirlistref,$listerror) = + &Apache::lonnet::dirlist("$dir_root/$destination/$newidx",$coursedom,$coursenum,1); + my @dir_lines; + my $dirptr=16384; + if (ref($dirlistref) eq 'ARRAY') { + foreach my $dir_line (sort + { + my ($afile)=split('&',$a,2); + my ($bfile)=split('&',$b,2); + return (lc($afile) cmp lc($bfile)); + } (@{$dirlistref})) { + my ($filename,$dom,undef,$testdir,undef,undef,undef,undef,$size,undef,$mtime,undef,undef,undef,$obs,undef)=split(/\&/,$dir_line,16); + $filename =~ s/\s+$//; + next if ($filename =~ /^\.\.?$/); + my $isdir = 0; + if ($dirptr&$testdir) { + $isdir = 1; + } + push(@dir_lines, [$filename,$dom,$isdir,$size,$mtime,$obs]); + } + } + return @dir_lines; } -sub parse_supplemental_title { +sub is_supplemental_title { my ($title) = @_; - - my ($foldertitle,$renametitle); - if ($title =~ /&&&/) { - $title = &HTML::Entites::decode($title); - } - if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) { - $renametitle=$4; - my ($time,$uname,$udom) = ($1,$2,$3); - $foldertitle=&Apache::lontexconvert::msgtexconverted($4); - my $name = &Apache::loncommon::plainname($uname,$udom); - $name = &HTML::Entities::encode($name,'"<>&\''); - $renametitle = &HTML::Entities::encode($renametitle,'"<>&\''); - $title=''.&Apache::lonlocal::locallocaltime($time).' '. - $name.':
'.$foldertitle; - } - if (wantarray) { - return ($title,$foldertitle,$renametitle); - } - return $title; + return scalar($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/); } # --------------------------------------------------------------- An entry line sub entryline { - my ($index,$title,$url,$folder,$allowed,$residx,$coursenum,$crstype)=@_; + my ($index,$title,$url,$folder,$allowed,$residx,$coursenum,$coursedom, + $crstype,$uploadtag,$supplementalflag)=@_; my ($foldertitle,$pagetitle,$renametitle); if (&is_supplemental_title($title)) { - ($title,$foldertitle,$renametitle) = &parse_supplemental_title($title); + ($title,$foldertitle,$renametitle) = &Apache::loncommon::parse_supplemental_title($title); $pagetitle = $foldertitle; } else { $title=&HTML::Entities::encode($title,'"<>&\''); @@ -1177,12 +2132,11 @@ sub entryline { my $orderidx=$LONCAPA::map::order[$index]; - $renametitle=~s/\\/\\\\/g; $renametitle=~s/\"\;/\\\"/g; $renametitle=~s/ /%20/g; my $line=&Apache::loncommon::start_data_table_row(); - my ($form_start,$form_end); + my ($form_start,$form_end,$form_common); # Edit commands my ($container, $type, $esc_path, $path, $symb); if ($env{'form.folderpath'}) { @@ -1196,9 +2150,18 @@ sub entryline { $type = $container = 'page'; $esc_path=&escape($env{'form.pagepath'}); $path = &HTML::Entities::encode($env{'form.pagepath'},'<>&"'); - $symb=&escape($env{'form.pagesymb'}); } - my $cpinfo=''; + if (!$supplementalflag && $residx) { + my $currurl = $url; + $currurl =~ s{^http(|s)(:|:)//}{/adm/wrapper/ext/}; + my $path = 'uploaded/'. + $env{'course.'.$env{'request.course.id'}.'.domain'}.'/'. + $env{'course.'.$env{'request.course.id'}.'.num'}.'/'; + $symb = &Apache::lonnet::encode_symb($path.$folder.".$container", + $residx, + &Apache::lonnet::declutter($currurl)); + } + my %lt; if ($allowed) { my $incindex=$index+1; my $selectbox=''; @@ -1221,35 +2184,83 @@ sub entryline { } $selectbox.=''; } - my %lt=&Apache::lonlocal::texthash( + %lt=&Apache::lonlocal::texthash( 'up' => 'Move Up', 'dw' => 'Move Down', 'rm' => 'Remove', 'ct' => 'Cut', 'rn' => 'Rename', - 'cp' => 'Copy'); + 'cp' => 'Copy', + 'ex' => 'External Resource', + 'ed' => 'Edit', + 'pr' => 'Preview', + 'sv' => 'Save', + 'ul' => 'URL', + 'ti' => 'Title', + ); my $nocopy=0; my $nocut=0; - if ($url=~/\.(page|sequence)$/) { - if ($url =~ m{/res/}) { - # no copy for published maps - $nocopy = 1; - } else { - foreach my $item (&Apache::lonsequence::attemptread(&Apache::lonnet::filelocation('',$url),1)) { - my ($title,$url,$ext,$type)=split(/\:/,$item); - if (($url=~/\.(page|sequence)/) && ($type ne 'zombie')) { - $nocopy=1; - last; - } - } - } - } + 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 $copylink=' '; my $cutlink=' '; + my $removelink=' '; my $skip_confirm = 0; if ( $folder =~ /^supplemental/ @@ -1265,16 +2276,23 @@ sub entryline { if (!$nocopy) { $copylink=(<$lt{'cp'} +$lt{'cp'} ENDCOPY } if (!$nocut) { $cutlink=(<$lt{'ct'} +$lt{'ct'} ENDCUT } - $form_start = (< + if (!$noremove) { + $removelink=(<$lt{'rm'} +ENDREM + } + $form_start = ' +
+'; + $form_common=(< @@ -1284,23 +2302,24 @@ END $line.=(<
- - $lt{ + + $lt{'up'}
- - $lt{ + + $lt{'dw'}
$form_start + $form_common $selectbox $form_end - $lt{'rm'} +$removelink $cutlink $lt{'rn'} $copylink @@ -1340,46 +2359,40 @@ END } } + my $editlink; my $orig_url = $url; $orig_url=~s{http(:|:)//https(:|:)//}{https$2//}; my $external = ($url=~s{^http(|s)(:|:)//}{/adm/wrapper/ext/}); - if ((!$isfolder) && ($residx) && ($folder!~/supplemental/) && (!$ispage)) { - my $symb=&Apache::lonnet::symbclean( - &Apache::lonnet::declutter('uploaded/'. - $env{'course.'.$env{'request.course.id'}.'.domain'}.'/'. - $env{'course.'.$env{'request.course.id'}.'.num'}.'/'.$folder. - '.sequence'). - '___'.$residx.'___'. - &Apache::lonnet::declutter($url)); - (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); - $url=&Apache::lonnet::clutter($url); - if ($url=~/^\/*uploaded\//) { - $url=~/\.(\w+)$/; - my $embstyle=&Apache::loncommon::fileembstyle($1); - if (($embstyle eq 'img') || ($embstyle eq 'emb')) { - $url='/adm/wrapper'.$url; - } elsif ($embstyle eq 'ssi') { - #do nothing with these - } elsif ($url!~/\.(sequence|page)$/) { - $url='/adm/coursedocs/showdoc'.$url; - } - } elsif ($url=~m|^/ext/|) { - $url='/adm/wrapper'.$url; - $external = 1; - } - if (&Apache::lonnet::symbverify($symb,$url)) { - $url.=(($url=~/\?/)?'&':'?').'symb='.&escape($symb); - } else { - $url=''; + if (!$supplementalflag && $residx && $symb) { + if ($container eq 'page') { + $url=&Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]); + $url.=(($url=~/\?/)?'&':'?').'symb='.&escape($symb); + } + if ((!$isfolder) && (!$ispage)) { + (undef,undef,$url)=&Apache::lonnet::decode_symb($symb); + $url=&Apache::lonnet::clutter($url); + if ($url=~/^\/*uploaded\//) { + $url=~/\.(\w+)$/; + my $embstyle=&Apache::loncommon::fileembstyle($1); + if (($embstyle eq 'img') || ($embstyle eq 'emb')) { + $url='/adm/wrapper'.$url; + } elsif ($embstyle eq 'ssi') { + #do nothing with these + } elsif ($url!~/\.(sequence|page)$/) { + $url='/adm/coursedocs/showdoc'.$url; + } + } elsif ($url=~m|^/ext/|) { + $url='/adm/wrapper'.$url; + $external = 1; + } + if (&Apache::lonnet::symbverify($symb,$url)) { + $url.=(($url=~/\?/)?'&':'?').'symb='.&escape($symb); + } else { + $url=''; + } } - if ($container eq 'page') { - my $symb=$env{'form.pagesymb'}; - - $url=&Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]); - $url.=(($url=~/\?/)?'&':'?').'symb='.&escape($symb); - } } - my ($parameterset,$rand_order_text) = (' ', ' '); + my ($rand_pick_text,$rand_order_text); if ($isfolder || $extension eq 'sequence') { my $foldername=&escape($foldertitle); my $folderpath=$env{'form.folderpath'}; @@ -1394,17 +2407,28 @@ END 'parameter_encrypturl'))[0]=~/^yes$/i) .':'.((&LONCAPA::map::getparameter($orderidx, 'parameter_randomorder'))[0]=~/^yes$/i); - $url.='folderpath='.&escape($folderpath).$cpinfo; - $parameterset=''; + $url.='folderpath='.&escape($folderpath); + my $rpicknum = (&LONCAPA::map::getparameter($orderidx, + 'parameter_randompick'))[0]; + my $rpckchk; + if ($rpicknum) { + $rpckchk = ' checked="checked"'; + } + my $formname = 'edit_rpick_'.$orderidx; + $rand_pick_text = +''."\n". +$form_common."\n". +''; + if ($rpicknum ne '') { + $rand_pick_text .= ': '.$rpicknum.''; + } + $rand_pick_text .= ''; my $ro_set= ((&LONCAPA::map::getparameter($orderidx,'parameter_randomorder'))[0]=~/^yes$/i?' checked="checked"':''); - $rand_order_text =' -'; + $rand_order_text = +$form_start. +$form_common.' +'; } if ($ispage) { my $pagename=&escape($pagetitle); @@ -1412,23 +2436,51 @@ END my $folderpath=$env{'form.folderpath'}; if ($folderpath) { $pagepath = $folderpath.'&' }; $pagepath.=$pagearg.'&'.$pagename; - my $symb=$env{'form.pagesymb'}; - if (!$symb) { - my $path='uploaded/'. - $env{'course.'.$env{'request.course.id'}.'.domain'}.'/'. - $env{'course.'.$env{'request.course.id'}.'.num'}.'/'; - $symb=&Apache::lonnet::encode_symb($path.$folder.'.sequence', - $residx, - $path.$pagearg.'.page'); - } $url.='pagepath='.&escape($pagepath). - '&pagesymb='.&escape($symb).$cpinfo; + '&pagesymb='.&escape($symb); } - if (($external) && ($allowed)) { - my $form = ($folder =~ /^default/)? 'newext' : 'supnewext'; - $external = ' '.&mt('Edit').''; - } else { - undef($external); + if ($allowed) { + my $fileloc = + &Apache::lonnet::declutter(&Apache::lonnet::filelocation('',$orig_url)); + + if ($external) { + $editlink = <<"EXTLNK"; +   + + $lt{'ed'} +
+ +
+EXTLNK + } else { + my ($cfile,$home,$switchserver,$forceedit,$forceview) = + &Apache::lonnet::can_edit_resource($fileloc,$coursenum,$coursedom,$orig_url); + if (($cfile ne '') && ($symb ne '')) { + my $jscall = + &Apache::lonhtmlcommon::jump_to_editres($cfile,$home, + $switchserver, + $forceedit, + undef,$symb); + if ($jscall) { + $editlink = ' '.&mt('Edit').''; + } + } + } } my $reinit; if ($crstype eq 'Community') { @@ -1440,7 +2492,7 @@ END if (($url=~m{/adm/(coursedocs|supplemental)}) || (!$allowed && $url)) { $line.=''; } elsif ($url) { - $line.=&Apache::loncommon::modal_link($url.(($url=~/\?/)?'&':'?').'inhibitmenu=yes', + $line.=&Apache::loncommon::modal_link($url.(($url=~/\?/)?'&':'?').'inhibitmenu=yes', '',600,500); } else { $line.=''; @@ -1449,12 +2501,14 @@ END if (($url=~m{/adm/(coursedocs|supplemental)}) || (!$allowed && $url)) { $line.=''.$title.''; } elsif ($url) { - $line.=&Apache::loncommon::modal_link($url.(($url=~/\?/)?'&':'?').'inhibitmenu=yes', + $line.=&Apache::loncommon::modal_link($url.(($url=~/\?/)?'&':'?').'inhibitmenu=yes', $title,600,500); } else { $line.=$title.' '.$reinit.''; } - $line.=$external.""; + $line.=$editlink.""; + $rand_pick_text = ' ' if ($rand_pick_text eq ''); + $rand_order_text = ' ' if ($rand_order_text eq ''); if (($allowed) && ($folder!~/^supplemental/)) { my %lt=&Apache::lonlocal::texthash( 'hd' => 'Hidden', @@ -1466,15 +2520,17 @@ END $line.=(< $form_start + $form_common $form_end
$form_start + $form_common $form_end - $form_start $parameterset $form_end
- $form_start $rand_order_text $form_end + $rand_pick_text
+ $rand_order_text ENDPARMS } $line.=&Apache::loncommon::end_data_table_row(); @@ -1593,7 +2649,7 @@ sub checkonthis { =item list_symbs() -List Symbs +List Content Identifiers =cut @@ -1601,9 +2657,9 @@ sub list_symbs { my ($r) = @_; my $crstype = &Apache::loncommon::course_type(); - $r->print(&Apache::loncommon::start_page('Symb List')); - $r->print(&Apache::lonhtmlcommon::breadcrumbs('Symb List')); - &startContentScreen($r,'tools'); + $r->print(&Apache::loncommon::start_page('List of Content Identifiers')); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Content Identifiers')); + $r->print(&startContentScreen('tools')); my $navmap = Apache::lonnavmaps::navmap->new(); if (!defined($navmap)) { $r->print('

'.&mt('Retrieval of List Failed').'

'. @@ -1612,11 +2668,25 @@ sub list_symbs { ''); &Apache::lonnet::logthis('Symb list failed - could not create navmap object in '.lc($crstype).':'.$env{'request.course.id'}); } else { - $r->print("
\n");
+        $r->print('

'.&mt("$crstype Content Identifiers").'

'. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''.&mt('Title').''.&mt('Identifier').''. + &Apache::loncommon::end_data_table_header_row()."\n"); + my $count; foreach my $res ($navmap->retrieveResources()) { - $r->print($res->compTitle()."\t".$res->symb()."\n"); + $r->print(&Apache::loncommon::start_data_table_row(). + ''.$res->compTitle().''. + ''.$res->symb().''. + &Apache::loncommon::start_data_table_row()); + $count ++; + } + if (!$count) { + $r->print(&Apache::loncommon::start_data_table_row(). + ''.&mt("$crstype is empty").''. + &Apache::loncommon::end_data_table_row()); } - $r->print("\n
\n"); + $r->print(&Apache::loncommon::end_data_table()); } } @@ -1624,13 +2694,15 @@ 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')); - &startContentScreen($r,'tools'); + $r->print(&Apache::loncommon::start_page('Verify '.$crstype.' Documents')); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Verify '.$crstype.' Documents')); + $r->print(&startContentScreen('tools')); + $r->print('

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

'); $hashtied=0; undef %alreadyseen; %alreadyseen=(); &tiehash(); + foreach my $key (keys(%hash)) { if ($hash{$key}=~/\.(page|sequence)$/) { if (($key=~/^src_/) && ($alreadyseen{&unescape($hash{$key})})) { @@ -1660,7 +2732,7 @@ sub checkversions { 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")); - &startContentScreen($r,'tools'); + $r->print(&startContentScreen('tools')); my $header=''; my $startsel=''; @@ -1713,9 +2785,11 @@ sub checkversions { if (&Apache::lonnet::put('resourceversions',\%newsetversions, $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'}) eq 'ok') { - $r->print('

'.&mt('Your Version Settings have been Saved').'

'); + $r->print(&Apache::loncommon::confirmwrapper( + &Apache::lonhtmlcommon::confirm_success(&mt('Your Version Settings have been Saved')))); } else { - $r->print('

'.&mt('An Error Occured while Attempting to Save your Version Settings').'

'); + $r->print(&Apache::loncommon::confirmwrapper( + &Apache::lonhtmlcommon::confirm_success(&mt('An Error Occured while Attempting to Save your Version Settings'),1))); } &mark_hash_old(); } @@ -1772,6 +2846,7 @@ sub checkversions { 'lw' => 'Version changes since last Week', 'sy' => 'Version changes since Yesterday', 'al' => 'All Resources (possibly large output)', + 'cd' => 'Change display', 'sd' => 'Display', 'fi' => 'File', 'md' => 'Modification Date', @@ -1781,12 +2856,17 @@ sub checkversions { 'sv' => 'Set Versions to be used in '.$crstype.' according to Selections below', 'sm' => 'Keep all Resources up-to-date with most recent Versions (default)', 'sc' => 'Set all Resource Versions to current Version (Fix Versions)', - 'di' => 'Differences'); + 'di' => 'Differences', + 'save' => 'Save changes', + 'vers' => 'Version choice(s) for specific resources', + 'act' => 'Actions'); $r->print(<$header
- -
+
+
+$lt{'cd'} -

$header

- +
+
+
+
+$lt{'act'} +$lt{'sm'}:
+$lt{'sc'}: +
+
+
+
+

$lt{'vers'}

+ ENDHEADERS + #number of columns for version history + my $num_ver_col = 1; + $r->print( + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''. + "". + "". + "". + ''. + ''); foreach my $key (sort(keys(%changes))) { - if ($changes{$key}>$starttime) { + #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.'); + $currentversion=''.&mt('Could not be determined.').''; } my $linkurl=&Apache::lonnet::clutter($key); - $r->print( - ''. - ''. - ''. - ''. - ''. - ''. - ''. + ''. + ''); my $lastold=1; for (my $prevvers=1;$prevvers<$currentversion;$prevvers++) { my $url=$root.'.'.$prevvers.'.'.$extension; @@ -1858,13 +2951,13 @@ ENDHEADERS # each of the four columns my $entries_per_col = 0; my $num_entries = ($currentversion-$lastold); - if ($num_entries % 4 == 0) { - $entries_per_col = $num_entries/4; + if ($num_entries % $num_ver_col == 0) { + $entries_per_col = $num_entries/$num_ver_col; } else { - $entries_per_col = $num_entries/4 + 1; + $entries_per_col = $num_entries/$num_ver_col + 1; } my $entries_count = 0; - $r->print(''); - if ($cols_output != 4) { - $r->print(''); + if ($cols_output != $num_ver_col) { + $r->print(''."\n"); } } - $r->print('
'.&mt('Resources').'$lt{'mr'}$lt{'ve'}$lt{'vu'}'.&mt('History').'


'. - &Apache::lonnet::gettitle($linkurl). - '
   '. - ''.$linkurl. - '
'. - &Apache::lonlocal::locallocaltime( - &Apache::lonnet::metadata($root.'.'.$extension, - 'lastrevisiondate') - ). - 'Most Recent: '. - ''.$currentversion.''. - 'In '.$crstype.': '. - ''); + $r->print( + &Apache::loncommon::end_data_table_header_row(). + &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')) { - $r->print($usedversion); + if($usedversion != $currentversion){ + $r->print(''.$usedversion.''); + }else{ + $r->print($usedversion); + } } else { $r->print($currentversion); } - $r->print(''. - 'Use: '); + $r->print(''); # Set version $r->print(&Apache::loncommon::select_form($setversions{$linkurl}, 'set_version_'.$linkurl, @@ -1844,7 +2938,6 @@ ENDHEADERS '' => '', 'mostrecent' => &mt('most recent'), map {$_,$_} (1..$currentversion)})); - $r->print('
'); + $r->print(''); my $cols_output = 1; for (my $prevvers=$lastold;$prevvers<$currentversion;$prevvers++) { my $url=$root.'.'.$prevvers.'.'.$extension; @@ -1883,21 +2976,21 @@ ENDHEADERS } $r->print('
'); if (++$entries_count % $entries_per_col == 0) { - $r->print('
'); + $r->print(''); $cols_output++; } } } - while($cols_output++ < 4) { - $r->print('') + while($cols_output++ < $num_ver_col) { + $r->print(''); } - $r->print('
'); - $r->print('

'.&mt('Done').'

'); + $r->print(''.&Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table(). + ''); &untiehash(); } @@ -1961,7 +3054,7 @@ $help{'Caching'}.'

'."\n\n"); sub init_breadcrumbs { my ($form,$text)=@_; &Apache::lonhtmlcommon::clear_breadcrumbs(); - &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/coursedocs", + &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/coursedocs?tools=1", text=>&Apache::loncommon::course_type().' Editor', faq=>273, bug=>'Instructor Interface', @@ -1976,9 +3069,9 @@ sub init_breadcrumbs { sub create_list_elements { my @formarr = @_; my $list = ''; - for my $button (@formarr){ - for my $picture(keys %$button) { - $list .= &Apache::lonhtmlcommon::htmltag('li', $picture.' '.$button->{$picture}, {class => 'LC_menubuttons_inline_text'}); + foreach my $button (@formarr){ + foreach my $picture (keys(%{$button})) { + $list .= &Apache::lonhtmlcommon::htmltag('li', $picture.' '.$button->{$picture}, {class => 'LC_menubuttons_inline_text', id => ''}); } } return $list; @@ -1996,23 +3089,23 @@ sub create_form_ul { # sub startContentScreen { - my ($r,$mode)=@_; - $r->print(''."\n"; + $output .= '
'. + '
'. + '
'; + return $output; } # @@ -2020,8 +3113,7 @@ sub startContentScreen { # sub endContentScreen { - my ($r)=@_; - $r->print('
'); + return ''; } sub supplemental_base { @@ -2033,7 +3125,14 @@ sub handler { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; return OK if $r->header_only; + +# get course data my $crstype = &Apache::loncommon::course_type(); + my $coursenum=$env{'course.'.$env{'request.course.id'}.'.num'}; + my $coursedom=$env{'course.'.$env{'request.course.id'}.'.domain'}; + +# graphics settings + $iconpath = &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL').'/'); # # --------------------------------------------- Initialize help topics for this @@ -2041,7 +3140,8 @@ sub handler { 'Adding_External_Resource','Navigate_Content', 'Adding_Folders','Docs_Overview', 'Load_Map', 'Supplemental','Score_Upload_Form','Adding_Pages', - 'Importing_LON-CAPA_Resource','Uploading_From_Harddrive', + 'Importing_LON-CAPA_Resource','Importing_IMS_Course', + 'Uploading_From_Harddrive', 'Check_Resource_Versions','Verify_Content') { $help{$topic}=&Apache::loncommon::help_open_topic('Docs_'.$topic); } @@ -2067,20 +3167,29 @@ sub handler { $allowed = &Apache::lonnet::allowed('mdc',$env{'request.course.id'}); } - if ($allowed && $env{'form.verify'}) { + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['chooseserver', + 'inhibitmenu']); + if ($allowed && $env{'form.chooseserver'}) { + &choose_dump_server($r); + return OK; + } elsif ($allowed && $env{'form.verify'}) { &init_breadcrumbs('verify','Verify Content'); &verifycontent($r); } elsif ($allowed && $env{'form.listsymbs'}) { - &init_breadcrumbs('listsymbs','List Symbs'); + &init_breadcrumbs('listsymbs','List Content IDs'); &list_symbs($r); } elsif ($allowed && $env{'form.docslog'}) { &init_breadcrumbs('docslog','Show Log'); - &docs_change_log($r); + my $folder = $env{'form.folder'}; + if ($folder eq '') { + $folder='default'; + } + &docs_change_log($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath); } elsif ($allowed && $env{'form.versions'}) { &init_breadcrumbs('versions','Check/Set Resource Versions'); &checkversions($r); } elsif ($allowed && $env{'form.dumpcourse'}) { - &init_breadcrumbs('dumpcourse','Dump '.&Apache::loncommon::course_type().' Documents to Construction Space'); + &init_breadcrumbs('dumpcourse','Dump '.&Apache::loncommon::course_type().' Content to Authoring Space'); &dumpcourse($r); } elsif ($allowed && $env{'form.exportcourse'}) { &init_breadcrumbs('exportcourse','IMS Export'); @@ -2088,22 +3197,23 @@ sub handler { } else { # # Done catching special calls -# The whole rest is for course and supplemental documents +# The whole rest is for course and supplemental documents and utilities menu # Get the parameters that may be needed # &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['folderpath','pagepath', 'pagesymb','forcesupplement','forcestandard', - 'symb','command']); + 'tools','symb','command']); # standard=1: this is a "new-style" course with an uploaded map as top level # standard=2: this is a "old-style" course, and there is nothing we can do my $standard=($env{'request.course.uri'}=~/^\/uploaded\//); -# Decide whether this should display supplemental or main content +# Decide whether this should display supplemental or main content or utilities # supplementalflag=1: show supplemental documents # supplementalflag=0: show standard documents +# toolsflag=1: show utilities my $supplementalflag=($env{'form.folderpath'}=~/^supplemental/); @@ -2114,6 +3224,8 @@ sub handler { if ($env{'form.forcestandard'}) { $supplementalflag=0; } unless ($allowed) { $supplementalflag=1; } unless ($standard) { $supplementalflag=1; } + my $toolsflag=0; + if ($env{'form.tools'}) { $toolsflag=1; } my $script=''; my $showdoc=0; @@ -2236,81 +3348,95 @@ sub handler { } # Store this - &Apache::loncommon::store_course_settings($stored_folderpath, - {'pagepath' => 'scalar', - 'folderpath' => 'scalar'}); - - if ($env{'form.folderpath'}) { - my (@folderpath)=split('&',$env{'form.folderpath'}); - $env{'form.foldername'}=&unescape(pop(@folderpath)); - $env{'form.folder'}=pop(@folderpath); - $container='sequence'; - } - if ($env{'form.pagepath'}) { - my (@pagepath)=split('&',$env{'form.pagepath'}); - $env{'form.pagename'}=&unescape(pop(@pagepath)); - $env{'form.folder'}=pop(@pagepath); - $container='page'; - $containertag = ''. - ''; - $uploadtag = - ''. - ''. - ''; - } else { - my $folderpath=$env{'form.folderpath'}; - if (!$folderpath) { - if ($env{'form.folder'} eq '' || - $env{'form.folder'} eq 'supplemental') { - $folderpath='default&'. - &escape(&mt('Main '.$crstype.' Documents')); + unless ($toolsflag) { + &Apache::loncommon::store_course_settings($stored_folderpath, + {'pagepath' => 'scalar', + 'folderpath' => 'scalar'}); + if ($env{'form.folderpath'}) { + my (@folderpath)=split('&',$env{'form.folderpath'}); + $env{'form.foldername'}=&unescape(pop(@folderpath)); + $env{'form.folder'}=pop(@folderpath); + $container='sequence'; + } + if ($env{'form.pagepath'}) { + my (@pagepath)=split('&',$env{'form.pagepath'}); + $env{'form.pagename'}=&unescape(pop(@pagepath)); + $env{'form.folder'}=pop(@pagepath); + $container='page'; + $containertag = ''. + ''; + $uploadtag = + ''. + ''. + ''; + } else { + my $folderpath=$env{'form.folderpath'}; + if (!$folderpath) { + if ($env{'form.folder'} eq '' || + $env{'form.folder'} eq 'supplemental') { + $folderpath='default&'. + &escape(&mt('Main '.$crstype.' Documents')); + } + } + $containertag = ''; + $uploadtag = ''; + } + if ($r->uri=~/^\/adm\/coursedocs\/showdoc\/(.*)$/) { + $showdoc='/'.$1; + } + if ($showdoc) { # got called in sequence from course + $allowed=0; + } else { + if ($allowed) { + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['cmd']); + $script=&Apache::lonratedt::editscript('simple'); } } - $containertag = ''; - $uploadtag = ''; - } - if ($r->uri=~/^\/adm\/coursedocs\/showdoc\/(.*)$/) { - $showdoc='/'.$1; - } - if ($showdoc) { # got called in sequence from course - $allowed=0; - } else { - if ($allowed) { - &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['cmd']); - $script=&Apache::lonratedt::editscript('simple'); - } } -# get course data - my $coursenum=$env{'course.'.$env{'request.course.id'}.'.num'}; - my $coursedom=$env{'course.'.$env{'request.course.id'}.'.domain'}; - # get personal data my $uname=$env{'user.name'}; my $udom=$env{'user.domain'}; my $plainname=&escape(&Apache::loncommon::plainname($uname,$udom)); -# graphics settings - - $iconpath = &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL') . "/"); - if ($allowed) { - my @tabids; - if ($supplementalflag) { - @tabids = ('002','ee2','ff2'); + if ($toolsflag) { + $script .= &inject_data_js(); + my ($home,$other,%outhash)=&authorhosts(); + if (!$home && $other) { + my @hosts; + foreach my $aurole (keys(%outhash)) { + unless(grep(/^\Q$outhash{$aurole}\E/,@hosts)) { + push(@hosts,$outhash{$aurole}); + } + } + $script .= &dump_switchserver_js(@hosts); + } } else { - @tabids = ('aa1','bb1','cc1','ff1'); - unless ($env{'form.pagepath'}) { - unshift(@tabids,'001'); - push(@tabids,('dd1','ee1')); + my @tabids; + if ($supplementalflag) { + @tabids = ('002','ee2','ff2'); + } else { + @tabids = ('aa1','bb1','cc1','ff1'); + unless ($env{'form.pagepath'}) { + unshift(@tabids,'001'); + push(@tabids,('dd1','ee1')); + } } + my $tabidstr = join("','",@tabids); + $script .= &editing_js($udom,$uname,$supplementalflag). + &history_tab_js(). + &inject_data_js(). + &Apache::lonhtmlcommon::resize_scrollbox_js('docs',$tabidstr); + $addentries = { + onload => "javascript:resize_scrollbox('contentscroll','1','1');", + }; + } + if ($env{'docs.markedcopy_url'}) { + $script .= &paste_popup_js(); } - my $tabidstr = join("','",@tabids); - $script .= &editing_js($udom,$uname,$supplementalflag). - &resize_contentdiv_js($tabidstr); - $addentries = { - onload => "javascript:resize_contentdiv('contentscroll','1','1');", - }; + my $confirm_switch = &mt("Editing requires switching to the resource's home server.").'\n'. + &mt('Switch server?'); } # -------------------------------------------------------------------- Body tag $script = ' - if (newscrollboxw != scrollboxw) { - var newscrolltablew = newscrollboxw+offsetw; - scrolltable.style.width = newscrolltablew+"px"; - } - } +ENDSWJS - if (contentlistid.offsetWidth != contentlistwstart) { - listwchange = 1; - } + my $startpage = &Apache::loncommon::start_page('Choose server',$js, + {'only_body' => 1, + 'js_ready' => 1,}); + my $endpage = &Apache::loncommon::end_page({'js_ready' => 1}); - if (activeTab == 'cc1') { - if (document.getElementById('cc_hrule') != null) { - document.getElementById('cc_hrule').style.width=actabw+"px"; - } - } else { - if (activeTab == 'bb1') { - if (document.getElementById('bb_hrule') != null) { - document.getElementById('bb_hrule').style.width=actabw+"px"; - } - } else { - if (activeTab == 'ee2') { - if (document.getElementById('ee_hrule') != null) { - document.getElementById('ee_hrule').style.width=actabw+"px"; - } - } - } + my $hostpicker; + my $count = 0; + foreach my $host (sort(@hosts)) { + my $checked; + if ($count == 0) { + $checked = ' checked="checked"'; } + $hostpicker .= '  '; + $count++; } - if ((chkh == 1) || (listwchange)) { - var primaryheight = document.getElementById("LC_nav_bar").offsetHeight; - var secondaryheight = document.getElementById("LC_secondary_menu").offsetHeight; - var crumbsheight = document.getElementById("LC_breadcrumbs").offsetHeight; - var dccidheight = document.getElementById("dccid").offsetHeight; - - var uploadresultheight = 0; - if (document.getElementById("uploadfileresult") != null) { - uploadresultheight = document.getElementById("uploadfileresult").offsetHeight; - } - var tabbedheight = document.getElementById("tabbededitor").offsetHeight; - var contenteditorheight = document.getElementById("contenteditor").offsetHeight; - var scrollboxheight = scrollbox.offsetHeight; - var scrollboxscrollheight = scrollbox.scrollHeight; - var freevspace = vph-(primaryheight+secondaryheight+crumbsheight+dccidheight+uploadresultheight+tabbedheight+contenteditorheight); - - var minvscrollbox = 200; - var offsetv = 20; - var newscrollboxheight; - if (freevspace < 0) { - newscrollboxheight = scrollboxheight+freevspace-offsetv; - if (newscrollboxheight < minvscrollbox) { - newscrollboxheight = minvscrollbox; - } - scrollbox.style.height = newscrollboxheight + "px"; - } else { - if (scrollboxscrollheight > scrollboxheight) { - if (freevspace > offsetv) { - newscrollboxheight = scrollboxheight+freevspace-offsetv; - if (newscrollboxheight < minvscrollbox) { - newscrollboxheight = minvscrollbox; - } - scrollbox.style.height = newscrollboxheight+"px"; - } - } - } - scrollboxheight = scrollbox.offsetHeight; - var contentlistheight = document.getElementById("contentlist").offsetHeight; + + return <<"ENDSWITCHJS"; - if (scrollboxscrollheight <= scrollboxheight) { - if ((contentlistheight+offsetv)$lt{'duco'}<\\/h3>\\n'+ + '

$lt{'yone'}<\\/p>\\n'+ + '

$lt{'chos'}<\\/legend>\\n'+ + '
\\n'+ + '$hostpicker\\n'+ + '

\\n'+ + '\\n'+ + '<\\/form><\\/fieldset><\\/div>
\\n'); + newWindow.document.writeln('$endpage'); + newWindow.document.close(); + newWindow.focus(); +} + +ENDSWITCHJS +} + +sub makedocslogform { + my ($formelems,$docslog) = @_; + return <<"LOGSFORM"; + + + $formelems +
+LOGSFORM } -ENDRESIZESCRIPT - return; +sub makesimpleeditform { + my ($formelems) = @_; + return <<"SIMPFORM"; +
+ + $formelems +
+SIMPFORM } 1; @@ -3692,21 +4987,12 @@ sets @resources - array with the resourc Return hash with valid author names -=item dumpbutton() - -Generate "dump" button - =item clean() =item dumpcourse() Actually dump course - -=item exportbutton() - - Generate "export" button - =item group_import() Imports the given (name, url) resources into the course @@ -3724,6 +5010,22 @@ Generate "dump" button =item do_paste_from_buffer() +=item get_newmap_url() + +=item dbcopy() + +=item uniqueness_check() + +=item contained_map_check() + +=item reinit_role() + +=item url_paste_fixups() + +=item apply_fixups() + +=item copy_dependencies() + =item update_parameter() =item handle_edit_cmd() @@ -3736,8 +5038,6 @@ Generate "dump" button =item is_supplemental_title() -=item parse_supplemental_title() - =item entryline() =item tiehash() @@ -3766,6 +5066,48 @@ Check Versions Breadcrumbs for special functions +=item create_list_elements() + +=item create_form_ul() + +=item startContentScreen() + +=item endContentScreen() + +=item supplemental_base() + +=item embedded_form_elems() + +=item embedded_destination() + +=item return_to_editor() + +=item decompression_info() + +=item decompression_phase_one() + +=item decompression_phase_two() + +=item remove_archive() + +=item generate_admin_menu() + +=item generate_edit_table() + +=item editing_js() + +=item history_tab_js() + +=item inject_data_js() + +=item dump_switchserver_js() + +=item resize_scrollbox_js() + +=item makedocslogform() + +=item makesimpleeditform() + =back =cut