--- loncom/interface/londocs.pm 2014/08/25 01:42:30 1.584 +++ loncom/interface/londocs.pm 2020/09/08 19:15:18 1.674 @@ -1,7 +1,7 @@ # The LearningOnline Network # Documents # -# $Id: londocs.pm,v 1.584 2014/08/25 01:42:30 raeburn Exp $ +# $Id: londocs.pm,v 1.674 2020/09/08 19:15:18 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -43,12 +43,18 @@ use Apache::lonnavdisplay(); use Apache::lonextresedit(); use Apache::lontemplate(); use Apache::lonsimplepage(); +use Apache::lonhomework(); +use Apache::lonpublisher(); +use Apache::lonparmset(); +use Apache::loncourserespicker(); use HTML::Entities; use HTML::TokeParser; use GDBM_File; use File::MMagic; +use File::Copy; use Apache::lonlocal; use Cwd; +use UUID::Tiny ':std'; use LONCAPA qw(:DEFAULT :match); my $iconpath; @@ -139,7 +145,43 @@ sub clean { return $title; } - +sub default_folderpath { + my ($coursenum,$coursedom,$navmapref) = @_; + return unless ($coursenum && $coursedom && ref($navmapref)); +# Check if entire course is hidden and/or encrypted + my ($hiddenmap,$encryptmap,$folderpath,$hiddentop); + my $toplevel = "uploaded/$coursedom/$coursenum/default.sequence"; + unless (ref($$navmapref)) { + $$navmapref = Apache::lonnavmaps::navmap->new(); + } + if (ref($$navmapref)) { + if (lc($$navmapref->get_mapparam(undef,$toplevel,"0.hiddenresource")) eq 'yes') { + my $filterFunc = sub { my $res = shift; return (!$res->randomout() && !$res->is_map()) }; + my @resources = $$navmapref->retrieveResources($toplevel,$filterFunc,1,1); + unless (@resources) { + $hiddenmap = 1; + unless ($env{'request.role.adv'}) { + $hiddentop = 1; + if ($env{'form.folder'}) { + undef($env{'form.folder'}); + } + } + } + } + if (lc($$navmapref->get_mapparam(undef,$toplevel,"0.encrypturl")) eq 'yes') { + $encryptmap = 1; + } + } + unless ($hiddentop) { + $folderpath='default&'.&escape(&mt('Main Content')). + '::'.$hiddenmap.':'.$encryptmap.'::'; + } + if (wantarray) { + return ($folderpath,$hiddentop); + } else { + return $folderpath; + } +} sub dumpcourse { my ($r) = @_; @@ -564,9 +606,9 @@ sub recurse_html { } else { $relfile = $dependency; $depurl = $currurlpath; - $depurl =~ s{[^/]+$}{}; + $depurl =~ s{[^/]+$}{}; $depurl .= $dependency; - ($newcontainer) = ($depurl =~ m{^\Q$prefix\E(.+)$}); + ($newcontainer) = ($depurl =~ m{^\Q$prefix\E(.+)$}); } next if ($relfile eq ''); my $newname = $replacehash->{$container}; @@ -586,7 +628,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 +657,157 @@ sub group_import { } } if ($url) { + if ($url =~ m{^(/adm/$coursedom/$coursenum/(\d+)/ext\.tool)\:?(.*)$}) { + $url = $1; + my $marker = $2; + my $info = $3; + my ($toolid,%toolhash,%toolsettings); + my @extras = ('linktext','explanation','crslabel','crstitle','crsappend'); + my @toolinfo = split(/:/,$info); + if ($residx) { + %toolsettings=&Apache::lonnet::dump('exttool_'.$marker,$coursedom,$coursenum); + $toolid = $toolsettings{'id'}; + } else { + $toolid = shift(@toolinfo); + } + $toolid =~ s/\D//g; + ($toolhash{'target'},$toolhash{'width'},$toolhash{'height'}, + $toolhash{'linktext'},$toolhash{'explanation'},$toolhash{'crslabel'}, + $toolhash{'crstitle'},$toolhash{'crsappend'},$toolhash{'gradable'}) = @toolinfo; + foreach my $item (@extras) { + $toolhash{$item} = &unescape($toolhash{$item}); + } + if ($folder =~ /^supplemental/) { + delete($toolhash{'gradable'}); + } else { + $toolhash{'gradable'} =~ s/\D+//g; + } + if (ref($ltitoolsref) eq 'HASH') { + if (ref($ltitoolsref->{$toolid}) eq 'HASH') { + my @deleted; + $toolhash{'id'} = $toolid; + if (($toolhash{'target'} eq 'iframe') || ($toolhash{'target'} eq 'tab') || + ($toolhash{'target'} eq 'window')) { + if ($toolhash{'target'} eq 'window') { + foreach my $item ('width','height') { + $toolhash{$item} =~ s/^\s+//; + $toolhash{$item} =~ s/\s+$//; + if ($toolhash{$item} =~ /\D/) { + delete($toolhash{$item}); + if ($residx) { + if ($toolsettings{$item}) { + push(@deleted,$item); + } + } + } + } + } + } elsif ($residx) { + $toolhash{'target'} = $toolsettings{'target'}; + if ($toolhash{'target'} eq 'window') { + foreach my $item ('width','height') { + $toolhash{$item} = $toolsettings{$item}; + } + } + } elsif (ref($ltitoolsref->{$toolid}->{'display'}) eq 'HASH') { + $toolhash{'target'} = $ltitoolsref->{$toolid}->{'display'}->{'target'}; + if ($toolhash{'target'} eq 'window') { + $toolhash{'width'} = $ltitoolsref->{$toolid}->{'display'}->{'width'}; + $toolhash{'height'} = $ltitoolsref->{$toolid}->{'display'}->{'height'}; + } + } + if ($toolhash{'target'} eq 'iframe') { + foreach my $item ('width','height','linktext','explanation') { + delete($toolhash{$item}); + if ($residx) { + if ($toolsettings{$item}) { + push(@deleted,$item); + } + } + } + } elsif ($toolhash{'target'} eq 'tab') { + foreach my $item ('width','height') { + delete($toolhash{$item}); + if ($residx) { + if ($toolsettings{$item}) { + push(@deleted,$item); + } + } + } + } + if (ref($ltitoolsref->{$toolid}->{'crsconf'}) eq 'HASH') { + foreach my $item ('label','title','linktext','explanation') { + my $crsitem; + if (($item eq 'label') || ($item eq 'title')) { + $crsitem = 'crs'.$item; + } else { + $crsitem = $item; + } + if ($ltitoolsref->{$toolid}->{'crsconf'}->{$item}) { + $toolhash{$crsitem} =~ s/^\s+//; + $toolhash{$crsitem} =~ s/\s+$//; + if ($toolhash{$crsitem} eq '') { + delete($toolhash{$crsitem}); + } + } else { + delete($toolhash{$crsitem}); + } + if (($residx) && (exists($toolsettings{$crsitem}))) { + unless (exists($toolhash{$crsitem})) { + push(@deleted,$crsitem); + } + } + } + } + if ($toolhash{'passback'}) { + my $gradesecret = UUID::Tiny::create_uuid_as_string(UUID_V4); + $toolhash{'gradesecret'} = $gradesecret; + $toolhash{'gradesecretdate'} = time; + } + if ($toolhash{'roster'}) { + my $rostersecret = UUID::Tiny::create_uuid_as_string(UUID_V4); + $toolhash{'rostersecret'} = $rostersecret; + $toolhash{'rostersecretdate'} = time; + } + my $changegradable; + if (($residx) && ($folder =~ /^default/)) { + if ($toolsettings{'gradable'}) { + unless (($toolhash{'gradable'}) || (defined($LONCAPA::map::zombies[$residx]))) { + push(@deleted,'gradable'); + $changegradable = 1; + } + } elsif ($toolhash{'gradable'}) { + $changegradable = 1; + } + if (($caller eq 'londocs') && (defined($LONCAPA::map::zombies[$residx]))) { + $changegradable = 1; + if ($toolsettings{'gradable'}) { + $toolhash{'gradable'} = 1; + } + } + } + my $putres = &Apache::lonnet::put('exttool_'.$marker,\%toolhash,$coursedom,$coursenum); + if ($putres eq 'ok') { + if (@deleted) { + &Apache::lonnet::del('exttool_'.$marker,\@deleted,$coursedom,$coursenum); + } + if (($changegradable) && ($folder =~ /^default/)) { + my $val; + if ($toolhash{'gradable'}) { + $val = 'yes'; + } else { + $val = 'no'; + } + &LONCAPA::map::storeparameter($residx,'parameter_0_gradable',$val, + 'string_yesno'); + &remember_parms($residx,'gradable','set',$val); + } + } else { + return (&mt('Failed to save update to external tool.'),1); + } + } + } + } if (($caller eq 'londocs') && ($folder =~ /^default/)) { if (($url =~ /\.(page|sequence)$/) && (!$donechk)) { @@ -626,8 +819,8 @@ sub group_import { $donechk = 1; } if ($url =~ m{^/uploaded/\Q$coursedom\E/\Q$coursenum\E/(default_\d+\.)(page|sequence)$}) { - &contained_map_check($url,$folder,\%removefrommap,\%removeparam, - \%addedmaps,\%hierarchy,\%titles,$allmaps); + &contained_map_check($url,$folder,$coursenum,$coursedom,\%removefrommap, + \%removeparam,\%addedmaps,\%hierarchy,\%titles,$allmaps); $importuploaded = 1; } elsif ($url =~ m{^/res/.+\.(page|sequence)$}) { next if ($allmaps->{$url}); @@ -652,7 +845,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.'); @@ -667,7 +860,7 @@ $initialtext END $env{'form.output'}=$newhtml; - my $result = + my $result = &Apache::lonnet::finishuserfileupload($coursenum,$coursedom, 'output', "$filepath/$residx/$fname.html"); @@ -791,12 +984,14 @@ sub log_docs { } sub docs_change_log { - my ($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath)=@_; + my ($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath,$canedit)=@_; my $supplementalflag=($env{'form.folderpath'}=~/^supplemental/); + my $navmap; my $js = ''."\n". -'
'. +''. '

