--- loncom/interface/londocs.pm 2014/06/16 00:09:31 1.582 +++ loncom/interface/londocs.pm 2016/05/08 18:39:27 1.602 @@ -1,7 +1,7 @@ # The LearningOnline Network # Documents # -# $Id: londocs.pm,v 1.582 2014/06/16 00:09:31 raeburn Exp $ +# $Id: londocs.pm,v 1.602 2016/05/08 18:39:27 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -586,7 +586,7 @@ sub recurse_html { } sub group_import { - my ($coursenum, $coursedom, $folder, $container, $caller, @files) = @_; + my ($coursenum, $coursedom, $folder, $container, $caller, $ltitoolsref, @files) = @_; my ($donechk,$allmaps,%hierarchy,%titles,%addedmaps,%removefrommap, %removeparam,$importuploaded,$fixuperrors); $allmaps = {}; @@ -615,6 +615,39 @@ sub group_import { } } if ($url) { + if ($url =~ m{^(/adm/$coursedom/$coursenum/(\d+)/exttool)s?\:?(.*)$}) { + $url = $1; + my $marker = $2; + my $info = $3; + my ($toolid,%toolhash); + my @toolinfo = split(/:/,$info); + if ($residx) { + my %toolsettings=&Apache::lonnet::dump('exttool_'.$marker,$coursedom,$coursenum); + $toolid = $toolsettings{'id'}; + } else { + $toolid = shift(@toolinfo); + } + $toolid =~ s/\D//g; + ($toolhash{'target'},$toolhash{'width'},$toolhash{'height'}) = @toolinfo; + if (ref($ltitoolsref) eq 'HASH') { + if (ref($ltitoolsref->{$toolid}) eq 'HASH') { + if ($ltitoolsref->{$toolid}->{'url'} =~ m{^https://}) { + $url =~ s/exttool$/exttools/; + } + $toolhash{'id'} = $toolid; + if ($toolhash{'target'} eq 'iframe') { + delete($toolhash{'width'}); + delete($toolhash{'height'}); + } elsif ($toolhash{'target'} eq 'window') { + foreach my $item ('width','height') { + $toolhash{$item} =~ s/^\s+//; + $toolhash{$item} =~ s/\s+$//; + } + } + my $putres = &Apache::lonnet::put('exttool_'.$marker,\%toolhash,$coursedom,$coursenum); + } + } + } if (($caller eq 'londocs') && ($folder =~ /^default/)) { if (($url =~ /\.(page|sequence)$/) && (!$donechk)) { @@ -652,7 +685,7 @@ sub group_import { } else { $fname =~ s/\W/_/g; } - if (length($fname > 15)) { + if (length($fname) > 15) { $fname = substr($fname,0,14); } my $initialtext = &mt('Replace with your own content.'); @@ -977,7 +1010,7 @@ sub docs_change_log { sub update_paste_buffer { my ($coursenum,$coursedom,$folder) = @_; - my (@possibles,%removals,%cuts); + my (@possibles,%removals,%cuts,$output); if ($env{'form.multiremove'}) { $env{'form.multiremove'} =~ s/,$//; map { $removals{$_} = 1; } split(/,/,$env{'form.multiremove'}); @@ -1022,33 +1055,41 @@ sub update_paste_buffer { foreach my $suffix (@currpaste) { my $cid = $env{'docs.markedcopy_crs_'.$suffix}; my $url = $env{'docs.markedcopy_url_'.$suffix}; + my $mapidx = $env{'docs.markedcopy_map_'.$suffix}; if (($cid =~ /^$match_domain(?:_)$match_courseid$/) && ($url ne '')) { - $pasteurls{$cid.'_'.$url} = 1; + $pasteurls{$cid.'_'.$url.'_'.$mapidx} = 1; } } } # Mark items for copying (skip any items already in user's paste buffer) my %addtoenv; - + + my @pathitems = split(/\&/,$env{'form.folderpath'}); + my @folderconf = split(/\:/,$pathitems[-1]); + my $ispage = $folderconf[4]; + foreach my $item (@possibles) { my ($orderidx,$cmd) = split(/:/,$item); next if ($orderidx =~ /\D/); next unless (($cmd eq 'cut') || ($cmd eq 'copy') || ($cmd eq 'remove')); + my $mapidx = $folder.':'.$orderidx.':'.$ispage; my ($title,$url)=split(':',$LONCAPA::map::resources[$orderidx]); my %denied = &action_restrictions($coursenum,$coursedom, &LONCAPA::map::qtescape($url), $env{'form.folderpath'},\%curr_groups); next if ($denied{'copy'}); $url=~s{http(:|:)//https(:|:)//}{https$2//}; - next if (exists($pasteurls{$coursedom.'_'.$coursenum.'_'.$url})); + next if (exists($pasteurls{$coursedom.'_'.$coursenum.'_'.$mapidx})); my ($suffix,$errortxt,$locknotfreed) = &new_timebased_suffix($env{'user.domain'},$env{'user.name'},'paste'); - push(@newpaste,$suffix); - if ($locknotfreed) { - return $locknotfreed; - last; + if ($suffix ne '') { + push(@newpaste,$suffix); + } else { + if ($locknotfreed) { + return $locknotfreed; + } } if (&is_supplemental_title($title)) { &Apache::lonnet::appenv({'docs.markedcopy_supplemental_'.$suffix => $title}); @@ -1059,7 +1100,7 @@ sub update_paste_buffer { $addtoenv{'docs.markedcopy_url_'.$suffix} = $url, $addtoenv{'docs.markedcopy_cmd_'.$suffix} = $cmd, $addtoenv{'docs.markedcopy_crs_'.$suffix} = $env{'request.course.id'}; - + $addtoenv{'docs.markedcopy_map_'.$suffix} = $mapidx; if ($url =~ m{^/uploaded/$match_domain/$match_courseid/(default|supplemental)_?(\d*)\.(page|sequence)$}) { my $prefix = $1; my $subdir =$2; @@ -1082,13 +1123,17 @@ sub update_paste_buffer { } } } + if ($locknotfreed) { + $output = $locknotfreed; + last; + } } if (@newpaste) { $addtoenv{'docs.markedcopies'} = join(',',(@currpaste,@newpaste)); } &Apache::lonnet::appenv(\%addtoenv); delete($env{'form.markcopy'}); - return; + return $output; } sub recurse_uploaded_maps { @@ -1133,14 +1178,17 @@ sub print_paste_buffer { next if ($suffix =~ /\D/); my $cid = $env{'docs.markedcopy_crs_'.$suffix}; my $url = $env{'docs.markedcopy_url_'.$suffix}; + my $mapidx = $env{'docs.markedcopy_map_'.$suffix}; if (($cid =~ /^$match_domain\_$match_courseid$/) && ($url ne '')) { $clipboardcount ++; my ($is_external,$othercourse,$fromsupp,$is_uploaded_map,$parent, - $canpaste,$nopaste,$othercrs,$areachange); + $canpaste,$nopaste,$othercrs,$areachange,$is_exttool); my $extension = (split(/\./,$env{'docs.markedcopy_url_'.$suffix}))[-1]; if ($url =~ m{^(?:/adm/wrapper/ext|(?:http|https)(?::|:))//} ) { $is_external = 1; + } elsif ($url =~ m{^/adm/$match_domain/$match_courseid/\d+/exttools?$}) { + $is_exttool = 1; } if ($folder =~ /^supplemental/) { $canpaste = &supp_pasteable($env{'docs.markedcopy_url_'.$suffix}); @@ -1158,9 +1206,7 @@ sub print_paste_buffer { 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)'); - } + $othercrs = '
'.&mt('(from another course)'); } else { $canpaste = 0; $nopaste = &mt('Paste from another course unavailable.'); @@ -1174,13 +1220,24 @@ sub print_paste_buffer { } $is_uploaded_map = 1; } + } elsif (($url =~ m{^/res/lib/templates/\w+\.problem$}) || + ($url =~ m{^/adm/($match_domain)/($match_username)/\d+/(bulletinboard|smppg)$})) { + if ($cid ne $env{'request.course.id'}) { + my ($srcdom,$srcnum) = split(/_/,$cid); + if ($env{"user.priv.cm./$srcdom/$srcnum"} =~ /\Q:mdc&F\E/) { + $othercrs = '
'.&mt('(from another course)'); + } else { + $canpaste = 0; + $nopaste = &mt('Paste from another course unavailable.'); + } + } } - } - if ($canpaste) { - push(@pasteable,$suffix); + if ($canpaste) { + push(@pasteable,$suffix); + } } my $buffer; - if ($is_external) { + if (($is_external) || ($is_exttool)) { $buffer = &mt('External Resource').': '. &LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix}).' ('. &LONCAPA::map::qtescape($url).')'; @@ -1191,10 +1248,14 @@ sub print_paste_buffer { $icon = &Apache::loncommon::lonhttpdurl($r->dir_config('lonIconsURL')); $icon .= '/navmap.folder.closed.gif'; } + my $title = $env{'docs.markedcopy_title_'.$suffix}; + if ($title eq '') { + ($title) = ($url =~ m{/([^/]+)$}); + } $buffer = ''. ': '. &Apache::loncommon::parse_supplemental_title( - &LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix})); + &LONCAPA::map::qtescape($title)); } $pasteitems .= '
'; my ($options,$onclick); @@ -1336,36 +1397,42 @@ sub supp_pasteable { (($url =~ /\.sequence$/) && ($url =~ m{^/uploaded/})) || ($url =~ m{^/uploaded/$match_domain/$match_courseid/(docs|supplemental)/(default|\d+)/\d+/}) || ($url =~ m{^/adm/$match_domain/$match_username/aboutme}) || - ($url =~ m{^/public/$match_domain/$match_courseid/syllabus})) { + ($url =~ m{^/public/$match_domain/$match_courseid/syllabus}) || + ($url =~ m{^/adm/$match_domain/$match_courseid/\d+/exttools?$})) { return 1; } return; } sub paste_popup_js { - my %lt = &Apache::lonlocal::texthash( + my %html_js_lt = &Apache::lonlocal::texthash( show => 'Show Options', hide => 'Hide Options', + ); + my %js_lt = &Apache::lonlocal::texthash( none => 'No items selected from clipboard.', ); + &html_escape(\%html_js_lt); + &js_escape(\%html_js_lt); + &js_escape(\%js_lt); return <<"END"; function showPasteOptions(suffix) { document.getElementById('pasteoptions_'+suffix).style.display='block'; - document.getElementById('pasteoptionstext_'+suffix).innerHTML = '    $lt{'hide'}'; + document.getElementById('pasteoptionstext_'+suffix).innerHTML = '    $html_js_lt{'hide'}'; return; } function hidePasteOptions(suffix) { document.getElementById('pasteoptions_'+suffix).style.display='none'; - document.getElementById('pasteoptionstext_'+suffix).innerHTML ='    $lt{'show'}'; + document.getElementById('pasteoptionstext_'+suffix).innerHTML ='    $html_js_lt{'show'}'; return; } function showOptions(caller,suffix) { if (document.getElementById('pasteoptionstext_'+suffix)) { if (caller.checked) { - document.getElementById('pasteoptionstext_'+suffix).innerHTML ='    $lt{'show'}'; + document.getElementById('pasteoptionstext_'+suffix).innerHTML ='    $html_js_lt{'show'}'; } else { document.getElementById('pasteoptionstext_'+suffix).innerHTML =''; } @@ -1394,7 +1461,7 @@ function validateClipboard() { if (numchk > 0) { return true; } else { - alert("$lt{'none'}"); + alert("$js_lt{'none'}"); return false; } } @@ -1454,10 +1521,12 @@ sub do_paste_from_buffer { } my (%msgs,%before,%after,@dopaste,%is_map,%notinsupp,%notincrs,%duplicate, - %prefixchg,%srcdom,%srcnum,%marktomove,$save_err,$lockerrors,$allresult); + %prefixchg,%srcdom,%srcnum,%srcmapidx,%marktomove,$save_err,$lockerrors,$allresult); foreach my $suffix (@topaste) { my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url_'.$suffix}); + my $cid=&LONCAPA::map::qtescape($env{'docs.markedcopy_crs_'.$suffix}); + my $mapidx=&LONCAPA::map::qtescape($env{'docs.markedcopy_map_'.$suffix}); # Supplemental content may only include certain types of content # Early out if pasted content is not supported in Supplemental area if ($folder =~ /^supplemental/) { @@ -1479,8 +1548,21 @@ sub do_paste_from_buffer { } $srcdom{$suffix} = $srcd; $srcnum{$suffix} = $srcn; + } elsif (($url =~ m{^/res/lib/templates/\w+\.problem$}) || + ($url =~ m{^/adm/$match_domain/$match_username/\d+/(bulletinboard|smppg)$})) { + my ($srcd,$srcn) = split(/_/,$cid); +# 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 (($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; } - + $srcmapidx{$suffix} = $mapidx; push(@dopaste,$suffix); if ($url=~/\.(page|sequence)$/) { $is_map{$suffix} = 1; @@ -1490,7 +1572,7 @@ sub do_paste_from_buffer { 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. +# a folder/page or a document). if (($folder =~ /^supplemental/) && (($oldprefix =~ /^default/) || ($oldprefix eq 'docs'))) { $prefixchg{$suffix} = 'docstosupp'; } elsif (($folder =~ /^default/) && ($oldprefix =~ /^supplemental/)) { @@ -1572,12 +1654,13 @@ sub do_paste_from_buffer { # Maps need to be copied first my (%removefrommap,%removeparam,%addedmaps,%rewrites,%retitles,%copies, %dbcopies,%zombies,%params,%docmoves,%mapmoves,%mapchanges,%newsubdir, - %newurls,%tomove); + %newurls,%tomove,%resdatacopy); if (ref($marktomove{$suffix}) eq 'ARRAY') { map { $tomove{$_} = 1; } @{$marktomove{$suffix}}; } my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url_'.$suffix}); my $title=&LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix}); + my $cid=&LONCAPA::map::qtescape($env{'docs.markedcopy_crs_'.$suffix}); my $oldurl = $url; if ($is_map{$suffix}) { # If pasting a map, check if map contains other maps @@ -1613,7 +1696,7 @@ sub do_paste_from_buffer { \%retitles,\%copies,\%dbcopies, \%zombies,\%params,\%mapmoves, \%mapchanges,\%tomove,\%newsubdir, - \%newurls)) { + \%newurls,\%resdatacopy)) { $mapmoves{$url} = 1; } $url = $newurl; @@ -1622,10 +1705,10 @@ sub do_paste_from_buffer { $coursenum,$srcdom{$suffix},$srcnum{$suffix}, $allmaps,\%rewrites,\%retitles,\%copies,\%dbcopies, \%zombies,\%params,\%mapmoves,\%mapchanges, - \%tomove,\%newsubdir,\%newurls); + \%tomove,\%newsubdir,\%newurls,\%resdatacopy); } } elsif ($url=~m {^/res/}) { -# published map can only exists once, so remove from paste buffer when done +# published map can only exist once, so remove from paste buffer when done push(@toclear,$suffix); # if pasting published map (main content area only) check map not already in course if ($folder =~ /^default/) { @@ -1638,13 +1721,21 @@ sub do_paste_from_buffer { } if ($url=~ m{/(bulletinboard|smppg)$}) { my $prefix = $1; + my $fromothercrs; #need to copy the db contents to a new one, unless this is a move. my %info = ( src => $url, cdom => $coursedom, cnum => $coursenum, - ); - unless ($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') { + ); + if (($srcdom{$suffix} =~ /^$match_domain$/) && ($srcnum{$suffix} =~ /^$match_courseid$/)) { + unless (($srcdom{$suffix} eq $coursedom) && ($srcnum{$suffix} eq $coursenum)) { + $fromothercrs = 1; + $info{'cdom'} = $srcdom{$suffix}; + $info{'cnum'} = $srcnum{$suffix}; + } + } + unless (($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') && (!$fromothercrs)) { my (%lockerr,$msg); my ($newurl,$result,$errtext) = &dbcopy(\%info,$coursedom,$coursenum,\%lockerr); @@ -1720,6 +1811,12 @@ sub do_paste_from_buffer { } } } + } elsif ($url =~ m{^/res/lib/templates/(\w+)\.problem$}) { + my $template = $1; + if ($newidx) { + ©_templated_files($url,$srcdom{$suffix},$srcnum{$suffix},$srcmapidx{$suffix}, + $coursedom,$coursenum,$template,$newidx,"$folder.$container"); + } } $LONCAPA::map::resources[$newidx]=$title.':'.&LONCAPA::map::qtunescape($url). ':'.$ext.':normal:res'; @@ -1733,7 +1830,8 @@ sub do_paste_from_buffer { } } -# Apply any changes to maps, or copy dependencies for uploaded HTML pages +# Apply any changes to maps, or copy dependencies for uploaded HTML pages, or update +# resourcedata for simpleproblems copied from another course unless ($allresult eq 'fail') { my %updated = ( rewrites => \%rewrites, @@ -1741,6 +1839,7 @@ sub do_paste_from_buffer { removefrommap => \%removefrommap, removeparam => \%removeparam, dbcopies => \%dbcopies, + resdatacopy => \%resdatacopy, retitles => \%retitles, ); my %info = ( @@ -1905,8 +2004,8 @@ sub get_newmap_url { sub dbcopy { my ($dbref,$coursedom,$coursenum,$lockerrorsref) = @_; my ($url,$result,$errtext); - $url = $dbref->{'src'}; if (ref($dbref) eq 'HASH') { + $url = $dbref->{'src'}; if ($url =~ m{/(smppg|bulletinboard)$}) { my $prefix = $1; if (($dbref->{'cdom'} =~ /^$match_domain$/) && @@ -1944,7 +2043,7 @@ sub dbcopy { my $photo = $contents{'uploaded.photourl'}; my ($subdir,$fname) = ($photo =~ m{^/uploaded/$match_domain/$match_courseid/+(bulletin|simplepage)/(?:|\d+/)([^/]+)$}); - my $newphoto; + my $newphoto; if ($fname ne '') { my $content = &Apache::lonnet::getfile($photo); unless ($content eq '-1') { @@ -1997,6 +2096,95 @@ sub dbcopy { return ($url,$result,$errtext); } +sub copy_templated_files { + my ($srcurl,$srcdom,$srcnum,$srcmapinfo,$coursedom,$coursenum,$template,$newidx,$newmapname) = @_; + my ($srcfolder,$srcid,$srcwaspage) = split(/:/,$srcmapinfo); + my $srccontainer = 'sequence'; + if ($srcwaspage) { + $srccontainer = 'page'; + } + my $srcsymb = "uploaded/$srcdom/$srcnum/$srcfolder.$srccontainer". + '___'.$srcid.'___'.&Apache::lonnet::declutter($srcurl); + my $srcprefix = $srcdom.'_'.$srcnum.'.'.$srcsymb; + my %srcparms=&Apache::lonnet::dump('resourcedata',$srcdom,$srcnum,$srcprefix); + my $newsymb = "uploaded/$coursedom/$coursenum/$newmapname".'___'.$newidx.'___lib/templates/'. + $template.'.problem'; + my $newprefix = $coursedom.'_'.$coursenum.'.'.$newsymb; + if ($template eq 'simpleproblem') { + $srcprefix .= '.0.'; + my $weightprefix = $newprefix; + $newprefix .= '.0.'; + my @simpleprobqtypes = qw(radio option string essay numerical); + my $qtype=$srcparms{$srcprefix.'questiontype'}; + if (grep(/^\Q$qtype\E$/,@simpleprobqtypes)) { + my %newdata; + foreach my $type (@simpleprobqtypes) { + if ($type eq $qtype) { + $newdata{"$weightprefix.$type.weight"}=1; + } else { + $newdata{"$weightprefix.$type.weight"}=0; + } + } + $newdata{$newprefix.'hiddenparts'} = '!'.$qtype; + $newdata{$newprefix.'questiontext'} = $srcparms{$srcprefix.'questiontext'}; + $newdata{$newprefix.'hinttext'} = $srcparms{$srcprefix.'hinttext'}; + if ($qtype eq 'numerical') { + $newdata{$newprefix.'numericalscript'} = $srcparms{$srcprefix.'numericalscript'}; + $newdata{$newprefix.'numericalanswer'} = $srcparms{$srcprefix.'numericalanswer'}; + $newdata{$newprefix.'numericaltolerance'} = $srcparms{$srcprefix.'numericaltolerance'}; + $newdata{$newprefix.'numericalsigfigs'} = $srcparms{$srcprefix.'numericalsigfigs'}; + } elsif (($qtype eq 'option') || ($qtype eq 'radio')) { + my $maxfoils=$srcparms{$srcprefix.'maxfoils'}; + unless (defined($maxfoils)) { $maxfoils=10; } + unless ($maxfoils=~/^\d+$/) { $maxfoils=10; } + if ($maxfoils<=0) { $maxfoils=10; } + my $randomize=$srcparms{$srcprefix.'randomize'}; + unless (defined($randomize)) { $randomize='yes'; } + unless ($randomize eq 'no') { $randomize='yes'; } + $newdata{$newprefix.'maxfoils'} = $maxfoils; + $newdata{$newprefix.'randomize'} = $randomize; + if ($qtype eq 'option') { + $newdata{$newprefix.'options'} = $srcparms{$srcprefix.'options'}; + } + for (my $i=1; $i<=10; $i++) { + $newdata{$newprefix.'value'.$i} = $srcparms{$srcprefix.'value'.$i}; + $newdata{$newprefix.'position'.$i} = $srcparms{$srcprefix.'position'.$i}; + $newdata{$newprefix.'text'.$i} = $srcparms{$srcprefix.'text'.$i}; + } + + } elsif (($qtype eq 'option') || ($qtype eq 'radio')) { + my $maxfoils=$srcparms{$srcprefix.'maxfoils'}; + unless (defined($maxfoils)) { $maxfoils=10; } + unless ($maxfoils=~/^\d+$/) { $maxfoils=10; } + if ($maxfoils<=0) { $maxfoils=10; } + my $randomize=$srcparms{$srcprefix.'randomize'}; + unless (defined($randomize)) { $randomize='yes'; } + unless ($randomize eq 'no') { $randomize='yes'; } + $newdata{$newprefix.'maxfoils'} = $maxfoils; + $newdata{$newprefix.'randomize'} = $randomize; + if ($qtype eq 'option') { + $newdata{$newprefix.'options'} = $srcparms{$srcprefix.'options'}; + } + for (my $i=1; $i<=10; $i++) { + $newdata{$newprefix.'value'.$i} = $srcparms{$srcprefix.'value'.$i}; + $newdata{$newprefix.'position'.$i} = $srcparms{$srcprefix.'position'.$i}; + $newdata{$newprefix.'text'.$i} = $srcparms{$srcprefix.'text'.$i}; + } + } elsif ($qtype eq 'string') { + $newdata{$newprefix.'stringanswer'} = $srcparms{$srcprefix.'stringanswer'}; + $newdata{$newprefix.'stringtype'} = $srcparms{$srcprefix.'stringtype'}; + } + if (keys(%newdata)) { + my $putres = &Apache::lonnet::cput('resourcedata',\%newdata,$coursedom, + $coursenum); + if ($putres eq 'ok') { + &Apache::lonnet::devalidatecourseresdata($coursenum,$coursedom); + } + } + } + } +} + sub uniqueness_check { my ($newurl) = @_; my $unique = 1; @@ -2062,7 +2250,7 @@ sub contained_map_check { sub url_paste_fixups { my ($oldurl,$folder,$prefixchg,$cdom,$cnum,$fromcdom,$fromcnum,$allmaps, $rewrites,$retitles,$copies,$dbcopies,$zombies,$params,$mapmoves, - $mapchanges,$tomove,$newsubdir,$newurls) = @_; + $mapchanges,$tomove,$newsubdir,$newurls,$resdatacopy) = @_; my $checktitle; if (($prefixchg) && ($oldurl =~ m{^/uploaded/$match_domain/$match_courseid/supplemental})) { @@ -2111,7 +2299,7 @@ sub url_paste_fixups { $srcdom,$srcnum,$allmaps,$rewrites, $retitles,$copies,$dbcopies,$zombies, $params,$mapmoves,$mapchanges,$tomove, - $newsubdir,$newurls); + $newsubdir,$newurls,$resdatacopy); next; } else { ($newurl,my $error) = @@ -2135,7 +2323,7 @@ sub url_paste_fixups { $cnum,$srcdom,$srcnum,$allmaps, $rewrites,$retitles,$copies,$dbcopies, $zombies,$params,$mapmoves,$mapchanges, - $tomove,$newsubdir,$newurls)) { + $tomove,$newsubdir,$newurls,$resdatacopy)) { $mapmoves->{$ressrc} = 1; } $changed = 1; @@ -2164,6 +2352,12 @@ sub url_paste_fixups { $dbcopies->{$oldurl}{$id}{'cnum'} = $fromcnum; $changed = 1; } + } elsif ($ressrc eq '/res/lib/templates/simpleproblem.problem') { + if (($fromcdom ne $cdom) || ($fromcnum ne $cnum)) { + $resdatacopy->{$oldurl}{$id}{'src'} = $ressrc; + $resdatacopy->{$oldurl}{$id}{'cdom'} = $fromcdom; + $resdatacopy->{$oldurl}{$id}{'cnum'} = $fromcnum; + } } elsif ($ressrc =~ m{^/public/($match_domain)/($match_courseid)/(.+)$}) { next if ($skip); my $srcdom = $1; @@ -2195,7 +2389,7 @@ sub apply_fixups { $oldurl,$url,$caller) = @_; my (%rewrites,%zombies,%removefrommap,%removeparam,%dbcopies,%retitles, %params,%newsubdir,%before,%after,%copies,%docmoves,%mapmoves,@msgs, - %lockerrors,$lockmsg); + %resdatacopy,%lockerrors,$lockmsg); if (ref($updated) eq 'HASH') { if (ref($updated->{'rewrites'}) eq 'HASH') { %rewrites = %{$updated->{'rewrites'}}; @@ -2215,6 +2409,9 @@ sub apply_fixups { if (ref($updated->{'retitles'}) eq 'HASH') { %retitles = %{$updated->{'retitles'}}; } + if (ref($updated->{'resdatacopy'}) eq 'HASH') { + %resdatacopy = %{$updated->{'resdatacopy'}}; + } } if (ref($info) eq 'HASH') { if (ref($info->{'newsubdir'}) eq 'HASH') { @@ -2365,6 +2562,35 @@ sub apply_fixups { } } } + if (ref($resdatacopy{$key}) eq 'HASH') { + if ($newsubdir{$key}) { + + } + foreach my $idx (keys(%{$resdatacopy{$key}})) { + if (ref($resdatacopy{$key}{$idx}) eq 'HASH') { + my $srcurl = $resdatacopy{$key}{$idx}{'src'}; + if ($srcurl =~ m{^/res/lib/templates/(\w+)\.problem$}) { + my $template = $1; + if (($resdatacopy{$key}{$idx}{'cdom'} =~ /^$match_domain$/) && + ($resdatacopy{$key}{$idx}{'cnum'} =~ /^$match_courseid$/)) { + my $srcdom = $resdatacopy{$key}{$idx}{'cdom'}; + my $srcnum = $resdatacopy{$key}{$idx}{'cnum'}; + my ($newmapname) = ($key =~ m{/([^/]+)$}); + my ($srcfolder,$srccontainer) = split(/\./,$newmapname); + my $srcmapinfo = $srcfolder.':'.$idx; + if ($srccontainer eq 'page') { + $srcmapinfo .= ':1'; + } + if ($newsubdir{$key}) { + $newmapname =~ s/^((?:default|supplemental)_)(\d+)/$1$newsubdir{$key}/; + } + ©_templated_files($srcurl,$srcdom,$srcnum,$srcmapinfo,$cdom, + $cnum,$template,$idx,$newmapname); + } + } + } + } + } if (ref($params{$key}) eq 'HASH') { %currparam = %{$params{$key}}; } @@ -2603,26 +2829,44 @@ sub update_parameter { } return $haschanges; } else { - return 0 if ($env{'form.changeparms'} !~ /^($valid_parameters_re)$/); + my $haschanges = 0; + return $haschanges if ($env{'form.changeparms'} !~ /^($valid_parameters_re)$/); my $which = $env{'form.changeparms'}; my $idx = $env{'form.setparms'}; + my $oldvalue = 0; + my $newvalue = 0; + my $current = (&LONCAPA::map::getparameter($idx,'parameter_'.$which))[0]; + if ($which eq 'randompick') { + if ($current =~ /^(\d+)$/) { + $oldvalue = $1; + } + } elsif ($current =~ /^yes$/i) { + $oldvalue = 1; + } if ($env{'form.'.$which.'_'.$idx}) { - my $value = ($which eq 'randompick') ? $env{'form.rpicknum_'.$idx} - : 'yes'; - &LONCAPA::map::storeparameter($idx, 'parameter_'.$which, $value, - $parameter_type{$which}); - &remember_parms($idx,$which,'set',$value); - } else { - &LONCAPA::map::delparameter($idx,'parameter_'.$which); - - &remember_parms($idx,$which,'del'); + $newvalue = ($which eq 'randompick') ? $env{'form.rpicknum_'.$idx} + : 1; } - return 1; + if ($oldvalue ne $newvalue) { + $haschanges = 1; + if ($newvalue) { + my $storeval = 'yes'; + if ($which eq 'randompick') { + $storeval = $newvalue; + } + &LONCAPA::map::storeparameter($idx, 'parameter_'.$which, $storeval, + $parameter_type{$which}); + &remember_parms($idx,$which,'set',$storeval); + } else { + &LONCAPA::map::delparameter($idx,'parameter_'.$which); + &remember_parms($idx,$which,'del'); + } + } + return $haschanges; } } - sub handle_edit_cmd { my ($coursenum,$coursedom) =@_; if ($env{'form.cmd'} eq '') { @@ -2672,7 +2916,7 @@ sub handle_edit_cmd { sub editor { my ($r,$coursenum,$coursedom,$folder,$allowed,$upload_output,$crstype, - $supplementalflag,$orderhash,$iconpath,$pathitem)=@_; + $supplementalflag,$orderhash,$iconpath,$pathitem,$ltitoolsref)=@_; my ($randompick,$ishidden,$isencrypted,$plain,$is_random_order,$container); if ($allowed) { (my $breadcrumbtrail,$randompick,$ishidden,$isencrypted,$plain, @@ -2714,7 +2958,7 @@ sub editor { &snapshotbefore(); if (&update_parameter()) { - ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container); + ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container,1); return $errtext if ($fatal); } @@ -2776,9 +3020,9 @@ sub editor { $r->print($upload_output); # Rename, cut, copy or remove a single resource - if (&handle_edit_cmd()) { + if (&handle_edit_cmd($coursenum,$coursedom)) { my $contentchg; - if ($env{'form.cmd'} =~ m{^(del|cut)_}) { + if ($env{'form.cmd'} =~ m{^(remove|cut)_}) { $contentchg = 1; } ($errtext,$fatal)=&storemap($coursenum,$coursedom,$folder.'.'.$container,$contentchg); @@ -2810,7 +3054,7 @@ sub editor { my ($name,$url)=split(/\:/,$LONCAPA::map::resources[$res]); $name=&LONCAPA::map::qtescape($name); $url=&LONCAPA::map::qtescape($url); - next unless ($name && $url); + next unless $url; my %denied = &action_restrictions($coursenum,$coursedom,$url, $env{'form.folderpath'},\%curr_groups); @@ -2871,6 +3115,17 @@ sub editor { } else { return $errortxt; } + } elsif ($url =~ m{^/adm/$coursedom/$coursenum/new/exttool}) { + my ($suffix,$errortxt,$locknotfreed) = + &new_timebased_suffix($coursedom,$coursenum,'exttool'); + if ($locknotfreed) { + $r->print($locknotfreed); + } + if ($suffix) { + $url =~ s{^(/adm/$coursedom/$coursenum)/new}{$1/$suffix}; + } else { + return $errortxt; + } } elsif ($url =~ m{^/uploaded/$coursedom/$coursenum/(docs|supplemental)/(default|\d+)/new.html$}) { if ($supplementalflag) { next unless ($1 eq 'supplemental'); @@ -2893,7 +3148,7 @@ sub editor { } ($errtext,$fatal,my $fixuperrors) = &group_import($coursenum, $coursedom, $folder,$container, - 'londocs',@imports); + 'londocs',$ltitoolsref,@imports); return $errtext if ($fatal); if ($fixuperrors) { $r->print($fixuperrors); @@ -2978,7 +3233,7 @@ sub editor { $output .= &entryline($idx,$name,$url,$folder,$allowed,$res, $coursenum,$coursedom,$crstype, $pathitem,$supplementalflag,$container, - \%filters,\%curr_groups); + \%filters,\%curr_groups,$ltitoolsref); $idx++; $shown++; } @@ -3174,7 +3429,9 @@ sub process_file_upload { } my $quotatype = 'unofficial'; if ($crstype eq 'Community') { - $quotatype = 'community'; + $quotatype = 'community'; + } elsif ($crstype eq 'Placement') { + $quotatype = 'placement'; } elsif ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.coursecode'}) { $quotatype = 'official'; } elsif ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.textbook'}) { @@ -3344,7 +3601,8 @@ sub is_supplemental_title { sub entryline { my ($index,$title,$url,$folder,$allowed,$residx,$coursenum,$coursedom, - $crstype,$pathitem,$supplementalflag,$container,$filtersref,$currgroups)=@_; + $crstype,$pathitem,$supplementalflag,$container,$filtersref,$currgroups, + $ltitoolsref)=@_; my ($foldertitle,$renametitle,$oldtitle); if (&is_supplemental_title($title)) { ($title,$foldertitle,$renametitle) = &Apache::loncommon::parse_supplemental_title($title); @@ -3358,6 +3616,7 @@ sub entryline { $renametitle=~s/\\/\\\\/g; $renametitle=~s/\"\;/\\\"/g; + $renametitle=~s/"/%22/g; $renametitle=~s/ /%20/g; $oldtitle = $renametitle; $renametitle=~s/\'/\\\'/g; @@ -3434,6 +3693,7 @@ END 'rn' => 'Rename', 'cp' => 'Copy', 'ex' => 'External Resource', + 'et' => 'External Tool', 'ed' => 'Edit', 'pr' => 'Preview', 'sv' => 'Save', @@ -3451,6 +3711,7 @@ END |/aboutme$ |/navmaps$ |/bulletinboard$ + |/exttools?$ |\.html$)}x) || $isexternal) { $skip_confirm = 1; @@ -3587,7 +3848,9 @@ END } } elsif ($url=~m|^/ext/|) { $url='/adm/wrapper'.$url; - } + } elsif ($url=~m{^/adm/$coursedom/$coursenum/\d+/exttools?$}) { + $url='/adm/wrapper'.$url; + } if (&Apache::lonnet::symbverify($symb,$url)) { $url.=(($url=~/\?/)?'&':'?').'symb='.&escape($symb); } else { @@ -3653,12 +3916,17 @@ $form_common."\n". $form_end; } } elsif ($supplementalflag && !$allowed) { + my $isexttool; + if ($url=~m{^/adm/$coursedom/$coursenum/\d+/exttools?$}) { + $url='/adm/wrapper'.$url; + $isexttool = 1; + } $url .= ($url =~ /\?/) ? '&':'?'; $url .= 'folderpath='.&HTML::Entities::encode($esc_path,'<>&"'); if ($title) { $url .= '&title='.&HTML::Entities::encode($renametitle,'<>&"'); } - if ($isexternal && $orderidx) { + if ((($isexternal) || ($isexttool)) && $orderidx) { $url .= '&idx='.$orderidx; } } @@ -3669,6 +3937,11 @@ $form_end; if ($isexternal) { ($editlink,$extresform) = &Apache::lonextresedit::extedit_form(0,$residx,$orig_url,$title,$pathitem); + } elsif ($orig_url =~ m{^/adm/$coursedom/$coursenum/\d+/exttools?$}) { + ($editlink,$extresform) = + &Apache::lonextresedit::extedit_form(0,$residx,$orig_url,$title,$pathitem, + undef,undef,undef,'tool',$coursedom, + $coursenum,$ltitoolsref); } elsif (!$isfolder && !$ispage) { my ($cfile,$home,$switchserver,$forceedit,$forceview) = &Apache::lonnet::can_edit_resource($fileloc,$coursenum,$coursedom,$orig_url); @@ -3767,9 +4040,11 @@ sub action_restrictions { 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=~m{^/res/lib/templates/([^/]+)\.problem$}) { + unless ($1 eq 'simpleproblem') { + $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)) { @@ -3825,6 +4100,7 @@ sub new_timebased_suffix { if ($type eq 'paste') { $prefix = $type; $namespace = 'courseeditor'; + $idtype = 'addcode'; } elsif ($type eq 'map') { $prefix = 'docs'; if ($area eq 'supplemental') { @@ -3836,9 +4112,8 @@ sub new_timebased_suffix { $prefix = $type; $namespace = 'templated'; } - $idtype = 'concat'; my ($suffix,$freedlock,$error) = - &Apache::lonnet::get_timebased_id($prefix,'num',$namespace,$dom,$num); + &Apache::lonnet::get_timebased_id($prefix,'num',$namespace,$dom,$num,$idtype); if (!$suffix) { if ($type eq 'paste') { $errtext = &mt('Failed to acquire a unique timestamp-based suffix when adding to the paste buffer.'); @@ -3858,9 +4133,19 @@ sub new_timebased_suffix { '
'. &mt('There was a problem removing a lockfile.').' '; if ($type eq 'paste') { - &mt('This will prevent use of the paste buffer until th next log-in.'); + if ($freedlock eq 'nolock') { + $locknotfreed = + '
'. + &mt('A lockfile was not released when you added content to the clipboard earlier in this session.').' '. + + &mt('As a result addition of items to the clipboard will be unavailable until your next log-in.'); + } else { + $locknotfreed .= + &mt('This will prevent addition of items to the clipboard until your next log-in.'); + } } elsif ($type eq 'map') { - &mt('This will prevent creation of additional folders or composite pages in this course.'); + $locknotfreed .= + &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.'); @@ -4568,6 +4853,7 @@ sub handler { my $container; my $containertag; my $pathitem; + my %ltitools; # Do we directly jump somewhere? @@ -4705,11 +4991,14 @@ sub handler { } } my $tabidstr = join("','",@tabids); - $script .= &editing_js($udom,$uname,$supplementalflag). + %ltitools = &Apache::lonnet::get_domain_ltitools($coursedom); + my $posslti = keys(%ltitools); + my $exttoolurl = "/adm/$coursedom/$coursenum/new/exttool"; + $script .= &editing_js($udom,$uname,$supplementalflag,$posslti). &history_tab_js(). &inject_data_js(). &Apache::lonhtmlcommon::resize_scrollbox_js('docs',$tabidstr,$tid). - &Apache::lonextresedit::extedit_javascript(); + &Apache::lonextresedit::extedit_javascript(\%ltitools); $addentries = { onload => "javascript:resize_scrollbox('contentscroll','1','1');", }; @@ -4725,7 +5014,9 @@ sub handler { .'// '."\n" - .''."\n"; + .''."\n" + .''."\n"; # Breadcrumbs &Apache::lonhtmlcommon::clear_breadcrumbs(); @@ -4825,6 +5116,8 @@ sub handler { 'impo' => 'Import', 'lnks' => 'Import from Stored Links', 'impm' => 'Import from Assembled Map', + 'extr' => 'External Resource', + 'extt' => 'External Tool', 'selm' => 'Select Map', 'load' => 'Load Map', 'newf' => 'New Folder', @@ -4855,9 +5148,32 @@ sub handler { 'webctce4' => 'WebCT 4 Campus Edition', ); # ----------------------------------------------------------------------------- + + # Calculate free quota space for a user or course. A javascript function checks + # file size to determine if upload should be allowed. + my $quotatype = 'unofficial'; + if ($crstype eq 'Community') { + $quotatype = 'community'; + } elsif ($crstype eq 'Placement') { + $quotatype = 'placement'; + } elsif ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.coursecode'}) { + $quotatype = 'official'; + } elsif ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.textbook'}) { + $quotatype = 'textbook'; + } + my $disk_quota = &Apache::loncommon::get_user_quota($coursenum,$coursedom, + 'course',$quotatype); # expressed in MB + my $current_disk_usage = 0; + foreach my $subdir ('docs','supplemental') { + $current_disk_usage += &Apache::lonnet::diskusage($coursedom,$coursenum, + "userfiles/$subdir",1); # expressed in kB + } + my $free_space = 1024 * ((1024 * $disk_quota) - $current_disk_usage); + my $fileupload=(< - + + FIUP my $checkbox=(<