'. &mt($message,' "/adm/coursedocs?tools=1&verify=1&checkstale=$env{'form.checkstale'}", + text=>'Results', + faq=>273, + bug=>'Instructor Interface'}); + &verifycontent($r,$env{'form.checkstale'}); + } else { + &contentverifyform($r); + } } elsif ($allowed && $env{'form.listsymbs'}) { &init_breadcrumbs('listsymbs','List Content IDs'); &list_symbs($r); + } elsif ($allowed && $env{'form.shorturls'}) { + &init_breadcrumbs('shorturls','Set/Display Shortened URLs','Docs_Short_URLs'); + &short_urls($r,$canedit); } elsif ($allowed && $env{'form.docslog'}) { &init_breadcrumbs('docslog','Show Log'); my $folder = $env{'form.folder'}; if ($folder eq '') { $folder='default'; } - &docs_change_log($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath); + &docs_change_log($r,$coursenum,$coursedom,$folder,$allowed,$crstype,$iconpath,$canedit); } elsif ($allowed && $env{'form.versions'}) { &init_breadcrumbs('versions','Check/Set Resource Versions','Docs_Check_Resource_Versions'); - &checkversions($r); - } elsif ($allowed && $env{'form.dumpcourse'}) { + &checkversions($r,$canedit); + } elsif ($canedit && $env{'form.dumpcourse'}) { &init_breadcrumbs('dumpcourse','Copy '.&Apache::loncommon::course_type().' Content to Authoring Space'); &dumpcourse($r); - } elsif ($allowed && $env{'form.exportcourse'}) { + } elsif ($canedit && $env{'form.exportcourse'}) { &init_breadcrumbs('exportcourse','IMS Export'); &Apache::imsexport::exportcourse($r); } else { + if ($canedit && $env{'form.authorrole'}) { + $noendpage = 1; + my ($redirect,$error) = &makenewproblem($r,$coursedom,$coursenum); + if ($redirect) { + if (($env{'form.newresourceadd'}) && ($env{'form.folderpath'})) { + my $container = 'sequence'; + my ($breadcrumbtrail,$randompick,$ishidden,$isencrypted,$plain, + $is_random_order,$container) = + &Apache::lonhtmlcommon::docs_breadcrumbs($allowed,$crstype,1); + my (@folders)=split('&',$env{'form.folderpath'}); + $env{'form.foldername'}=&unescape(pop(@folders)); + my $folder=pop(@folders); + my ($errtext,$fatal) = &mapread($coursenum,$coursedom, + $folder.'.'.$container); + my $warning; + if ($fatal) { + if ($container eq 'page') { + $warning = &mt('An error occurred retrieving the contents of the current page.'); + } else { + $warning = &mt('An error occurred retrieving the contents of the current folder.'); + } + } else { + my $url = $redirect; + my $srcfile = $londocroot.$url; + $url =~ s{^/priv/}{/res/}; + my $targetfile = $londocroot.$url; + my $nokeyref = &Apache::lonpublisher::getnokey($r->dir_config('lonIncludes')); + my $output = &Apache::lonpublisher::batchpublish($r,$srcfile,$targetfile,$nokeyref,1); + $env{'form.folder'} = $folder; + &snapshotbefore(); + my $title = &LONCAPA::map::qtunescape($env{'form.newresourcetitle'}); + my $ext = 'false'; + my $newidx = &LONCAPA::map::getresidx(&LONCAPA::map::qtunescape($url)); + $LONCAPA::map::resources[$newidx]=$title.':'.&LONCAPA::map::qtunescape($url). + ':'.$ext.':normal:res'; + push(@LONCAPA::map::order,$newidx); + &LONCAPA::map::storeparameter($newidx,'parameter_hiddenresource','yes', + 'string_yesno'); + &remember_parms($newidx,'hiddenresource','set','yes'); + ($errtext,$fatal) = + &storemap($coursenum, $coursedom, $folder.'.'.$container,1); + &log_differences($plain); + &mark_hash_old(); + $r->internal_redirect($redirect); + return OK; + } + } else { + $r->internal_redirect($redirect); + } + } + } # # Done catching special calls # The whole rest is for course and supplemental documents and utilities menu @@ -4540,6 +5501,26 @@ sub handler { 'forcesupplement','forcestandard', 'tools','symb','command','supppath']); + foreach my $item ('forcesupplement','forcestandard','tools') { + next if ($env{'form.'.$item} eq ''); + unless ($env{'form.'.$item} eq '1') { + delete($env{'form.'.$item}); + } + } + + if ($env{'form.command'}) { + unless ($env{'form.command'} =~ /^(direct|directnav|editdocs|editsupp|contents|home)$/) { + delete($env{'form.command'}); + } + } + + if ($env{'form.symb'}) { + my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($env{'form.symb'}); + unless (($id =~ /^\d+$/) && (&Apache::lonnet::is_on_map($resurl))) { + delete($env{'form.symb'}); + } + } + # 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 @@ -4562,19 +5543,54 @@ sub handler { my $toolsflag=0; if ($env{'form.tools'}) { $toolsflag=1; } + if ($env{'form.folderpath'} ne '') { + my @items = split(/\&/,$env{'form.folderpath'}); + my $badpath; + for (my $i=0; $i<@items; $i++) { + my $odd = $i%2; + if (($odd) && (!$supplementalflag) && ($items[$i] !~ /^[^:]*:(|\d+):(|1):(|1):(|1):(|1)$/)) { + $badpath = 1; + } elsif ((!$odd) && ($items[$i] !~ /^(default|supplemental)(|_\d+)$/)) { + $badpath = 1; + } + last if ($badpath); + } + if ($badpath) { + delete($env{'form.folderpath'}); + } + } + + if ($env{'form.supppath'} ne '') { + my @items = split(/\&/,$env{'form.supppath'}); + my $badpath; + for (my $i=0; $i<@items; $i++) { + my $odd = $i%2; + if ((!$odd) && ($items[$i] !~ /^supplemental(|_\d+)$/)) { + $badpath = 1; + } + last if ($badpath); + } + if ($badpath) { + delete($env{'form.supppath'}); + } + } + my $script=''; my $showdoc=0; my $addentries = {}; my $container; my $containertag; my $pathitem; + my %ltitools; + my $hiddentop; + my $navmap; + my $filterFunc = sub { my $res = shift; return (!$res->randomout() && !$res->is_map()) }; # Do we directly jump somewhere? - if (($env{'form.command'} eq 'direct') || ($env{'form.command'} eq 'directnav')) { if ($env{'form.symb'} ne '') { $env{'form.folderpath'}= - &Apache::loncommon::symb_to_docspath($env{'form.symb'}); + &Apache::loncommon::symb_to_docspath($env{'form.symb'},\$navmap); &Apache::lonnet::appenv({'docs.exit.'.$env{'request.course.id'} => $env{'form.command'}.'_'.$env{'form.symb'}}); } elsif ($env{'form.supppath'} ne '') { @@ -4583,12 +5599,10 @@ sub handler { $env{'form.command'}.'_'.$env{'form.supppath'}}); } } elsif ($env{'form.command'} eq 'editdocs') { - $env{'form.folderpath'} = 'default&'. - &escape(&mt('Main Content').':::::'); + $env{'form.folderpath'} = &default_folderpath($coursenum,$coursedom,\$navmap); &Apache::lonnet::appenv({'docs.exit.'.$env{'request.course.id'} => $env{'form.command'}}); } elsif ($env{'form.command'} eq 'editsupp') { - $env{'form.folderpath'} = 'supplemental&'. - &escape('Supplemental Content'); + $env{'form.folderpath'} = &supplemental_base(); &Apache::lonnet::appenv({'docs.exit.'.$env{'request.course.id'} => '/adm/supplemental'}); } elsif ($env{'form.command'} eq 'contents') { &Apache::lonnet::appenv({'docs.exit.'.$env{'request.course.id'} => '/adm/navmaps'}); @@ -4607,7 +5621,14 @@ sub handler { if ((!$env{'form.folderpath'}) && $allowed) { &Apache::loncommon::restore_course_settings($stored_folderpath, {'folderpath' => 'scalar'}); - unless (&unescape($env{'form.folderpath'}) =~ m{^(default|supplemental)&}) { + + if (&unescape($env{'form.folderpath'}) =~ m{^(default|supplemental)&}) { + if ($supplementalflag) { + undef($env{'form.folderpath'}) if ($1 eq 'default'); + } else { + undef($env{'form.folderpath'}) if ($1 eq 'supplemental'); + } + } else { undef($env{'form.folderpath'}); } } @@ -4624,19 +5645,48 @@ sub handler { .'&'. $env{'form.folderpath'}; } +# If allowed and user's role is not advanced check folderpath is not hidden + if (($allowed) && (!$env{'request.role.adv'}) && + ($env{'form.folderpath'} ne '') && (!$supplementalflag)) { + my $folderurl; + my @pathitems = split(/\&/,$env{'form.folderpath'}); + my $folder = $pathitems[-2]; + if ($folder eq '') { + undef($env{'form.folderpath'}); + } else { + $folderurl = "uploaded/$coursedom/$coursenum/$folder"; + if ((split(/\:/,$pathitems[-1]))[5]) { + $folderurl .= '.page'; + } else { + $folderurl .= '.sequence'; + } + unless (ref($navmap)) { + $navmap = Apache::lonnavmaps::navmap->new(); + } + if (ref($navmap)) { + if (lc($navmap->get_mapparam(undef,$folderurl,"0.hiddenresource")) eq 'yes') { + my @resources = $navmap->retrieveResources($folderurl,$filterFunc,1,1); + unless (@resources) { + undef($env{'form.folderpath'}); + } + } + } + } + } + + # If after all of this, we still don't have any paths, make them unless ($env{'form.folderpath'}) { if ($supplementalflag) { $env{'form.folderpath'}=&supplemental_base(); - } else { - $env{'form.folderpath'}='default&'.&escape(&mt('Main Content'). - ':::::'); + } elsif ($allowed) { + ($env{'form.folderpath'},$hiddentop) = &default_folderpath($coursenum,$coursedom,\$navmap); } } # Store this unless ($toolsflag) { - if ($allowed) { + if (($allowed) && ($env{'form.folderpath'} ne '')) { &Apache::loncommon::store_course_settings($stored_folderpath, {'folderpath' => 'scalar'}); } @@ -4654,8 +5704,12 @@ sub handler { } else { if ($env{'form.folder'} eq '' || $env{'form.folder'} eq 'supplemental') { - $folderpath='default&'. - &escape(&mt('Main Content').':::::'); + if ($env{'form.folder'} eq 'supplemental') { + $folderpath=&supplemental_base(); + } elsif (!$hiddentop) { + $folderpath='default&'. + &escape(&mt('Main Content').':::::'); + } } } $containertag = ''; @@ -4666,7 +5720,7 @@ sub handler { if ($showdoc) { # got called in sequence from course $allowed=0; } else { - if ($allowed) { + if ($canedit) { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['cmd']); $script=&Apache::lonratedt::editscript('simple'); } @@ -4695,7 +5749,7 @@ sub handler { my $tid = 1; my @tabids; if ($supplementalflag) { - @tabids = ('002','ee2','ff2'); + @tabids = ('002','dd2','ee2','ff2'); $tid = 2; } else { @tabids = ('aa1','bb1','cc1','ff1'); @@ -4705,11 +5759,15 @@ sub handler { } } my $tabidstr = join("','",@tabids); - $script .= &editing_js($udom,$uname,$supplementalflag). + %ltitools = &Apache::lonnet::get_domain_lti($coursedom,'consumer'); + my $posslti = keys(%ltitools); + my $hostname = $r->hostname(); + $script .= &editing_js($udom,$uname,$supplementalflag,$coursedom,$coursenum,$posslti, + $londocroot,$canedit,$hostname,\$navmap). &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 +5783,9 @@ sub handler { .'// '."\n" - .''."\n"; + .''."\n" + .''."\n"; # Breadcrumbs &Apache::lonhtmlcommon::clear_breadcrumbs(); @@ -4734,34 +5794,52 @@ sub handler { $r->print(&Apache::loncommon::start_page("$crstype documents",undef, {'force_register' => $showdoc,})); } elsif ($toolsflag) { + my ($breadtext,$breadtitle); + $breadtext = "$crstype Editor"; + if ($canedit) { + $breadtitle = 'Editing '.$crstype.' Contents'; + } else { + $breadtext .= ' (View-only mode)'; + $breadtitle = 'Viewing '.$crstype.' Contents'; + } &Apache::lonhtmlcommon::add_breadcrumb({ - href=>"/adm/coursedocs",text=>"$crstype Contents"}); + href=>"/adm/coursedocs",text=>$breadtext}); $r->print(&Apache::loncommon::start_page("$crstype Contents", $script) .&Apache::loncommon::help_open_menu('','',273,'RAT') .&Apache::lonhtmlcommon::breadcrumbs( - 'Editing Course Contents') + $breadtitle) ); } elsif ($r->uri eq '/adm/supplemental') { my $brcrum = &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype); $r->print(&Apache::loncommon::start_page("Supplemental $crstype Content",undef, {'bread_crumbs' => $brcrum,})); } else { + my ($breadtext,$breadtitle,$helpitem); + $breadtext = "$crstype Editor"; + if ($canedit) { + $breadtitle = 'Editing '.$crstype.' Contents'; + $helpitem = 'Docs_Adding_Course_Doc'; + } else { + $breadtext .= ' (View-only mode)'; + $breadtitle = 'Viewing '.$crstype.' Contents'; + $helpitem = 'Docs_Viewing_Course_Doc'; + } &Apache::lonhtmlcommon::add_breadcrumb({ - href=>"/adm/coursedocs",text=>"$crstype Contents"}); + href=>"/adm/coursedocs",text=>$breadtext}); $r->print(&Apache::loncommon::start_page("$crstype Contents", $script, {'add_entries' => $addentries} ) .&Apache::loncommon::help_open_menu('','',273,'RAT') .&Apache::lonhtmlcommon::breadcrumbs( - 'Editing '.$crstype.' Contents', - 'Docs_Adding_Course_Doc') + $breadtitle, + $helpitem) ); } my %allfiles = (); my %codebase = (); my ($upload_result,$upload_output,$uploadphase); - if ($allowed) { + if ($canedit) { if (($env{'form.uploaddoc.filename'}) && ($env{'form.cmd'}=~/^upload_(\w+)/)) { my $context = $1; @@ -4769,6 +5847,7 @@ sub handler { undef($hadchanges); $uploadphase = &process_file_upload(\$upload_output,$coursenum,$coursedom, \%allfiles,\%codebase,$context,$crstype); + undef($navmap); if ($hadchanges) { &mark_hash_old(); } @@ -4784,7 +5863,7 @@ sub handler { my ($destination,$dir_root) = &embedded_destination(); my $url_root = '/uploaded/'.$docudom.'/'.$docuname; my $actionurl = '/adm/coursedocs'; - my ($result,$flag) = + my ($result,$flag) = &Apache::loncommon::upload_embedded('coursedoc',$destination, $docuname,$docudom,$dir_root,$url_root,undef,undef,undef,$state, $actionurl); @@ -4795,11 +5874,11 @@ sub handler { my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'}; my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'}; my ($destination,$dir_root) = &embedded_destination(); - my $result = + my $result = &Apache::loncommon::modify_html_refs('coursedoc',$destination, $docuname,$docudom,undef, $dir_root); - $r->print($result.&return_to_editor()); + $r->print($result.&return_to_editor()); } elsif ($env{'form.phase'} eq 'decompress_uploaded') { $uploadphase = 'decompress_phase_one'; $r->print(&decompression_phase_one(). @@ -4813,7 +5892,7 @@ sub handler { if ($allowed && $toolsflag) { $r->print(&startContentScreen('tools')); - $r->print(&generate_admin_menu($crstype)); + $r->print(&generate_admin_menu($crstype,$canedit)); $r->print(&endContentScreen()); } elsif ((!$showdoc) && (!$uploadphase)) { # ----------------------------------------------------------------------------- @@ -4825,6 +5904,9 @@ sub handler { 'impo' => 'Import', 'lnks' => 'Import from Stored Links', 'impm' => 'Import from Assembled Map', + 'imcr' => 'Import from Course Resources', + 'extr' => 'External Resource', + 'extt' => 'External Tool', 'selm' => 'Select Map', 'load' => 'Load Map', 'newf' => 'New Folder', @@ -4833,7 +5915,10 @@ sub handler { 'navc' => 'Table of Contents', 'sipa' => 'Simple Course Page', 'sipr' => 'Simple Problem', - 'webp' => 'Blank Web Page (editable)', + 'webp' => 'Blank Web Page (editable)', + 'stpr' => 'Standard Problem', + 'news' => 'New sub-directory', + 'crpr' => 'Create Problem', 'drbx' => 'Drop Box', 'scuf' => 'External Scores (handgrade, upload, clicker)', 'bull' => 'Discussion Board', @@ -4847,17 +5932,64 @@ sub handler { 'se' => 'Select', 'file' => 'File', 'title' => 'Title', + 'addp' => 'Add Placeholder to course?', + 'uste' => 'Use Template?', + 'fnam' => 'File Name:', + 'loca' => 'Location:', + 'dire' => 'Directory:', + 'cate' => 'Category:', + 'tmpl' => 'Template:', 'comment' => 'Comment', 'parse' => 'Upload embedded images/multimedia files if HTML file', 'bb5' => 'Blackboard 5', 'bb6' => 'Blackboard 6', 'angel5' => 'ANGEL 5.5', 'webctce4' => 'WebCT 4 Campus Edition', + 'yes' => 'Yes', + 'no' => 'No', + 'er' => 'Editing rights unavailable for your current role.', ); # ----------------------------------------------------------------------------- + + # 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 $usage = $current_disk_usage/1024; # in MB + my $quota = $disk_quota; + my $percent; + if ($disk_quota == 0) { + $percent = 100.0; + } else { + $percent = 100*($usage/$disk_quota); + } + $usage = sprintf("%.2f",$usage); + $quota = sprintf("%.2f",$quota); + $percent = sprintf("%.0f",$percent); + my $quotainfo = '

'.&mt('Currently using [_1] of the [_2] available.', + $percent.'%',$quota.' MB').'

'; + my $fileupload=(< - + + FIUP my $checkbox=(< --> CHBO my $imsfolder = $env{'form.folder'}; @@ -4882,7 +6014,7 @@ CHBO

$lt{'cms'}:  - @@ -4892,7 +6024,7 @@ CHBO

- + IMSFORM @@ -4904,10 +6036,10 @@ IMSFORM FUFORM + my $mapimportjs; + if ($canedit) { + $mapimportjs = "javascript:openbrowser('mapimportform','importmap','sequence,page','');"; + } else { + $mapimportjs = "javascript:alert('".&js_escape($lt{'er'})."');"; + } my $importpubform=(< $lt{'impm'}$help{'Load_Map'} @@ -4930,31 +6068,73 @@ FUFORM $lt{'copm'}
-  $lt{'selm'}
- + onfocus="this.blur();$mapimportjs" $disabled /> +  $lt{'selm'}
+ SEDFFORM + my $importcrsresform; + my ($numdirs,$pickfile) = + &Apache::loncommon::import_crsauthor_form('crsresimportform','coursepath','coursefile', + "resize_scrollbox('contentscroll','1','0');", + undef,'res'); + if ($pickfile) { + $importcrsresform=(< + $lt{'imcr'}$help{'Course_Resources'} +
+ +
+CRSFORM + } + + my $fromstoredjs; + if ($canedit) { + $fromstoredjs = 'open_StoredLinks_Import()'; + } else { + $fromstoredjs = "alert('".&js_escape($lt{'er'})."')"; + } + my @importpubforma = ( { ''.$lt{srch}.'' => $pathitem."$lt{'srch'}" }, { ''.$lt{impo}.'' => "$lt{'impo'}$help{'Importing_LON-CAPA_Resource'}" }, - { ''.$lt{lnks}.'' => "$lt{'lnks'}" }, - { ''.$lt{impm}.'' => $importpubform } - ); + { ''.$lt{lnks}.'' => ''.$lt{'lnks'}.'' }, + { ''.$lt{impm}.'' => $importpubform }, + ); + if ($pickfile) { + push(@importpubforma,{ ''.$lt{imcr}.'' => $importcrsresform}); + } $importpubform = &create_form_ul(&create_list_elements(@importpubforma)); my $extresourcesform = &Apache::lonextresedit::extedit_form(0,0,undef,undef,$pathitem, - $help{'Adding_External_Resource'}); + $help{'Adding_External_Resource'}, + undef,undef,undef,undef,undef,undef,$disabled); + my $exttoolform = + &Apache::lonextresedit::extedit_form(0,0,undef,undef,$pathitem, + $help{'Adding_External_Tool'},undef, + undef,'tool',$coursedom,$coursenum, + \%ltitools,$disabled); if ($allowed) { my $folder = $env{'form.folder'}; if ($folder eq '') { $folder='default'; } - my $output = &update_paste_buffer($coursenum,$coursedom,$folder); - if ($output) { - $r->print($output); + if ($canedit) { + my $output = &update_paste_buffer($coursenum,$coursedom,$folder); + if ($output) { + $r->print($output); + } } $r->print(< @@ -4964,6 +6144,11 @@ SEDFFORM $containertag +
+ + + $containertag +
HIDDENFORM $r->print(&makesimpleeditform($pathitem)."\n". @@ -4986,12 +6171,12 @@ HIDDENFORM } # - + my $hostname = $r->hostname(); my $savefolderpath; if ($allowed) { my $folder=$env{'form.folder'}; - if ($folder eq '' || $supplementalflag) { + if ((($folder eq '') && (!$hiddentop)) || ($supplementalflag)) { $folder='default'; $savefolderpath = $env{'form.folderpath'}; $env{'form.folderpath'}='default&'.&escape(&mt('Main Content')); @@ -5000,9 +6185,13 @@ HIDDENFORM } my $postexec=''; if ($folder eq 'default') { + my $windowname = 'loncapaclient'; + if ($env{'request.lti.login'}) { + $windowname .= 'lti'; + } $r->print(''."\n" ); @@ -5015,17 +6204,17 @@ HIDDENFORM my $newnavform=(< - + $pathitem - $lt{'navc'} + $lt{'navc'} $help{'Navigate_Content'} NNFORM my $newsmppageform=(< - + $pathitem $lt{'sipa'} @@ -5035,7 +6224,7 @@ NSPFORM my $newsmpproblemform=(< - + $pathitem $lt{'sipr'} @@ -5046,7 +6235,7 @@ NSPROBFORM my $newdropboxform=(< - + $pathitem $lt{'drbx'} @@ -5056,7 +6245,7 @@ NDBFORM my $newexuploadform=(< - + $pathitem $lt{'scuf'} @@ -5066,7 +6255,7 @@ NEXUFORM my $newbulform=(< - + $pathitem $lt{'bull'} @@ -5076,18 +6265,18 @@ NBFORM my $newaboutmeform=(< - + $pathitem - $lt{'mypi'} + $lt{'mypi'} $help{'My Personal Information Page'} NAMFORM my $newaboutsomeoneform=(< - + $pathitem $lt{'abou'} @@ -5096,11 +6285,11 @@ NASOFORM my $newrosterform=(< - + $pathitem - $lt{'rost'} + $lt{'rost'} $help{'Course_Roster'} NROSTFORM @@ -5117,14 +6306,231 @@ NROSTFORM } my $newwebpageform =(< - + $pathitem $lt{'webp'} $help{'Web_Page'} NWEBFORM - + + my @ids=&Apache::lonnet::current_machine_ids(); + my %select_menus; + my $numauthor = 0; + my $numcrsdirs = 0; + my $toppath = "/priv/$env{'user.domain'}/$env{'user.name'}"; + if ($env{'user.author'}) { + $numauthor ++; + $select_menus{'author'}->{'text'} = &Apache::lonnet::plaintext('au'); + if (grep(/^\Q$env{'user.home'}\E$/,@ids)) { + my $is_home = 1; + my %subdirs; + &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs); + $select_menus{'author'}->{'default'} = '/'; + $select_menus{'author'}->{'select2'}->{'/'} = '/'; + my @ordered = ('/'); + foreach my $relpath (sort { lc($a) cmp lc($b) } (keys(%subdirs))) { + $select_menus{'author'}->{'select2'}->{$relpath} = $relpath; + push(@ordered,$relpath); + } + $select_menus{'author'}->{'order'} = \@ordered; + } else { + $select_menus{'author'}->{'select2'}->{'switch'} = &mt('Switch server required'); + $select_menus{'author'}->{'default'} = 'switch'; + $select_menus{'author'}->{'order'} = ['switch']; + } + } + my %roleshash = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},'userroles', + ['active'],['ca','aa']); + my $crshome = $env{'course.'.$env{'request.course.id'}.'.home'}; + my %by_roletype; + if (keys(%roleshash)) { + foreach my $entry (keys(%roleshash)) { + my ($auname,$audom,$roletype) = split(/:/,$entry); + my $key = $entry; + $key =~ s/:/___/g; + $by_roletype{$roletype}{$auname.'___'.$audom} = 1; + $select_menus{$key}->{'text'} = &Apache::lonnet::plaintext($roletype)." ($audom/$auname)"; + my $rolehome = &Apache::lonnet::homeserver($auname,$audom); + if (grep(/^\Q$rolehome\E$/,@ids)) { + my $is_home = 1; + my (%subdirs,@ordered); + my $toppath="/priv/$audom/$auname"; + &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs); + $select_menus{$key}->{'default'} = '/'; + $select_menus{$key}->{'select2'}->{'/'} = '/'; + my @ordered = ('/'); + foreach my $relpath (sort { lc($a) cmp lc($b) } (keys(%subdirs))) { + $select_menus{$key}->{'select2'}->{$relpath} = $relpath; + push(@ordered,$relpath); + } + $select_menus{$key}->{'order'} = \@ordered; + } else { + $select_menus{$key}->{'select2'}->{'switch'} = &mt('Switch server required'); + $select_menus{$key}->{'default'} = 'switch'; + $select_menus{$key}->{'order'} = ['switch']; + } + $numauthor ++; + } + } + my ($pickdir,$showtitle); + if ($numauthor) { + my @order; + my $defrole; + if ($env{'user.author'}) { + push(@order,'author'); + $defrole = 'author'; + } + if (keys(%by_roletype)) { + foreach my $possrole ('ca','aa') { + if (ref($by_roletype{$possrole}) eq 'HASH') { + foreach my $author (sort { lc($a) cmp lc($b) } (keys(%{$by_roletype{$possrole}}))) { + unless ($defrole) { + $defrole = $author; + } + push(@order,$author.'___'.$possrole); + } + } + } + } + $select_menus{'course'}->{'text'} = &mt('Course Resource'); + if (grep(/^\Q$crshome\E$/,@ids)) { + my $is_home = 1; + my %subdirs; + my $toppath="/priv/$coursedom/$coursenum"; + &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs); + $numcrsdirs = keys(%subdirs); + $select_menus{'course'}->{'default'} = '/'; + $select_menus{'course'}->{'select2'}->{'/'} = '/'; + my @ordered = ('/'); + foreach my $relpath (sort { lc($a) cmp lc($b) } (keys(%subdirs))) { + $select_menus{'course'}->{'select2'}->{$relpath} = $relpath; + push(@ordered,$relpath); + } + $select_menus{'course'}->{'order'} = \@ordered; + } else { + $select_menus{'course'}->{'select2'}->{'switch'} = &mt('Switch server required'); + $select_menus{'course'}->{'default'} = 'switch'; + $select_menus{'course'}->{'order'} = ['switch']; + } + push(@order,'course'); + $pickdir = $lt{'loca'}. + &Apache::loncommon::linked_select_forms('courseresform','
'.$lt{'dire'}, + $defrole,'authorrole','authorpath', + \%select_menus,\@order,'toggleCrsResTitle();', + '','priv').'
'; + $showtitle = 'none'; + } else { + my $is_home; + $showtitle = 'inline'; + if (grep(/^\Q$crshome\E$/,@ids)) { + $is_home = 1; + $pickdir .= ''; + my $toppath="/priv/$coursedom/$coursenum'}"; + my %subdirs; + &Apache::lonnet::recursedirs($is_home,'priv',$londocroot,$toppath,'',\%subdirs); + $numcrsdirs = keys(%subdirs); + if ($numcrsdirs) { + $pickdir .= &mt('Directory: ').''; + } else { + $pickdir .= ''."\n"; + } + } + } + + my %seltemplate_menus; + my @files = &Apache::lonhomework::get_template_list('problem'); + my @noexamplelink = ('blank.problem','blank.library','script.library'); + my $currentcategory = ''; + my @ordered = (''); + my %templatehelp; + my $defcategory = ''; + my @catorder = ($defcategory); + $seltemplate_menus{$defcategory}->{'order'} = ['']; + $seltemplate_menus{$defcategory}->{'text'} = ''; + foreach my $file (@files) { + if (ref($file) eq 'ARRAY') { + my ($path,$title,$category,$help) = @{$file}; + next if ($title !~ /\S/); + if (&js_escape($category) ne $currentcategory) { + $currentcategory = &js_escape($category); + push(@catorder,&js_escape($currentcategory)); + $seltemplate_menus{$currentcategory}->{'text'} = $category; + $seltemplate_menus{$currentcategory}->{'default'} = ''; + $seltemplate_menus{$currentcategory}->{'select2'}->{''} = ''; + push(@{$seltemplate_menus{$currentcategory}->{'order'}},''); + } + if ($path) { + $seltemplate_menus{$currentcategory}->{'select2'}->{&js_escape($path)} = $title; + push(@{$seltemplate_menus{$currentcategory}->{'order'}},&js_escape($path)); + if ($help) { + $templatehelp{$path} = $help; + } + } + } + } + + my $templates = $lt{'cate'}.' '. + &Apache::loncommon::linked_select_forms('courseresform','
'.$lt{'tmpl'}.' ', + $defcategory,'tempcategory','template', + \%seltemplate_menus,\@catorder, + "resize_scrollbox('contentscroll','1','0');", + "toggleExampleText();",'template').'
'; + my $templatepreview = ''. + ''.&mt('Example').''; + my $crsresform=(< + $lt{'stpr'}$help{'Course_Resource'} +
+ +
+ +RESFORM my $specialdocumentsform; my @specialdocumentsforma; @@ -5163,7 +6569,7 @@ NFFORM $pathitem - $lt{'syll'} + $lt{'syll'} $help{'Syllabus'} @@ -5171,27 +6577,40 @@ NSYLFORM my $newgroupfileform=(< - + $pathitem - $lt{'grpo'} + $lt{'grpo'} $help{'Group Portfolio'} NGFFORM - @specialdocumentsforma=( + if ($container eq 'page') { + @specialdocumentsforma=( + {''.$lt{webp}.''=>$newwebpageform}, + ); + } else { + @specialdocumentsforma=( {''.$lt{newp}.''=>$newpageform}, - {''.$lt{syll}.''=>$newsylform}, - {''.$lt{navc}.''=>$newnavform}, + {''.$lt{syll}.''=>$newsylform}, + {''.$lt{navc}.''=>$newnavform}, {''.$lt{sipa}.''=>$newsmppageform}, {''.$lt{webp}.''=>$newwebpageform}, - ); + ); + } $specialdocumentsform = &create_form_ul(&create_list_elements(@specialdocumentsforma)); - - my @importdoc = ( - {''.$lt{extr}.''=>$extresourcesform} + my @external = ( + {''.$lt{extr}.''=>$extresourcesform} ); + if (keys(%ltitools)) { + push(@external, + {''.$lt{extt}.''=>$exttoolform}, + ); + } + my $externalform = &create_form_ul(&create_list_elements(@external)); + + my @importdoc = (); unless ($container eq 'page') { push(@importdoc, {''.$lt{imsf}.''=>$imspform} @@ -5206,7 +6625,7 @@ NGFFORM {''.$lt{sipr}.''=>$newsmpproblemform}, {''.$lt{drbx}.''=>$newdropboxform}, {''.$lt{scuf}.''=>$newexuploadform}, - + {''.$lt{stpr}.''=>$crsresform}, ); $gradingform = &create_form_ul(&create_list_elements(@gradingforma)); @@ -5214,32 +6633,37 @@ NGFFORM {''.$lt{bull}.''=>$newbulform}, {''.$lt{mypi}.''=>$newaboutmeform}, {''.$lt{abou}.''=>$newaboutsomeoneform}, - {''.$lt{rost}.''=>$newrosterform}, - {''.$lt{grpo}.''=>$newgroupfileform}, + {''.$lt{rost}.''=>$newrosterform}, + {''.$lt{grpo}.''=>$newgroupfileform}, ); $communityform = &create_form_ul(&create_list_elements(@communityforma)); my %orderhash = ( 'aa' => ['Upload',$fileuploadform], 'bb' => ['Import',$importpubform], - 'cc' => ['Grading',$gradingform], + 'cc' => ['External',$externalform], + 'dd' => ['Grading',$gradingform], + 'ff' => ['Other',$specialdocumentsform], ); unless ($container eq 'page') { $orderhash{'00'} = ['Newfolder',$newfolderform]; - $orderhash{'dd'} = ['Collaboration',$communityform]; - $orderhash{'ee'} = ['Other',$specialdocumentsform]; + $orderhash{'ee'} = ['Collaboration',$communityform]; } $hadchanges=0; unless (($supplementalflag || $toolsflag)) { my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, - $supplementalflag,\%orderhash,$iconpath,$pathitem); + $supplementalflag,\%orderhash,$iconpath,$pathitem, + \%ltitools,$canedit,$hostname,\$navmap,$hiddentop); + undef($navmap); if ($error) { $r->print('

'.$error.'

'); } if ($hadchanges) { - &mark_hash_old(); - } + unless (&is_hash_old()) { + &mark_hash_old(); + } + } &changewarning($r,''); } @@ -5269,7 +6693,7 @@ unless ($container eq 'page') {