--- loncom/interface/londocs.pm 2013/08/13 15:38:35 1.558 +++ loncom/interface/londocs.pm 2025/01/07 21:01:37 1.722 @@ -1,7 +1,7 @@ # The LearningOnline Network # Documents # -# $Id: londocs.pm,v 1.558 2013/08/13 15:38:35 raeburn Exp $ +# $Id: londocs.pm,v 1.722 2025/01/07 21:01:37 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -41,11 +41,20 @@ use Apache::lonclonecourse; use Apache::lonnavmaps; use Apache::lonnavdisplay(); use Apache::lonextresedit(); +use Apache::lontemplate(); +use Apache::lonsimplepage(); +use Apache::lonhomework(); +use Apache::lonpublisher(); +use Apache::loncourserespicker(); use HTML::Entities; use HTML::TokeParser; +use HTML::LCParser; 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; @@ -82,7 +91,7 @@ sub storemap { if ($map =~ /^default/) { $hadchanges=1; - } else { + } elsif ($contentchg) { $suppchanges=1; } return ($errtext,0); @@ -94,6 +103,7 @@ sub authorhosts { my %outhash=(); my $home=0; my $other=0; + my @ids=&Apache::lonnet::current_machine_ids(); foreach my $key (keys(%env)) { if ($key=~/^user\.role\.(au|ca)\.(.+)$/) { my $role=$1; @@ -110,7 +120,6 @@ 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; @@ -136,16 +145,131 @@ 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 validate_supppath { + my ($coursenum,$coursedom) = @_; + my $backto; + if ($env{'form.supppath'} ne '') { + my @items = split(/\&/,$env{'form.supppath'}); + my ($badpath,$got_supp,$supppath,%supphidden,%suppids); + for (my $i=0; $i<@items; $i++) { + my $odd = $i%2; + if ((!$odd) && ($items[$i] !~ /^supplemental(|_\d+)$/)) { + $badpath = 1; + last; + } elsif ($odd) { + my $suffix; + my $idx = $i-1; + if ($items[$i] =~ /^([^:]*)::(|1):::$/) { + $backto .= '&'.$1; + } elsif ($items[$idx] eq 'supplemental') { + $backto .= '&'.$items[$i]; + } else { + $backto .= '&'.$items[$i]; + my $is_hidden; + unless ($got_supp) { + my ($supplemental) = &Apache::loncommon::get_supplemental($coursenum,$coursedom); + if (ref($supplemental) eq 'HASH') { + if (ref($supplemental->{'hidden'}) eq 'HASH') { + %supphidden = %{$supplemental->{'hidden'}}; + } + if (ref($supplemental->{'ids'}) eq 'HASH') { + %suppids = %{$supplemental->{'ids'}}; + } + } + $got_supp = 1; + } + if (ref($suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}) eq 'ARRAY') { + my $mapid = $suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}->[0]; + if ($supphidden{$mapid}) { + $is_hidden = 1; + } + } + $suffix = '::'.$is_hidden.':::'; + } + $supppath .= '&'.$items[$i].$suffix; + } else { + $supppath .= '&'.$items[$i]; + $backto .= '&'.$items[$i]; + } + } + if ($badpath) { + delete($env{'form.supppath'}); + } else { + $supppath =~ s/^\&//; + $backto =~ s/^\&//; + $env{'form.supppath'} = $supppath; + } + } + return $backto; +} sub dumpcourse { my ($r) = @_; my $crstype = &Apache::loncommon::course_type(); - $r->print(&Apache::loncommon::start_page('Dump '.$crstype.' Content to Authoring Space')."\n". - &Apache::lonhtmlcommon::breadcrumbs('Dump '.$crstype.' Content to Authoring Space')."\n"); + my ($starthash,$js); + unless (($env{'form.authorspace'}) && ($env{'form.authorfolder'}=~/\w/)) { + $js = <<"ENDJS"; + +ENDJS + $starthash = { + add_entries => {'onload' => "hide_searching();"}, + }; + } + $r->print(&Apache::loncommon::start_page('Copy uploaded content to Authoring Space',$js,$starthash)."\n". + &Apache::lonhtmlcommon::breadcrumbs('Copy uploaded content to Authoring Space')."\n"); $r->print(&startContentScreen('tools')); my ($home,$other,%outhash)=&authorhosts(); unless ($home) { + $r->print('

'.&mt('No author or co-author roles on this server.').'

'); $r->print(&endContentScreen()); return ''; } @@ -154,127 +278,1399 @@ sub dumpcourse { if (($env{'form.authorspace'}) && ($env{'form.authorfolder'}=~/\w/)) { # Do the dumping unless ($outhash{'home_'.$env{'form.authorspace'}}) { - $r->print(&endContentScreen()); + $r->print('

'.&mt('Selected Authoring Space is not on this server.').'

'. + &endContentScreen()); return ''; } my ($ca,$cd)=split(/\:/,$env{'form.authorspace'}); $r->print('

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

'); my $title=$env{'form.authorfolder'}; $title=&clean($title); - my %replacehash=(); - foreach my $key (keys(%env)) { - if ($key=~/^form\.namefor\_(.+)/) { - $replacehash{$1}=$env{$key}; - } + my ($navmap,$errormsg) = + &Apache::loncourserespicker::get_navmap_object($crstype,'dumpdocs'); + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my (%maps,%resources,%titles); + if (!ref($navmap)) { + $r->print($errormsg. + &endContentScreen()); + return ''; + } else { + &Apache::loncourserespicker::enumerate_course_contents($navmap,\%maps,\%resources,\%titles, + 'dumpdocs',$cdom,$cnum); } + my @todump = &Apache::loncommon::get_env_multiple('form.archive'); + my (%tocopy,%replacehash,%lookup,%deps,%display,%result,%depresult,%simpleproblems,%simplepages, + %newcontent,%has_simpleprobs); + foreach my $item (sort {$a <=> $b} (@todump)) { + my $name = $env{'form.namefor_'.$item}; + if ($resources{$item}) { + my ($map,$id,$res) = &Apache::lonnet::decode_symb($resources{$item}); + if ($res =~ m{^uploaded/$cdom/$cnum/\E((?:docs|supplemental)/.+)$}) { + $tocopy{$1} = $name; + $display{$item} = $1; + $lookup{$1} = $item; + } elsif ($res eq 'lib/templates/simpleproblem.problem') { + $simpleproblems{$item} = { + symb => $resources{$item}, + name => $name, + }; + $display{$item} = 'simpleproblem_'.$name; + if ($map =~ m{^\Quploaded/$cdom/$cnum/\E(.+)$}) { + $has_simpleprobs{$1}{$id} = $item; + } + } elsif ($res =~ m{^adm/$match_domain/$match_username/(\d+)/smppg}) { + my $marker = $1; + my $db_name = &Apache::lonsimplepage::get_db_name($res,$marker,$cdom,$cnum); + $simplepages{$item} = { + res => $res, + title => $titles{$item}, + db => $db_name, + marker => $marker, + symb => $resources{$item}, + name => $name, + }; + $display{$item} = '/'.$res; + } + } elsif ($maps{$item}) { + if ($maps{$item} =~ m{^\Quploaded/$cdom/$cnum/\E((?:default|supplemental)_\d+\.(?:sequence|page))$}) { + $tocopy{$1} = $name; + $display{$item} = $1; + $lookup{$1} = $item; + } + } else { + next; + } + } my $crs='/uploaded/'.$env{'request.course.id'}.'/'; $crs=~s/\_/\//g; - foreach my $item (keys(%replacehash)) { - my $newfilename=$title.'/'.$replacehash{$item}; - $newfilename=~s/\.(\w+)$//; - my $ext=$1; - $newfilename=&clean($newfilename); - $newfilename.='.'.$ext; - my @dirs=split(/\//,$newfilename); - my $path=$r->dir_config('lonDocRoot')."/priv/$cd/$ca"; - my $makepath=$path; - my $fail=0; - for (my $i=0;$i<$#dirs;$i++) { - $makepath.='/'.$dirs[$i]; - unless (-e $makepath) { - unless(mkdir($makepath,0777)) { $fail=1; } - } - } - $r->print('
'.$item.' => '.$newfilename.': '); - if (my $fh=Apache::File->new('>'.$path.'/'.$newfilename)) { - if ($item=~/\.(sequence|page|html|htm|xml|xhtml)$/) { - print $fh &Apache::lonclonecourse::rewritefile( - &Apache::lonclonecourse::readfile($env{'request.course.id'},$item), - (%replacehash,$crs => '') - ); - } else { - print $fh - &Apache::lonclonecourse::readfile($env{'request.course.id'},$item); - } - $fh->close(); - } else { - $fail=1; - } - if ($fail) { - $r->print(''.&mt('fail').''); - } else { - $r->print(''.&mt('ok').''); - } - } + my $mm = new File::MMagic; + my $prefix = "/uploaded/$cdom/$cnum/"; + %replacehash = %tocopy; + foreach my $item (sort(keys(%simpleproblems))) { + my $content = &Apache::imsexport::simpleproblem($simpleproblems{$item}{'symb'}); + $newcontent{$display{$item}} = $content; + } + my $gateway = Apache::lonhtmlgateway->new('web'); + foreach my $item (sort(keys(%simplepages))) { + if (ref($simplepages{$item}) eq 'HASH') { + my $pagetitle = $simplepages{$item}{'title'}; + my %fields = &Apache::lonnet::dump($simplepages{$item}{'db'},$cdom,$cnum); + my %contents; + foreach my $field (keys(%fields)) { + if ($field =~ /^(?:aaa|bbb|ccc)_(\w+)$/) { + my $name = $1; + my $msg = $fields{$field}; + if ($name eq 'webreferences') { + if ($msg =~ m{^https?://}) { + $contents{$name} = ''.$msg.''; + } + } else { + $msg = &Encode::decode('utf8',$msg); + $msg = $gateway->process_outgoing_html($msg,1); + $contents{$name} = $msg; + } + } elsif ($field eq 'uploaded.photourl') { + my $marker = $simplepages{$item}{marker}; + if ($fields{$field} =~ m{^\Q$prefix\E(simplepage/$marker/.+)$}) { + my $filepath = $1; + my ($relpath,$fname) = ($filepath =~ m{^(.+/)([^/]+)$}); + if ($fname ne '') { + $fname=~s/\.(\w+)$//; + my $ext=$1; + $fname = &clean($fname); + $fname.='.'.$ext; + $contents{image} = 'Image'; + $replacehash{$filepath} = $relpath.$fname; + $deps{$item}{$filepath} = 1; + } + } + } + } + $replacehash{'/'.$simplepages{$item}{'res'}} = $simplepages{$item}{'name'}; + $lookup{'/'.$simplepages{$item}{'res'}} = $item; + my $content = ' + + +'.$pagetitle.' + +'; + if ($contents{title}) { + $content .= "\n".'

'.$contents{title}.'

'; + } + if ($contents{image}) { + $content .= "\n".$contents{image}; + } + if ($contents{content}) { + $content .= ' +
+

'.&mt('Content').'

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

'.&mt('Web References').'

'. +$contents{webreferences}.' +
'; + } + $content .= ' + + +'; + $newcontent{'/'.$simplepages{$item}{res}} = $content; + } + } + foreach my $item (keys(%tocopy)) { + unless ($item=~/\.(sequence|page)$/) { + my $currurlpath = $prefix.$item; + my $currdirpath = &Apache::lonnet::filelocation('',$currurlpath); + &recurse_html($mm,$prefix,$currdirpath,$currurlpath,$item,$lookup{$item},\%replacehash,\%deps); + } + } + foreach my $num (sort {$a <=> $b} (@todump)) { + my $src = $display{$num}; + next if ($src eq ''); + my @needcopy = (); + if ($replacehash{$src}) { + push(@needcopy,$src); + if (ref($deps{$num}) eq 'HASH') { + foreach my $dep (sort(keys(%{$deps{$num}}))) { + if ($replacehash{$dep}) { + push(@needcopy,$dep); + } + } + } + } elsif ($src =~ /^simpleproblem_/) { + push(@needcopy,$src); + } + next if (@needcopy == 0); + my ($result,$depresult); + for (my $i=0; $i<@needcopy; $i++) { + my $item = $needcopy[$i]; + my $newfilename; + if ($simpleproblems{$num}) { + $newfilename=$title.'/'.$simpleproblems{$num}{'name'}; + } else { + $newfilename=$title.'/'.$replacehash{$item}; + } + $newfilename=~s/\.(\w+)$//; + my $ext=$1; + $newfilename=&clean($newfilename); + $newfilename.='.'.$ext; + my ($newrelpath) = ($newfilename =~ m{^\Q$title/\E(.+)$}); + if ($newrelpath ne $replacehash{$item}) { + $replacehash{$item} = $newrelpath; + } + my @dirs=split(/\//,$newfilename); + my $path=$r->dir_config('lonDocRoot')."/priv/$cd/$ca"; + my $makepath=$path; + my $fail; + my $origin; + for (my $i=0;$i<$#dirs;$i++) { + $makepath.='/'.$dirs[$i]; + unless (-e $makepath) { + unless(mkdir($makepath,0755)) { + $fail = &mt('Directory creation failed.'); + } + } + } + if ($i == 0) { + $result = '
'.$item.' => '.$newfilename.': '; + } else { + $depresult .= '
  • '.$item.' => '.$newfilename.' '. + ''. + &mt('(dependency)').': '; + } + if (-e $path.'/'.$newfilename) { + $fail = &mt('Destination already exists -- not overwriting.'); + } else { + if (my $fh=Apache::File->new('>'.$path.'/'.$newfilename)) { + if (($item =~ m{^/adm/$match_domain/$match_username/\d+/smppg}) || + ($item =~ /^simpleproblem_/)) { + print $fh $newcontent{$item}; + } else { + my $fileloc = &Apache::lonnet::filelocation('',$prefix.$item); + if (-e $fileloc) { + if ($item=~/\.(sequence|page|html|htm|xml|xhtml)$/) { + if ((($1 eq 'sequence') || ($1 eq 'page')) && + (ref($has_simpleprobs{$item}) eq 'HASH')) { + my %changes = %{$has_simpleprobs{$item}}; + my $content = &Apache::lonclonecourse::rewritefile( + &Apache::lonclonecourse::readfile($env{'request.course.id'},$item), + (%replacehash,$crs => '') + ); + my $updatedcontent = ''; + my $parser = HTML::TokeParser->new(\$content); + $parser->attr_encoded(1); + while (my $token = $parser->get_token) { + if ($token->[0] eq 'S') { + if (($token->[1] eq 'resource') && + ($token->[2]->{'src'} eq '/res/lib/templates/simpleproblem.problem') && + ($changes{$token->[2]->{'id'}})) { + my $id = $token->[2]->{'id'}; + $updatedcontent .= '<'.$token->[1]; + foreach my $attrib (@{$token->[3]}) { + next unless ($attrib =~ /^(src|type|title|id)$/); + if ($attrib eq 'src') { + my ($file) = ($display{$changes{$id}} =~ /^\Qsimpleproblem_\E(.+)$/); + if ($file) { + $updatedcontent .= ' '.$attrib.'="'.$file.'"'; + } else { + $updatedcontent .= ' '.$attrib.'="'.$token->[2]->{$attrib}.'"'; + } + } else { + $updatedcontent .= ' '.$attrib.'="'.$token->[2]->{$attrib}.'"'; + } + } + $updatedcontent .= ' />'."\n"; + } else { + $updatedcontent .= $token->[4]."\n"; + } + } else { + $updatedcontent .= $token->[2]; + } + } + print $fh $updatedcontent; + } else { + print $fh &Apache::lonclonecourse::rewritefile( + &Apache::lonclonecourse::readfile($env{'request.course.id'},$item), + (%replacehash,$crs => '') + ); + } + } else { + print $fh + &Apache::lonclonecourse::readfile($env{'request.course.id'},$item); + } + } else { + $fail = &mt('Source does not exist.'); + } + } + $fh->close(); + } else { + $fail = &mt('Could not write to destination.'); + } + } + my $text; + if ($fail) { + $text = ''.&mt('fail').(' 'x3).$fail.''; + } else { + $text = ''.&mt('ok').''; + } + if ($i == 0) { + $result .= $text; + } else { + $depresult .= $text.'
  • '; + } + } + $r->print($result); + if ($depresult) { + $r->print(''); + } + } } else { - $r->print(&mt('Searching ...').'
    '); - $r->rflush(); -# Input form - $r->print('
    '."\n"); - unless ($home==1) { - $r->print('
    '. - '
    '. - &mt('Select the Authoring Space'). - ''); - } else { - $r->print(''); - } - } - } - unless ($home==1) { - $r->print('
    '."\n"); - } - my $title=$origcrsdata{'description'}; - $title=~s/[\/\s]+/\_/gs; - $title=&clean($title); - $r->print('
    '. - '
    '.&mt('Folder in Authoring Space').''. - ''. - '

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

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

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

    '); + my $formname = 'dumpdoc'; + my $preamble = &authorspace_selector($r,$formname,$home,$title,%outhash). + '
    '."\n"; + my %uploadedfiles; + &tiehash(); + foreach my $file (&Apache::lonclonecourse::crsdirlist($origcrsid,'userfiles')) { + my ($ext)=($file=~/\.(\w+)$/); +# FIXME Check supplemental here + my $title=$hash{'title_'.$hash{ + 'ids_/uploaded/'.$origcrsdata{'domain'}.'/'.$origcrsdata{'num'}.'/'.$file}}; + if (!$title) { + $title=$file; + } else { + $title=~s|/|_|g; + } + $title=~s/\.(\w+)$//; + $title=&clean($title); + $title.='.'.$ext; +# $r->print("\n" + $uploadedfiles{$file} = $title; + } + &untiehash(); + $r->print(&Apache::loncourserespicker::create_picker($navmap,'dumpdocs',$formname,$crstype,undef, + undef,undef,$preamble,$home,\%uploadedfiles)); + } } $r->print(&endContentScreen()); } +sub authorspace_selector { + my ($r,$formname,$home,$title,%outhash) = @_; + $r->print('
    '.&mt('Searching ...').'
    '."\n"); + $r->rflush(); + my $preamble; + unless ($home==1) { + $preamble = '
    '. + '
    '. + &mt('Select the Authoring Space'). + ''; + } else { + $preamble .= ''; + } + } + unless ($home==1) { + $preamble .= '
    '."\n"; + } + $preamble .= '
    '. + '
    '.&mt('Folder in Authoring Space').''. + ''."\n". + '
    '."\n"; + return $preamble; +} + +sub recurse_html { + my ($mm,$prefix,$currdirpath,$currurlpath,$container,$item,$replacehash,$deps) = @_; + return unless ((ref($replacehash) eq 'HASH') && (ref($deps) eq 'HASH')); + my (%allfiles,%codebase); + if (&Apache::lonnet::extract_embedded_items($currdirpath,\%allfiles,\%codebase) eq 'ok') { + if (keys(%allfiles)) { + foreach my $dependency (keys(%allfiles)) { + next if (($dependency =~ m{^/(res|adm)/}) || ($dependency =~ m{^https?://})); + my ($depurl,$relfile,$newcontainer); + if ($dependency =~ m{^/}) { + if ($dependency =~ m{^\Q$currurlpath/\E(.+)$}) { + $relfile = $1; + if ($dependency =~ m{^\Q$prefix\E(.+)$}) { + $newcontainer = $1; + next if ($replacehash->{$newcontainer}); + } + $depurl = $dependency; + } else { + next; + } + } else { + $relfile = $dependency; + $depurl = $currurlpath; + $depurl =~ s{[^/]+$}{}; + $depurl .= $dependency; + ($newcontainer) = ($depurl =~ m{^\Q$prefix\E(.+)$}); + } + next if ($relfile eq ''); + my $newname = $replacehash->{$container}; + $newname =~ s{[^/]+$}{}; + $replacehash->{$newcontainer} = $newname.$relfile; + $deps->{$item}{$newcontainer} = 1; + my ($newurlpath) = ($depurl =~ m{^(.*)/[^/]+$}); + my $depfile = &Apache::lonnet::filelocation('',$depurl); + my $type = $mm->checktype_filename($depfile); + if ($type eq 'text/html') { + &recurse_html($mm,$prefix,$depfile,$newurlpath,$newcontainer,$item,$replacehash,$deps); + } + } + } + } + return; +} + +sub copycrsauthored { + my ($r,$coursenum,$coursedom,$coursehome,$readonly) = @_; + my ($starthash,$js,$title,$formname); + my %origcrsdata=&Apache::lonnet::coursedescription($env{'request.course.id'}); + $title=$origcrsdata{'description'}; + $title=~s/[\/\s]+/\_/gs; + $title=&clean($title); + my ($home,$other,%outhash)=&authorhosts(); + unless (($env{'form.authorspace'}) && ($env{'form.authorfolder'}=~/\w/)) { + my %js_lt; + $formname = 'copycrsauthored'; + if ($home) { + %js_lt = + &Apache::lonlocal::texthash( + yomu => 'You must select an Authoring Space', + whco => 'When Copyright set to "custom", URL of a published rights file is needed.', + ); + &js_escape(\%js_lt); + } + if ($home > 1) { + $js = <<"ENDJS"; + + +ENDJS + } elsif ($home) { + $js = <<"ENDJS"; + + +ENDJS + } + $js .= <<"ENDJS"; + +ENDJS + + $js .= "\n".&Apache::lonhtmlcommon::scripttag(&Apache::loncommon::browser_and_searcher_javascript())."\n"; + $starthash = { + add_entries => {'onload' => "hide_searching(); init_copycrs_form();"}, + }; + } + $r->print(&Apache::loncommon::start_page('Copy from Course Authoring to User Authoring',$js,$starthash)."\n". + &Apache::lonhtmlcommon::breadcrumbs('Copy from Course Authoring Space')."\n"); + $r->print(&startContentScreen('tools')); + unless ($home) { + $r->print('

    '.&mt('No author or co-author roles on this server.').'

    '); + $r->print(&endContentScreen()); + return ''; + } + my $docroot = $r->dir_config('lonDocRoot'); + my $is_course_home; + my @ids=&Apache::lonnet::current_machine_ids(); + if (($coursehome ne '') && (grep(/^\Q$coursehome\E$/,@ids))) { + $is_course_home = 1; + } + my $exclude = &Apache::lonnet::priv_exclude(); + my $srcurl = "/priv/$coursedom/$coursenum"; + my $srctop = $docroot.$srcurl; + my $resurl = "/res/$coursedom/$coursenum"; + my $res_exclude = &Apache::lonnet::res_exclude(); + if (($env{'form.authorspace'}) && ($env{'form.authorfolder'}=~/\w/)) { + $r->print('

    '.&mt('Copying Files and/or Sub-directories').'

    '); + if ($readonly) { + $r->print('

    '. + &mt('You do not have permission to copy files and/or directories from Course Authoring Space.'). + '

    '. + &endContentScreen()); + return ''; + } + unless ($outhash{'home_'.$env{'form.authorspace'}}) { + $r->print('

    '.&mt('Selected Authoring Space is not on this server.').'

    '. + &endContentScreen()); + return ''; + } + my ($ca,$cd)=split(/\:/,$env{'form.authorspace'}); + my $desturl = "/priv/$cd/$ca"; + my $destresurl = "/res/$cd/$ca"; + my $desttop = $docroot.$desturl; + my $subdir = &clean($env{'form.authorfolder'}); + $subdir = &cleandir($subdir); + if ($subdir eq '') { + $r->print('

    '.&mt('After removal of disallowed characters target sub-directory name was blank.').'

    '. + &endContentScreen()); + return ''; + } elsif ($subdir =~/^_+$/) { + $r->print('

    '.&mt('After replacement of non-alphanumeric characters with _ in target sub-directory name, nothing but underscores was left.').'

    '. + &endContentScreen()); + return ''; + } + my (%tocopy,%dirs_to_make,%files_to_copy); + map { $tocopy{&unescape($_)} = 1; } &Apache::loncommon::get_env_multiple('form.copytouser'); + if (keys(%tocopy)) { + my (%subdirs,%files); + &Apache::lonnet::recursedirs($is_course_home,1,undef,$exclude,0,0,$srcurl,'',\%subdirs,\%files); + foreach my $possible (sort(keys(%tocopy))) { + if ($possible =~ m{/$}) { + my $possdir = $possible; + $possdir =~ s{^/+|/+$}{}g; + if (exists($subdirs{$possdir})) { + $dirs_to_make{$possdir} = 1; + } else { + delete($tocopy{$possible}); + } + } else { + my ($path,$fname) = ($possible =~ m{(.*/)([^/]+)$}); + my $found = 0; + if ($path eq '/') { + if (ref($files{$path}) eq 'HASH') { + if (exists($files{$path}{$fname})) { + $found = 1; + $files_to_copy{$fname} = 1; + } + } + } else { + $path =~ s{^/+|/+$}{}g; + if (ref($files{$path}) eq 'HASH') { + if (exists($files{$path}{$fname})) { + $dirs_to_make{$path} = 1; + $files_to_copy{"$path/$fname"} = 1; + $found = 1; + } + } + } + unless ($found) { + delete($tocopy{$possible}); + } + } + } + } else { + $r->print('

    '.&mt('No files or directories selected for copying').'

    '); + $r->print(&endContentScreen()); + return ''; + } + if (keys(%tocopy)) { + my (%resdirs,%resfiles); + &Apache::lonnet::recursedirs($is_course_home,1,undef,$res_exclude,0,0,$resurl,'',\%resdirs,\%resfiles); + my ($notopdir,%newdir,%newfile,%checkdeps,%newresfile); + $r->print('

    '.&mt('Copy to: [_1]', + ''.$desturl.'/'.$subdir.''). + '

    '."\n"); + if (keys(%dirs_to_make)) { + unless (-e $desttop.'/'.$subdir) { + mkdir($desttop.'/'.$subdir,0755); + } + if (-e $desttop.'/'.$subdir) { + foreach my $dir (sort(keys(%dirs_to_make))) { + my @dirs=split(/\//,$dir); + my $path="$desttop/$subdir"; + my $makepath=$path; + my $fail; + for (my $i=0;$i<@dirs;$i++) { + $makepath.='/'.$dirs[$i]; + unless (-e $makepath) { + unless (mkdir($makepath,0755)) { + $fail = 1; + last; + } + if (($i == scalar(@dirs)-1) && (!$fail)) { + $newdir{$dir} = 1; + } + } + } + if ($fail) { + $r->print('

    '.&mt('Target directory: [_1] does not exist, and could not be created.', + ''.$desturl.'/'.$subdir.'/'.$dir.''). + '

    '."\n"); + } + } + } else { + $notopdir = 1; + } + } + if (keys(%files_to_copy)) { + unless (-e $desttop.'/'.$subdir) { + mkdir($desttop.'/'.$subdir,0755); + } + if (-e $desttop.'/'.$subdir) { + my $num = 0; + my ($copyright,$customdistfile); + if ($env{'form.copyright'} eq 'default' || $env{'form.copyright'} eq 'domain' || $env{'form.copyright'} eq 'public') { + $copyright = $env{'form.copyright'}; + } elsif ($env{'form.copyright'} eq 'custom') { + if ($env{'form.customrights'} =~ m{^/res/$match_domain/$match_username/.+\.rights$}) { + my ($rightsdom,$rightsuname) = ($1,$2); + my $rightshome = &Apache::lonnet::homeserver($rightsdom,$rightsuname); + if (($rightshome eq 'no_host') || ($rightshome eq '')) { + $copyright = 'default'; + } elsif (grep(/^\Q$rightshome\E$/,@ids)) { + if (-e $docroot.$env{'form.customrights'}) { + $copyright = 'custom'; + $customdistfile = $env{'form.customrights'}; + } else { + $copyright = 'default'; + } + } else { + my $rightsfile = &Apache::lonnet::filelocation('',$env{'form.customrights'}); + unless (&Apache::lonnet::getfile($rightsfile) eq '-1') { + $customdistfile = $env{'form.customrights'}; + } + } + } + } + my $sourceavail; + if ($env{'form.sourceavail'} =~ /^(open|closed)$/) { + $sourceavail = $env{'form.sourceavail'}; + } + my $respublish; + if ($env{'form.respublish'}) { + $respublish = 1; + } + my $nokeyref = &Apache::lonpublisher::getnokey($r->dir_config('lonIncludes')); + foreach my $file (keys(%files_to_copy)) { + my ($fail,$dup,$dir_is_file,$src,$dest,$path,$fname); + if ($file =~ m{/}) { + ($path,$fname) = ($file =~ m{^(.+)/([^/]+)$}); + if (-d "$desttop/$subdir/$path") { + if (-e "$desttop/$subdir/$path/$fname") { + $dup = 1; + } else { + $src = "$srctop/$path/$fname"; + $dest = "$desttop/$subdir/$path/$fname"; + } + } elsif (-f "$desttop/$subdir/$path") { + $dir_is_file = 1; + } else { + $fail = 1; + } + } elsif (-e "$desttop/$subdir/$file") { + $dup = 1; + } else { + $src = "$srctop/$file"; + $dest = "$desttop/$subdir/$file"; + $fname = $file; + } + if ($fail) { + $r->print('

    '.&mt('Target directory: [_1] does not exist, and could not be created.', + ''.$desturl.'/'.$subdir.'/'.$path.''). + '

    '."\n"); + } elsif ($dup) { + $r->print('

    '.&mt('Target file: [_1] already exists -- not overwriting.', + ''.$desturl.'/'.$subdir.'/'.$file.''). + '

    '."\n"); + } elsif ($dir_is_file) { + $r->print('

    '.&mt('Target directory: [_1] name is already in a use for a file -- not overwriting.', + ''.$desturl.'/'.$subdir.'/'.$file.''). + '

    '."\n"); + } elsif (($src ne '') && ($dest ne '')) { + my $ressrc = $docroot.$resurl.'/'.$file; + my $ressrcmeta = $ressrc.'.meta'; + my ($ext) = ($file =~ /\.(\w+)$/); + my $embstyle=&Apache::loncommon::fileembstyle($ext); + my ($getres,$getresmeta); + if ($respublish) { + if ($path eq '') { + if ((ref($resfiles{'/'}) eq 'HASH') && + (exists($resfiles{'/'}{$fname}))) { + $getres = 1; + $getresmeta = 1; + } + } elsif ((ref($resfiles{$path}) eq 'HASH') && + (exists($resfiles{$path}{$fname}))) { + $getres = 1; + $getresmeta = 1; + } + } + if ($is_course_home) { + my ($needpriv,$needprivmeta); + if ($respublish) { + if ($getres) { + if (&Apache::londiff::are_different_files($src,$ressrc)) { + $needpriv = 1; + if (&File::Copy::copy($ressrc,$dest)) { + if ($embstyle eq 'ssi') { + &crsres_fixup($dest,$coursenum,$coursedom,$ca,$cd); + } + } + } else { + if (&File::Copy::copy($src,$dest)) { + $newfile{$file} = $desturl.'/'.$subdir.'/'.$file; + if ($embstyle eq 'ssi') { + &crsres_fixup($dest,$coursenum,$coursedom,$ca,$cd,$subdir); + } + } + } + } else { + $needpriv = 1; + } + if ($getresmeta) { + if ((-e $src.'.meta') && (!-e $dest.'.meta')) { + if (&Apache::londiff::are_different_files($src.'.meta',$ressrc.'.meta')) { + if (&File::Copy::copy($ressrc.'.meta',$dest.'.meta')) { + &crsres_fixup_meta($dest,$coursenum,$coursedom,$ca,$cd,$copyright, + $customdistfile,$sourceavail,\%checkdeps); + } + $needprivmeta = 1; + } else { + if (&File::Copy::copy($src.'.meta',$dest.'.meta')) { + &crsres_fixup_meta($dest,$coursenum,$coursedom,$ca,$cd,$copyright, + $customdistfile,$sourceavail,\%checkdeps); + } + } + } + } + if ($getres) { + my $destresfile = $docroot.$destresurl.'/'.$subdir.'/'.$file; + if (-e $dest) { + my $output = &Apache::lonpublisher::batchpublish($r,$dest,$destresfile,$nokeyref,1); + if (-e $destresfile) { + $newresfile{$file} = $destresurl.'/'.$subdir.'/'.$file; + } + } + } + } else { + $needpriv = 1; + if ((-e $src.'.meta') && (!-e $dest.'.meta')) { + $needprivmeta = 1; + } + } + if ($needpriv) { + if (&File::Copy::copy($src,$dest)) { + $newfile{$file} = $desturl.'/'.$subdir.'/'.$file; + if ($embstyle eq 'ssi') { + &crsres_fixup($dest,$coursenum,$coursedom,$ca,$cd,$subdir); + } + } + } + if ($needprivmeta) { + if (&File::Copy::copy($src.'.meta',$dest.'.meta')) { + &crsres_fixup_meta($dest,$coursenum,$coursedom,$ca,$cd,$copyright, + $customdistfile,$sourceavail,\%checkdeps); + } + } + } else { + my ($needpriv,$needprivmeta); + if ($respublish) { + if ($getres) { + &Apache::lonnet::repcopy($docroot.$resurl.'/'.$file); + } + if ($getresmeta) { + &Apache::lonnet::repcopy($docroot.$resurl.'/'.$file.'.meta'); + } + if (-e $docroot.$resurl.'/'.$file) { + if (&Apache::lonnet::repcopy_crsprivfile($srcurl.'/'.$file,$dest) eq 'ok') { + if (&Apache::londiff::are_different_files($docroot.$resurl.'/'.$file,$dest)) { + $needpriv = 1; + if (&File::Copy::copy($docroot.$resurl.'/'.$file,$dest)) { + if ($embstyle eq 'ssi') { + &crsres_fixup($dest,$coursenum,$coursedom,$ca,$cd); + } + } + } else { + if ($embstyle eq 'ssi') { + &crsres_fixup($dest,$coursenum,$coursedom,$ca,$cd,$subdir); + } + $newfile{$file} = $desturl.'/'.$subdir.'/'.$file; + } + } + } else { + $needpriv = 1; + } + if (-e $docroot.$resurl.'/'.$file.'.meta') { + if (&Apache::lonnet::repcopy_crsprivfile($srcurl.'/'.$file.'.meta',$dest.'.meta') eq 'ok') { + if (&Apache::londiff::are_different_files($docroot.$resurl.'/'.$file.'.meta',$dest.'.meta')) { + $needprivmeta = 1; + if (&File::Copy::copy($docroot.$resurl.'/'.$file.'.meta',$dest.'.meta')) { + &crsres_fixup_meta($dest,$coursenum,$coursedom,$ca,$cd,$copyright, + $customdistfile,$sourceavail,\%checkdeps); + } + } else { + &crsres_fixup_meta($dest,$coursenum,$coursedom,$ca,$cd,$copyright, + $customdistfile,$sourceavail,\%checkdeps); + } + } + } else { + if (!-e $dest.'.meta') { + $needprivmeta = 1; + } + } + if ($getres) { + my $destresfile = $docroot.$destresurl.'/'.$subdir.'/'.$file; + if (-e $dest) { + my $output = &Apache::lonpublisher::batchpublish($r,$dest,$destresfile,$nokeyref,1); + if (-e $destresfile) { + $newresfile{$file} = $destresurl.'/'.$subdir.'/'.$file; + } + } + } + } else { + $needpriv = 1; + if (!-e $dest.'.meta') { + $needprivmeta = 1; + } + } + if ($needpriv) { + if (&Apache::lonnet::repcopy_crsprivfile($srcurl.'/'.$file,$dest) eq 'ok') { + if ($embstyle eq 'ssi') { + &crsres_fixup($dest,$coursenum,$coursedom,$ca,$cd,$subdir); + } + $newfile{$file} = $desturl.'/'.$subdir.'/'.$file; + } + } + if ($needprivmeta) { + if (&Apache::lonnet::repcopy_crsprivfile($srcurl.'/'.$file.'.meta',$dest.'.meta') eq 'ok') { + &crsres_fixup_meta($dest,$coursenum,$coursedom,$ca,$cd,$copyright, + $customdistfile,$sourceavail,\%checkdeps); + } + } + } + } + } + } else { + $notopdir = 1; + } + } + if ($notopdir) { + $r->print('

    '.&mt('No files or sub-directories copied').'
    '."\n". + ''.&mt('Target directory: [_1] does not exist, and could not be created.', + ''.$desturl.'/'.$subdir.''). + '

    '."\n"); + } + if (keys(%newdir)) { + $r->print('

    '.&mt('Created the following directories in [_1]:',''.$desturl.'/'.$subdir.''). + '

    '."\n". + '

    '."\n"); + } + if (keys(%newfile)) { + $r->print('

    '.&mt('Copied the following files to [_1]:',''.$desturl.'/'.$subdir.''). + '

    '."\n". + '

    '."\n"); + foreach my $file (keys(%newfile)) { + my %storehash = ( + 'priv' => $newfile{$file}, + 'who' => $env{'user.name'}.':'.$env{'user.domain'}, + ); + if (exists($newresfile{$file})) { + $storehash{'res'} = 1; + } + &Apache::lonnet::store_userdata(\%storehash,$file,'copycourseauthor',$coursedom,$coursenum); + } + } + if (keys(%checkdeps)) { + my %missingdep; + foreach my $depfile (sort(keys(%checkdeps))) { + unless (-e "$desttop/$depfile") { + $missingdep{$depfile} = 1; + } + } + if (keys(%missingdep)) { + $r->print('

    '.&mt('You may also need to copy the following missing dependencies for files copied to [_1]:', + ''.$desturl.'/'.$subdir.''). + '

    '."\n". + '

    '."\n"); + } + } + } else { + $r->print('

    '.&mt('No currently existing files or directories in Course Authoring Space selected for copying').'

    '); + $r->print(&endContentScreen()); + return ''; + } + } else { + my $chkname = 'copytouser'; + my $context = 'crsauthored'; + my (%subdirs,%files,@dirs_by_depth,@files_by_depth,%parent,%children,%hierarchy,@checked_maps); + &Apache::lonnet::recursedirs($is_course_home,1,undef,$exclude,0,0,$srcurl,'',\%subdirs,\%files,1); + foreach my $key (keys(%subdirs)) { + next if (($key eq '/') || ($key eq '')); + my @items = split(/\//,$key); + my $dir = pop(@items); + my $depth = scalar(@items); + my $path; + if (!$depth) { + $path = '/'; + } else { + $path = join('/',@items); + } + $dirs_by_depth[$depth]{$path}{$dir} = 1; + } + foreach my $path (keys(%files)) { + next if ($path eq ''); + my $depth; + if ($path eq '/') { + $depth = 0; + } else { + $depth = scalar(split(/\//,$path)); + } + if (ref($files{$path}) eq 'HASH') { + foreach my $file (keys(%{$files{$path}})) { + $files_by_depth[$depth]{$path}{$file} = $files{$path}{$file}; + } + } + } + my ($info,$display,$onsubmit,$togglebuttons,$disabled); + my (%resdirs,%resfiles); + &Apache::lonnet::recursedirs($is_course_home,1,undef,$res_exclude,0,0,$resurl,'',\%resdirs,\%resfiles); + my $numpub = 0; + if (keys(%resfiles)) { + foreach my $dir (keys(%resfiles)) { + if (ref($resfiles{$dir}) eq 'HASH') { + foreach my $file (keys(%{$resfiles{$dir}})) { + if (exists($files{$dir}{$file})) { + $numpub ++; + } + } + } + } + } + if ($readonly) { + $disabled = ' disabled="disabled"'; + } + if ($disabled) { + $togglebuttons = '
    '; + } else { + $togglebuttons = ''. + '  '; + } + my $preamble = &authorspace_selector($r,$formname,$home,$title,%outhash). + &courseresource_options($formname,$numpub). + '
    '."\n"; + my $display = '
    '."\n". + $preamble."\n". + '
    '."\n". + '
    '."\n". + ''.&mt('Content to copy').(' 'x4).$togglebuttons.''."\n". + ''. + &mt('Choose the files and/or folders to copy from Course Authoring to User Authoring'). + '

    '."\n"; + my $count = 0; +# +# Warning to developers: +# +# If you add or remove form elements which precede the table of items to copy +# you will need to modify the value for startcount. Form elements include both: +# and
    tags. +# $startcount (set to 9) contains the following: +# fieldsets with following legends: (a) Folder in Authoring Space, (b) Distribution to set in metadata +# (c) Content to copy +# inputs: textbox for destination folder; dropdown lists: (a) Copyright, (b) Source +# hidden: customrights file; buttons: (a) check all, (b) uncheck all. +# authorspace: if more than 1: a fieldset with legend: Select the Authoring Space, +# or if 1: an input (hidden) with available author/coauthor role. +# if there are multiple possible author/coauthor roles (i.e., $home > 1), +# incerement startcount by 1 for the dropdown list uses to select the target. +# +# If there are published files, increment startcount by 3: +# fieldset (legend: Published Resources), and two radio buttons (Yes/No). +# + my $startcount = 9; + if ($home > 1) { + $startcount ++; + } + if ($numpub) { + $startcount += 3; + } + my $lastcontainer = $startcount; + $display .= &Apache::loncommon::start_data_table()."\n". + &Apache::loncommon::start_data_table_header_row(). + ''.&mt('Copy?').''. + ''.&mt('Name').''. + ''.&mt('Last modified').''. + ''.&mt('Published?').''. + &Apache::loncommon::end_data_table_header_row()."\n"; + $count = &recurse_crsauthored(0,\@dirs_by_depth,\@files_by_depth,'/',$startcount, + $count,\$display,\%parent,\%children,$readonly, + $formname,$chkname,\$lastcontainer,\%resfiles); + $display .= &Apache::loncommon::end_data_table().'
    '; + unless ($readonly) { + $display .= '
    '. + '
    '. + ''. + '
    '; + } + $display .= &Apache::loncourserespicker::respicker_javascript($startcount,$count,$context,$formname,\%children, + \%hierarchy,\@checked_maps,$home,$chkname); + $r->print($display); + } + $r->print(&endContentScreen()); +} + +sub recurse_crsauthored { + my ($currdepth,$dirs_by_depth,$files_by_depth,$currpath,$startcount,$count,$displayref, + $parent,$children,$readonly,$formname,$chkname,$lastcontainerref,$resfilesref) = @_; + return $count unless ((ref($dirs_by_depth) eq 'ARRAY') && (ref($files_by_depth) eq 'ARRAY') && + (ref($resfilesref) eq 'HASH')); + my ($disabled,$hasdirs,$hasfiles,%unique,%dirs,%files); + if ((ref($dirs_by_depth->[$currdepth]) eq 'HASH') && + (ref($dirs_by_depth->[$currdepth]{$currpath}) eq 'HASH')) { + $hasdirs = 1; + %dirs = %{$dirs_by_depth->[$currdepth]{$currpath}}; + map { $unique{$_} = 1; } keys(%dirs); + } + if ((ref($files_by_depth->[$currdepth]) eq 'HASH') && + (ref($files_by_depth->[$currdepth]{$currpath}) eq 'HASH')) { + $hasfiles = 1; + %files = %{$files_by_depth->[$currdepth]{$currpath}}; + map { $unique{$_} = 1; } keys(%files); + } + if ($readonly) { + $disabled = ' disabled="disabled"'; + } + my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); + my $whitespace = + ''; + $parent->{$currdepth} = $$lastcontainerref; + foreach my $item (sort { lc($a) cmp lc($b) } (keys(%unique))) { + next if ($item eq ''); + my $currelem; + if ($hasdirs && exists($dirs{$item})) { + $count ++; + my $deeper = $currdepth+1; + my ($newpath,$showpath); + if ($currpath eq '/') { + $newpath = $item; + $showpath = $currpath.$item.'/'; + } else { + $newpath = $currpath.'/'.$item; + $showpath = '/'.$currpath.'/'.$item.'/'; + } + $currelem = $count+$startcount; + $$lastcontainerref = $currelem; + $children->{$parent->{$currdepth}} .= $currelem.':'; + my $icon = 'src="'.$location.'/navmap.folder.open.gif" alt="'.&mt('Folder').'"'; + $$displayref .= &Apache::loncommon::start_data_table_row(). + ''; + for (my $i=0; $i<$currdepth; $i++) { + $$displayref .= "$whitespace\n"; + } + $$displayref .= ' '.$item.'  '. + &Apache::loncommon::end_data_table_row()."\n"; + $count = &recurse_crsauthored($deeper,$dirs_by_depth,$files_by_depth,$newpath, + $startcount,$count,$displayref,$parent,$children, + $readonly,$formname,$chkname,$lastcontainerref,$resfilesref); + } + if ($hasfiles && exists($files{$item})) { + $count ++; + $currelem = $count+$startcount; + $children->{$parent->{$currdepth}} .= $currelem.':'; + my $icon = 'src="'.&Apache::loncommon::icon($item).'"'; + my ($ext) = ($item =~ /\.([^.]+)$/); + my $alttext; + if (lc($ext) eq 'problem') { + $alttext = ' alt="'.&mt('Problem Icon').'"'; + } elsif ($ext =~ /^x?html?$/i) { + $alttext = ' alt="'.&mt('Web Page Icon').'"'; + } elsif ($ext =~ /^(jpg|gif|png|svg|jpeg)$/) { + $alttext = ' alt="'.&mt('Image Icon').'"'; + } else { + $alttext = ' alt="'.&mt('Resource Icon').'"'; + } + my $showpath; + if ($currpath eq '/') { + $showpath = $currpath; + } else { + $showpath = "/$currpath/"; + } + my ($published,$lastmod); + if ((ref($resfilesref->{$currpath})) && (exists($resfilesref->{$currpath}{$item}))) { + $published = ''.&mt('yes').''; + } else { + $published = ''.&mt('no').''; + } + $$displayref .= &Apache::loncommon::start_data_table_row(). + ''; + for (my $i=0; $i<$currdepth; $i++) { + $$displayref .= "$whitespace\n"; + } + $$displayref .= ' '.$item.''. + ''.&Apache::lonlocal::locallocaltime($files{$item}).''. + ''.$published.''. + &Apache::loncommon::end_data_table_row()."\n"; + } + } + $$lastcontainerref = $parent->{$currdepth}; + return $count; +} + +sub courseresource_options { + my ($formname,$numpub) = @_; + my %lt = &Apache::lonlocal::texthash( + 'default' => 'System wide - can be used for any courses system wide', + 'domain' => 'Domain only - use limited to courses in the domain', + 'custom' => 'Customized right of use ...', + 'public' => 'Public - no authentication or authorization required for use', + 'closed' => 'Closed - XML source is closed to everyone', + 'open' => 'Open - XML source is open to people who want to use it', + 'sel' => 'Select', + ); + my $output; + if ($numpub) { + $output .= '
    '. + '
    '.&mt('Published Resources').''. + &mt('[quant,_1,file] in Course Authoring Space also exist in Resource Space.', + $numpub).'
    '. + &mt('Publish copied files in selected Authoring Space?').': '."\n". + ''."\n". + ''."\n". + '
    '."\n"; + } + $output .= '
    '. + '
    '.&mt('Distribution to set in metadata').''. + &mt('Copyright').': '. + '
    '."\n". + &mt('Source').' :'. + '
    '."\n". + '
    '."\n"; + return $output; +} + +sub crsres_fixup_meta { + my ($dest,$coursenum,$coursedom,$ca,$cd,$copyright,$customdistfile,$sourceavail,$checkdeps) = @_; + return unless (ref($checkdeps) eq 'HASH'); + if (open(my $fh,'<',$dest.'.meta')) { + my ($output,$now,$setsourceavail); + $now = time; + if (($dest =~ /\.(xml|html|htm|xhtml|xhtm)$/i) || ($dest =~ /$LONCAPA::assess_re/)) { + $setsourceavail = 1; + } + while (my $line=<$fh>) { + chomp($line); + if ($line eq "$coursenum:$coursedom") { + $output .= "$ca:$cd\n"; + } elsif ($line eq 'custom') { + $output .= "$copyright\n"; + } elsif ($line =~ m{^\d+$}) { + $output .= "$now\n"; + } elsif ($line eq "/res/$coursedom/$coursenum/default.rights") { + $output .= "$customdistfile\n"; + } elsif ($line =~ m{^(open|closed)$}) { + if ($setsourceavail) { + $output .= "$sourceavail\n"; + } + } elsif ($line eq "$coursedom") { + $output .= "$cd\n"; + } elsif ($line =~ m{^\d+$}) { + $output .= "$now\n"; + } elsif ($line =~ m{^$match_username:$match_domain$}) { + $output .= "$env{'user.name'}:$env{'user.domain'}\n"; + } elsif ($line eq "$coursenum:$coursedom") { + $output .= "$ca:$cd\n"; + } elsif ($line =~ m{^(.+)$}) { + my @deps = split(/\s*,\s*/,$1); + my @newdeps; + my $changed = 0; + foreach my $dep (@deps) { + if ($dep =~ m{^/res/$coursedom/$coursenum/(.+)$}) { + my $rest = $1; + push(@newdeps,"/res/$cd/$ca/$rest"); + $checkdeps->{$rest} = 1; + $changed ++; + } else { + push(@newdeps,$dep); + } + } + if ($changed) { + $output .= ''.join(',',@newdeps).''."\n"; + } + } else { + $output .= "$line\n"; + } + } + close($fh); + if (open(my $fh,'>',$dest.'.meta')) { + print $fh $output; + close($fh); + } + } +} + +sub crsres_fixup { + my ($dest,$coursenum,$coursedom,$ca,$cd,$subdir) = @_; + my $outstring=''; + my $changes = 0; + my @parser; + $parser[0]=HTML::LCParser->new($dest); + $parser[-1]->xml_mode(1); + my $token; + while (@parser) { + while ($token=$parser[-1]->get_token) { + if ($token->[0] eq 'S') { + my $tag=$token->[1]; + my $lctag=lc($tag); + my %parms=%{$token->[2]}; + foreach my $type ('src','href','background','bgimg') { + foreach my $key (keys(%parms)) { + if ($key =~ /^$type$/i) { + next if (($lctag eq 'img') && ($type eq 'src') && + ($parms{$key} =~ m{^data\:image/gif;base64,})); + if ($parms{$key} =~ m{^\Q/res/$coursedom/$coursenum/\E}si) { + $parms{$key} =~ s{^\Q/res/$coursedom/$coursenum/\E}{/res/$cd/$ca/$subdir/}si; + $changes ++; + } + } + } + } + # probably a image type --> CHBO my $imsfolder = $env{'form.folder'}; @@ -4496,20 +7614,22 @@ CHBO IMSFORM @@ -4521,10 +7641,12 @@ 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'} @@ -4547,31 +7675,101 @@ FUFORM $lt{'copm'}
    -  $lt{'selm'}
    - + onfocus="this.blur();$mapimportjs" $disabled /> +  $lt{'selm'}
    + SEDFFORM + my ($importcrsresform,$checkcrsres); + if ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.crsauthor'}) { + $checkcrsres = 1; + } elsif ($env{'course.'.$coursedom.'_'.$coursenum.'.internal.crsauthor'} ne '0') { + my %domdefs=&Apache::lonnet::get_domain_defaults($coursedom); + my $type = lc($env{'course.'.$env{'request.course.id'}.'.type'}); + unless (($type eq 'community') || ($type eq 'placement')) { + $type = 'unofficial'; + if ($env{'course.'.$env{'request.course.id'}.'internal.coursecode'} ne '') { + $type = 'official'; + } elsif ($env{'course.'.$env{'request.course.id'}.'internal.textbook'} ne '') { + $type = 'textbook'; + } else { + $type = 'unofficial'; + } + } + if ($domdefs{$type.'crsauthor'}) { + $checkcrsres = 1; + } + } + if ($checkcrsres) { + my ($numdirs,$pickfile) = + &Apache::loncommon::import_crsauthor_form('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{srch}.'' => $pathitem."$lt{'srch'}$help{'Search_LON-CAPA_Resource'}" }, { ''.$lt{impo}.'' => "$lt{'impo'}$help{'Importing_LON-CAPA_Resource'}" }, - { ''.$lt{lnks}.'' => "$lt{'lnks'}" }, - { ''.$lt{impm}.'' => $importpubform } - ); + { ''.$lt{lnks}.'' => ''.$lt{'lnks'}.''.$help{'Import_Stored_Links'} }, + { ''.$lt{impm}.'' => $importpubform }, + ); + if ($importcrsresform) { + 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(< @@ -4581,6 +7779,11 @@ SEDFFORM $containertag +
    + + + $containertag +
    HIDDENFORM $r->print(&makesimpleeditform($pathitem)."\n". @@ -4603,12 +7806,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')); @@ -4617,9 +7820,13 @@ HIDDENFORM } my $postexec=''; if ($folder eq 'default') { + my $windowname = 'loncapaclient'; + if ($env{'request.lti.login'}) { + $windowname .= 'lti'; + } $r->print(''."\n" ); @@ -4632,17 +7839,17 @@ HIDDENFORM my $newnavform=(< - + $pathitem - $lt{'navc'} + $lt{'navc'} $help{'Navigate_Content'} NNFORM my $newsmppageform=(< - + $pathitem $lt{'sipa'} @@ -4652,18 +7859,18 @@ NSPFORM my $newsmpproblemform=(< - + $pathitem $lt{'sipr'} - $help{'Simple Problem'} + $help{'Simple_Problem'} NSPROBFORM my $newdropboxform=(< - + $pathitem $lt{'drbx'} @@ -4673,7 +7880,7 @@ NDBFORM my $newexuploadform=(< - + $pathitem $lt{'scuf'} @@ -4683,7 +7890,7 @@ NEXUFORM my $newbulform=(< - + $pathitem $lt{'bull'} @@ -4693,18 +7900,18 @@ NBFORM my $newaboutmeform=(< - + $pathitem - $lt{'mypi'} + $lt{'mypi'} $help{'My Personal Information Page'} NAMFORM my $newaboutsomeoneform=(< - + $pathitem $lt{'abou'} @@ -4713,11 +7920,11 @@ NASOFORM my $newrosterform=(< - + $pathitem - $lt{'rost'} + $lt{'rost'} $help{'Course_Roster'} NROSTFORM @@ -4734,14 +7941,172 @@ NROSTFORM } my $newwebpageform =(< - + $pathitem $lt{'webp'} $help{'Web_Page'} NWEBFORM - + my $showpath = &HTML::Entities::encode($env{'form.folderpath'},'<>&"'); + my @ids=&Apache::lonnet::current_machine_ids(); + my $machines_str = "'".join("','",@ids)."'"; + my (%is_home,%toppath,$rolehomes); + if ($env{'user.author'}) { + if (grep(/^\Q$env{'user.home'}\E$/,@ids)) { + $is_home{'author'} = 1; + } + $rolehomes = ''."\n"; + } + my %roleshash = &Apache::lonnet::get_my_roles($env{'user.name'},$env{'user.domain'},'userroles', + ['active'],['ca','aa']); + my %by_roletype; + if (keys(%roleshash)) { + foreach my $entry (keys(%roleshash)) { + my ($auname,$audom,$roletype) = split(/:/,$entry); + my $key = $entry; + $key =~ s/:/___/g; + my $author = $auname.'___'.$audom; + $by_roletype{$roletype}{$author} = 1; + my $rolehome = &Apache::lonnet::homeserver($auname,$audom); + $toppath{$author} = "/priv/$audom/$auname"; + if (grep(/^\Q$rolehome\E$/,@ids)) { + $is_home{$author} = 1; + } + $rolehomes .= ''."\n"; + } + } + my $crshome = $env{'course.'.$env{'request.course.id'}.'.home'}; + if (grep(/^\Q$crshome\E$/,@ids)) { + $is_home{'course'} = 1; + } + $rolehomes .= ''."\n"; + my $pickdir = $lt{'loca'}. + '
    '."\n". + $lt{'dire'}. + '
    '."\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; + if (($env{'user.author'}) || ($checkcrsres)) { + $crsresform=(< + $lt{'stpr'}$help{'Standard_Problem'} +
    + +
    + +RESFORM + } my $specialdocumentsform; my @specialdocumentsforma; @@ -4758,7 +8123,7 @@ my $newfolderb;
    - + $lt{'newp'} $help{'Adding_Pages'}
    @@ -4769,18 +8134,18 @@ NPFORM
    $pathitem - + $lt{'newf'}$help{'Adding_Folders'}
    NFFORM my $newsylform=(< - + $pathitem - $lt{'syll'} + $lt{'syll'} $help{'Syllabus'} @@ -4788,27 +8153,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 ($posslti) { + 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} @@ -4822,41 +8200,50 @@ NGFFORM @gradingforma=( {''.$lt{sipr}.''=>$newsmpproblemform}, {''.$lt{drbx}.''=>$newdropboxform}, - {''.$lt{scuf}.''=>$newexuploadform}, - + {''.$lt{scuf}.''=>$newexuploadform} ); + if ($crsresform) { + push(@gradingforma, + {''.$lt{stpr}.''=>$crsresform} + ); + } $gradingform = &create_form_ul(&create_list_elements(@gradingforma)); @communityforma=( {''.$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], + 'bb' => ['External',$externalform], + 'cc' => ['Import',$importpubform], + 'dd' => ['Assessment',$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,''); } @@ -4868,7 +8255,7 @@ unless ($container eq 'page') { unless ($supplementalflag) { $folder='supplemental'; } - if ($folder =~ /^supplemental$/ && + if (($folder eq 'supplemental') && (($env{'form.folderpath'} =~ /^default\&/) || ($env{'form.folderpath'} eq ''))) { $env{'form.folderpath'} = &supplemental_base(); } elsif ($allowed) { @@ -4886,8 +8273,10 @@ unless ($container eq 'page') {
    SUPDOCFORM my $supnewfolderform=(< - + $pathitem $lt{'newf'} @@ -4915,7 +8305,15 @@ SNFFORM my $supextform = &Apache::lonextresedit::extedit_form(1,0,undef,undef,$pathitem, - $help{'Adding_External_Resource'}); + $help{'Adding_External_Resource'}, + undef,undef,undef,undef,undef,undef, + $disabled); + + my $supexttoolform = + &Apache::lonextresedit::extedit_form(1,0,undef,undef,$pathitem, + $help{'Adding_External_Tool'}, + undef,undef,'tool',$coursedom, + $coursenum,\%ltitools,$disabled); my $supnewsylform=(< @@ -4923,7 +8321,7 @@ SNFFORM $pathitem - $lt{'syll'} + $lt{'syll'} $help{'Syllabus'} SNSFORM @@ -4934,7 +8332,7 @@ SNSFORM $pathitem - $lt{'mypi'} + $lt{'mypi'} $help{'My Personal Information Page'} SNAMFORM @@ -4961,48 +8359,50 @@ SWEBFORM my @specialdocs = ( - {''.$lt{syll}.'' + {''.$lt{syll}.'' =>$supnewsylform}, - {''.$lt{mypi}.'' + {''.$lt{mypi}.'' =>$supnewaboutmeform}, {''.$lt{webp}.''=>$supwebpageform}, ); -my @supimportdoc = ( - {''.$lt{extr}.'' - =>$supextform}, - {''.$lt{upl}.'' + my @supexternal = ( + {''.$lt{extr}.'' + =>$supextform}); + if ($posslti) { + push(@supexternal, + {''.$lt{extt}.'' + =>$supexttoolform}); + } + my @supimportdoc = ( + {''.$lt{upl}.'' =>$supupdocform}, - ); + ); $supupdocform = &create_form_ul(&create_list_elements(@supimportdoc)); my %suporderhash = ( '00' => ['Supnewfolder', $supnewfolderform], - 'ee' => ['Upload',$supupdocform], + 'dd' => ['Upload',$supupdocform], + 'ee' => ['External',&create_form_ul(&create_list_elements(@supexternal))], 'ff' => ['Other',&create_form_ul(&create_list_elements(@specialdocs))] ); if ($supplementalflag) { - my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, - $supplementalflag,\%suporderhash,$iconpath,$pathitem); - if ($error) { - $r->print('

    '.$error.'

    '); - } else { - if ($suppchanges) { - my %servers = &Apache::lonnet::internet_dom_servers($coursedom); - my @ids=&Apache::lonnet::current_machine_ids(); - foreach my $server (keys(%servers)) { - next if (grep(/^\Q$server\E$/,@ids)); - my $hashid=$coursenum.':'.$coursedom; - &Apache::lonnet::remote_devalidate_cache($server,'suppcount',$hashid); - } - &Apache::lonnet::get_numsuppfiles($coursenum,$coursedom,1); - undef($suppchanges); - } - } + $suppchanges = 0; + my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, + $supplementalflag,\%suporderhash,$iconpath,$pathitem, + \%ltitools,$canedit,$hostname); + if ($error) { + $r->print('

    '.$error.'

    '); + } + if ($suppchanges) { + &Apache::lonnet::update_supp_caches($coursedom,$coursenum); + undef($suppchanges); + } } } elsif ($supplementalflag) { my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$crstype, - $supplementalflag,'',$iconpath,$pathitem); + $supplementalflag,'',$iconpath,$pathitem,'',$canedit, + $hostname); if ($error) { $r->print('

    '.$error.'

    '); } @@ -5029,13 +8429,16 @@ my %suporderhash = ( &entryline(0,&mt("Click to download or use your browser's Save Link function"),$showdoc).''); } } - $r->print(&Apache::loncommon::end_page()); + unless ($noendpage) { + $r->print(&Apache::loncommon::end_page()); + } return OK; } sub embedded_form_elems { my ($phase,$primaryurl,$newidx) = @_; my $folderpath = &HTML::Entities::encode($env{'form.folderpath'},'<>&"'); + $newidx =~s /\D+//g; return < @@ -5056,7 +8459,11 @@ sub embedded_destination { } elsif ($folder =~ /^(default|supplemental)_(\d+)$/) { $destination .= $2.'/'; } - $destination .= $env{'form.newidx'}; + my $newidx = $env{'form.newidx'}; + $newidx =~s /\D+//g; + if ($newidx) { + $destination .= $newidx; + } my $dir_root = '/userfiles'; return ($destination,$dir_root); } @@ -5076,15 +8483,18 @@ sub decompression_info { my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'}; my $container='sequence'; my ($pathitem,$hiddenelem); - my @hiddens = ('newidx','comment','position','folderpath'); + my @hiddens = ('newidx','comment','position','folderpath','archiveurl'); if ($env{'form.folderpath'} =~ /\:1$/) { $container='page'; } unshift(@hiddens,$pathitem); foreach my $item (@hiddens) { + if ($item eq 'newidx') { + next if ($env{'form.'.$item} =~ /\D/); + } if ($env{'form.'.$item}) { $hiddenelem .= ''."\n"; + &HTML::Entities::encode($env{'form.'.$item},'<>&"').'" />'."\n"; } } return ($destination,$dir_root,$londocroot,$docudom,$docuname,$container, @@ -5099,7 +8509,7 @@ sub decompression_phase_one { $error = &mt('Archive file "[_1]" not in the expected location.',$env{'form.archiveurl'}); } else { my $file = $1; - $output = + $output = &Apache::loncommon::process_decompression($docudom,$docuname,$file, $destination,$dir_root, $hiddenelem); @@ -5141,25 +8551,38 @@ sub remove_archive { } else { $delwarning = &mt('An error occurred retrieving the contents of the current folder.'); } - $delwarning .= &mt('As a result the archive file has not been removed.'); + $delwarning .= ' '.&mt('As a result the archive file has not been removed.'); } else { my $currcmd = $env{'form.cmd'}; my $position = $env{'form.position'}; - if ($position > 0) { - $env{'form.cmd'} = 'del_'.$position; - my ($title,$url,@rrest) = - split(/:/,$LONCAPA::map::resources[$LONCAPA::map::order[$position]]); - if (&handle_edit_cmd($docuname,$docudom)) { - ($errtext,$fatal) = &storemap($docuname,$docudom,$map,1); - if ($fatal) { - if ($container eq 'page') { - $delwarning = &mt('An error occurred updating the contents of the current page.'); + my $archiveidx = $position; + if ($position > 0) { + if (($env{'form.autoextract_camtasia'}) && (scalar(@LONCAPA::map::order) == 2)) { + $archiveidx = $position-1; + } + $env{'form.cmd'} = 'remove_'.$archiveidx; + my ($title,$url,@rrest) = + split(/:/,$LONCAPA::map::resources[$LONCAPA::map::order[$archiveidx]]); + if ($url eq $env{'form.archiveurl'}) { + if (&handle_edit_cmd($docuname,$docudom)) { + ($errtext,$fatal) = &storemap($docuname,$docudom,$map,1); + if ($suppchanges) { + &Apache::lonnet::update_supp_caches($docudom,$docuname); + undef($suppchanges); + } + if ($fatal) { + if ($container eq 'page') { + $delwarning = &mt('An error occurred updating the contents of the current page.'); + } else { + $delwarning = &mt('An error occurred updating the contents of the current folder.'); + } } else { - $delwarning = &mt('An error occurred updating the contents of the current folder.'); + $delresult = &mt('Archive file removed.'); } - } else { - $delresult = &mt('Archive file removed.'); } + } else { + $delwarning .= &mt('Archive file had unexpected item number in folder.'). + ' '.&mt('As a result the archive file has not been removed.'); } } $env{'form.cmd'} = $currcmd; @@ -5178,21 +8601,25 @@ sub remove_archive { } sub generate_admin_menu { - my ($crstype) = @_; + my ($crstype,$canedit,$coursenum,$coursedom) = @_; my $lc_crstype = lc($crstype); my ($home,$other,%outhash)=&authorhosts(); - my %lt=&Apache::lonlocal::texthash ( + my %lt= ( # do not translate here 'vc' => 'Verify Content', 'cv' => 'Check/Set Resource Versions', 'ls' => 'List Resource Identifiers', + 'ct' => 'Display/Set Shortened URLs for Deep-linking', + 'ca' => "Enter $crstype Authoring Space", 'imse' => 'Export contents to IMS Archive', - 'dcd' => "Dump $crstype Content to Authoring Space", - ); - my ($candump,$dumpurl); + 'dcd' => 'Copy uploaded content to Authoring Space', + 'cpc' => 'Copy from Course Authoring to User Authoring', + ); + my ($candump,$dumpurl,$exportcrsurl); if ($home + $other > 0) { $candump = 'F'; if ($home) { $dumpurl = "javascript:injectData(document.courseverify,'dummy','dumpcourse','$lt{'dcd'}')"; + $exportcrsurl = "javascript:injectData(document.courseverify,'dummy','copyauthored','$lt{'cpc'}')"; } else { my @hosts; foreach my $aurole (keys(%outhash)) { @@ -5206,8 +8633,10 @@ sub generate_admin_menu { &HTML::Entities::encode($env{'request.role'},'"<>&').'&origurl='. &HTML::Entities::encode('/adm/coursedocs?dumpcourse=1','"<>&'); $dumpurl = "javascript:dump_needs_switchserver('$switchto')"; + $exportcrsurl = $dumpurl; } else { $dumpurl = "javascript:choose_switchserver_window()"; + $exportcrsurl = $dumpurl; } } } @@ -5217,14 +8646,14 @@ sub generate_admin_menu { { linktext => $lt{'vc'}, url => "javascript:injectData(document.courseverify,'dummy','verify','$lt{'vc'}')", permission => 'F', - help => 'Verify_Content', + help => 'Docs_Verify_Content', icon => 'verify.png', linktitle => 'Verify contents can be retrieved/rendered', }, { linktext => $lt{'cv'}, url => "javascript:injectData(document.courseverify,'dummy','versions','$lt{'cv'}')", permission => 'F', - help => 'Check_Resource_Versions', + help => 'Docs_Check_Resource_Versions', icon => 'resversion.png', linktitle => "View version information for resources in your $lc_crstype, and fix/unfix use of specific versions", }, @@ -5235,8 +8664,44 @@ sub generate_admin_menu { icon => 'symbs.png', linktitle => "List the unique identifier used for each resource instance in your $lc_crstype" }, + { linktext => $lt{'ct'}, + url => "javascript:injectData(document.courseverify,'dummy','shorturls','$lt{'ct'}')", + permission => 'F', + help => 'Docs_Short_URLs', + icon => 'shorturls.png', + linktitle => "Set shortened URLs for a resource or folder in your $lc_crstype for use in deep-linking" + }, ] - }, + }); + if ($canedit) { + my ($crsauname,$crsaudom,$crshome); + if (($coursenum ne '') && ($coursedom ne '')) { + my $crsauthorurl = "/priv/$coursedom/$coursenum/"; + ($crsauname,$crsaudom,$crshome) = &Apache::lonnet::constructaccess($crsauthorurl); + if (($crsauname eq $coursenum) && ($crsaudom eq $coursedom)) { + my @ids=&Apache::lonnet::current_machine_ids(); + my $linkurl; + if (grep(/^\Q$crshome\E$/,@ids)) { + $linkurl = $crsauthorurl; + } else { + my $jscall = &Apache::lonhtmlcommon::jump_to_editres($crsauthorurl,$crshome,1); + if ($jscall) { + $linkurl = 'javascript:'.$jscall; + } + } + if ((ref($menu[0]) eq 'HASH') && (ref($menu[0]->{'items'}) eq 'ARRAY') && ($linkurl)) { + push(@{$menu[0]->{items}}, + { linktext => $lt{'ca'}, + url => $linkurl, + permission => 'F', + help => 'Docs_Course_Authorspace', + icon => 'impcrsau.png', + linktitle => $lt{'ca'}, + }); + } + } + } + push(@menu, { categorytitle=>'Export', items =>[ { linktext => $lt{'imse'}, @@ -5249,12 +8714,25 @@ sub generate_admin_menu { { linktext => $lt{'dcd'}, url => $dumpurl, permission => $candump, - #help => '', + help => 'Docs_Dump_Course_Docs', icon => 'dump.png', linktitle => $lt{'dcd'}, }, ] }); + if (($crsauname eq $coursenum) && ($crsaudom eq $coursedom)) { + if ((ref($menu[1]) eq 'HASH') && (ref($menu[1]->{'items'}) eq 'ARRAY')) { + push(@{$menu[1]->{items}}, + { linktext => $lt{'cpc'}, + url => $exportcrsurl, + permission => 'F', + help => 'Docs_Export_Course_Author', + icon => 'res.png', + linktitle => $lt{'cpc'}, + }); + } + } + } return '
    '."\n". ''."\n". &Apache::lonhtmlcommon::generate_menu(@menu)."\n". @@ -5263,15 +8741,16 @@ sub generate_admin_menu { sub generate_edit_table { my ($tid,$orderhash_ref,$to_show,$iconpath,$jumpto,$readfile, - $need_save,$copyfolder) = @_; + $need_save,$copyfolder,$canedit) = @_; return unless(ref($orderhash_ref) eq 'HASH'); my %orderhash = %{$orderhash_ref}; - my $form; - my $activetab; - my $active; - if (($env{'form.active'} ne '') && ($env{'form.active'} ne 'aa')) { + my ($form, $activetab, $active, $disabled); + if (($env{'form.active'} ne '') && ($env{'form.active'} ne '00')) { $activetab = $env{'form.active'}; } + unless ($canedit) { + $disabled = ' disabled="disabled"'; + } my $backicon = $iconpath.'clickhere.gif'; my $backtext = &mt('Exit Editor'); $form = '
    '. @@ -5304,7 +8783,7 @@ sub generate_edit_table { $form .= '
  • '.&mt(${$orderhash{$name}}[0]).'
  • '."\n"; } else { - $form .= '
  • '.${$orderhash{$name}}[1].'
  • '."\n"; + $form .= '
  • '.${$orderhash{$name}}[1].'
  • '."\n"; } } @@ -5335,7 +8814,7 @@ sub generate_edit_table { - +
    END @@ -5361,8 +8840,9 @@ END } sub editing_js { - my ($udom,$uname,$supplementalflag) = @_; - my %lt = &Apache::lonlocal::texthash( + my ($udom,$uname,$supplementalflag,$coursedom,$coursenum,$posslti, + $londocroot,$canedit,$hostname,$navmapref) = @_; + my %js_lt = &Apache::lonlocal::texthash( p_mnf => 'Name of New Folder', t_mnf => 'New Folder', p_mnp => 'Name of New Page', @@ -5372,23 +8852,28 @@ sub editing_js { p_msb => 'Title for the Problem', p_mdb => 'Title for the Drop Box', p_mbb => 'Title for the Discussion Board', - p_mwp => 'Title for Web Page', + p_mwp => 'Title for Web Page', + p_mnr => 'Title for the Resource', p_mab => "Enter user:domain for User's Personal Information Page", p_mab2 => 'Personal Information Page of ', p_mab_alrt1 => 'Not a valid user:domain', p_mab_alrt2 => 'Please enter both user and domain in the format user:domain', p_chn => 'New Title', p_rmr1 => 'WARNING: Removing a resource makes associated grades and scores inaccessible!', - p_rmr2a => 'Remove[_99]', - p_rmr2b => '?[_99]', - p_rmr3a => 'Remove those [_2]', - p_rmr3b => 'items?[_2]', + p_rmr2a => 'Remove', + p_rmr2b => '?', + p_rmr3a => 'Remove those', + p_rmr3b => 'items?', + p_rmr4 => 'WARNING: Removing a resource uploaded to a course cannot be undone via "Undo Delete".', + p_rmr5 => 'Push "Cancel" and then use "Cut" instead if you might need to undo this change.', p_ctr1a => 'WARNING: Cutting a resource makes associated grades and scores inaccessible!', p_ctr1b => 'Grades remain inaccessible if resource is pasted into another folder.', - p_ctr2a => 'Cut[_98]', - p_ctr2b => '?[_98]', - p_ctr3a => 'Cut those[_2]', - p_ctr3b => 'items?[_2]', + p_ctr2a => 'Cut', + p_ctr2b => '?', + p_ctr3a => 'Cut those', + p_ctr3b => 'items?', + setal => 'Enter a (unique) alias', + delal => 'Are you sure you want to eliminate the alias?', rpck => 'Enter number to pick (e.g., 3)', imsfile => 'You must choose an IMS package for import', imscms => 'You must select which Course Management System was the source of the IMS package', @@ -5399,19 +8884,26 @@ sub editing_js { noor => 'No actions selected or changes to settings specified.', noch => 'No changes to settings specified.', noac => 'No actions selected.', + nofi => 'No file selected', + tinc => 'Title in course', + sunm => 'Sub-directory name', + edri => 'Editing rights unavailable for your current role.', + sele => 'Select', + swit => 'Switch server required', ); - + &js_escape(\%js_lt); my $crstype = &Apache::loncommon::course_type(); my $docs_folderpath = &HTML::Entities::encode($env{'environment.internal.'.$env{'request.course.id'}.'.docs_folderpath.folderpath'},'<>&"'); my $main_container_page; if (&HTML::Entities::decode($env{'environment.internal.'.$env{'request.course.id'}.'.docs_folderpath.folderpath'}) =~ /\:1$/) { $main_container_page = 1; } - my $toplevelmain = - &escape(&mt('Main Content').':::::'); - my $toplevelsupp = &supplemental_base(); - my $backtourl; + my $toplevelmain = &escape(&default_folderpath($coursenum,$coursedom,$navmapref)); + my $toplevelsupp = &supplemental_base(); + my $showfile_js = &Apache::loncommon::show_crsfiles_js(); + my @ids=&Apache::lonnet::current_machine_ids(); + my $machines_str = "'".join("','",@ids)."'"; if ($env{'docs.exit.'.$env{'request.course.id'}} =~ /^direct_(.+)$/) { my $caller = $1; if ($caller =~ /^supplemental/) { @@ -5420,8 +8912,47 @@ sub editing_js { my ($map,$id,$res)=&Apache::lonnet::decode_symb($caller); $res = &Apache::lonnet::clutter($res); if (&Apache::lonnet::is_on_map($res)) { - $backtourl = &HTML::Entities::encode(&Apache::lonnet::clutter($res),'<>&"').'?symb='. - &HTML::Entities::encode($caller,'<>&"'); + my ($url,$anchor); + if ($res =~ /^([^#]+)#([^#]+)$/) { + $url = $1; + $anchor = $2; + if (($caller =~ m{^([^#]+)\Q#$anchor\E$})) { + $caller = $1.&escape('#').$anchor; + } + } else { + $url = $res; + } + $backtourl = &HTML::Entities::encode(&Apache::lonnet::clutter($url),'<>&"'); + if ($backtourl =~ m{^\Q/uploaded/$coursedom/$coursenum/\Edefault_\d+\.sequence$}) { + $backtourl .= '?navmap=1'; + } else { + $backtourl .= '?symb='. + &HTML::Entities::encode($caller,'<>&"'); + } + if ($backtourl =~ m{^\Q/public/$coursedom/$coursenum/syllabus\E}) { + if (($ENV{'SERVER_PORT'} == 443) && + ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) { + unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) { + if ($hostname ne '') { + $backtourl = 'http://'.$hostname.$backtourl; + } + $backtourl .= (($backtourl =~ /\?/) ? '&':'?').'usehttp=1'; + } + } + } elsif ($backtourl =~ m{^/adm/wrapper/ext/(?!https:)}) { + if (($ENV{'SERVER_PORT'} == 443) && ($hostname ne '')) { + unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) { + if ($hostname ne '') { + $backtourl = 'http://'.$hostname.$backtourl; + } + $backtourl .= (($backtourl =~ /\?/) ? '&':'?').'usehttp=1'; + } + } + } + if ($anchor ne '') { + $backtourl .= '#'.&HTML::Entities::encode($anchor,'<>&"'); + } + $backtourl = &Apache::loncommon::escape_single($backtourl); } else { $backtourl = '/adm/navmaps'; } @@ -5429,56 +8960,71 @@ sub editing_js { } elsif ($env{'docs.exit.'.$env{'request.course.id'}} eq '/adm/menu') { $backtourl = '/adm/menu'; } elsif ($supplementalflag) { - $backtourl = '/adm/supplemental'; + if (($env{'request.role.adv'}) || + (&Apache::lonnet::has_unhidden_suppfiles($coursenum,$coursedom))) { + $backtourl = '/adm/supplemental'; + } else { + $backtourl = '/adm/navmaps'; + } } else { $backtourl = '/adm/navmaps'; } - my $fieldsets = "'ext','doc'"; + my $fieldsets = "'doc'"; unless ($main_container_page) { $fieldsets .=",'ims'"; } + my $extfieldsets = "'ext'"; + if ($posslti) { + $extfieldsets .= ",'tool'"; + } if ($supplementalflag) { - $fieldsets = "'suppext','suppdoc'"; + $fieldsets = "'suppdoc'"; + $extfieldsets = "'suppext'"; + if ($posslti) { + $extfieldsets .= ",'supptool'"; + } } - return <=0) { + for (i = numfiles; i >= 0; i--) { + selelem.remove(i); + } + } + if ((location == '') || (location == null) || (location == 'undefined')) { + if (selelem.options.length == 0) { + selelem.options[selelem.options.length] = new Option('',''); + selelem.selectedIndex = 0; + } + if (document.getElementById('newstdproblem')) { + document.getElementById('newstdproblem').style.display = 'none'; + } + return; + } + var machineIds = new Array($machines_str); + var athome = 0; + var role = location; + if ((location == 'author') || (location == 'course')) { + if (document.getElementById('rolehome_'+location)) { + var currhome = document.getElementById('rolehome_'+location).value; + if ((currhome != '') && (currhome != null) && (currhome != 'undefined')) { + if (machineIds.includes(currhome)) { + athome = 1; + } + } + } + } else { + const roleinfo = location.split('___'); + role = encodeURIComponent(roleinfo[0]+'./'+roleinfo[1]); + if (document.getElementById('rolehome_coauthor_'+roleinfo[1]+'_'+roleinfo[0])) { + var currhome = document.getElementById('rolehome_coauthor_'+roleinfo[1]+'_'+roleinfo[0]).value; + if ((currhome != '') && (currhome != null) && (currhome != 'undefined')) { + if (machineIds.includes(currhome)) { + athome = 1; + } + } + } + } + var templateradio = document.courseresform.elements['newresusetemp']; + if (athome) { + if (document.getElementById('stdprobswitch')) { + document.getElementById('stdprobswitch').style.display = 'none'; + } + if (document.getElementById('newstdproblem')) { + document.getElementById('newstdproblem').style.display = 'none'; + } + var canedit = '$canedit'; + if (canedit) { + if (templateradio.length > 1) { + for (var i=0; i 1) { + selelem.options[selelem.options.length] = new Option('$js_lt{sele}',''); + } + } + if (len) { + var j; + for (j = 0; j < len; j++) { + selelem.options[selelem.options.length] = new Option(data.dirs[j],data.dirs[j]); + } + selelem.selectedIndex = 0; + if (len == 1) { + toggleCrsResTitle(); + } + } + } + } + } + http.send(params); + } else { + selelem.options[selelem.options.length] = new Option('$js_lt{swit}','switch'); + selelem.selectedIndex = 0; + if (document.getElementById('stdprobswitch')) { + document.getElementById('stdprobswitch').style.display = 'block'; + } + if (document.getElementById('newstdproblem')) { + document.getElementById('newstdproblem').style.display = 'none'; + } + if (templateradio.length > 1) { + for (var i=0; i$lt{'more'}'; + document.getElementById('more'+caller).innerHTML = '  $js_lt{'more'}'; } else { document.getElementById('more'+caller).innerHTML = ''; } @@ -5991,10 +10085,10 @@ function togglePick(caller,value) { function toggleCheckUncheck(caller,more) { if (more == 1) { - document.getElementById('more'+caller).innerHTML = '  $lt{'less'}'; + document.getElementById('more'+caller).innerHTML = '  $js_lt{'less'}'; document.getElementById('allfields'+caller).style.display='block'; } else { - document.getElementById('more'+caller).innerHTML = '  $lt{'more'}'; + document.getElementById('more'+caller).innerHTML = '  $js_lt{'more'}'; document.getElementById('allfields'+caller).style.display='none'; } resize_scrollbox('contentscroll','1','1'); @@ -6036,6 +10130,7 @@ function checkSubmits() { var doactions = multiActions(); var cutwarnings = 0; var remwarnings = 0; + var removalinfo = 0; if (doactions == 1) { var remidxlist = document.cumulativeactions.allremoveidx.value; if ((remidxlist != '') && (remidxlist != null)) { @@ -6050,6 +10145,11 @@ function checkSubmits() { remwarnings ++; } } + if (document.getElementById('confirm_removal_'+remidxs[i])) { + if (document.getElementById('confirm_removal_'+remidxs[i]).value == 1) { + removalinfo ++; + } + } } } } @@ -6148,14 +10248,19 @@ function checkSubmits() { } if (doactions == 1) { if (numchanges > 0) { - if ((cutwarnings > 0) || (remwarnings > 0)) { + if ((cutwarnings > 0) || (remwarnings > 0) || (removalinfo > 0)) { if (remwarnings > 0) { - if (!confirm('$lt{"p_rmr1"}\\n\\n$lt{"p_rmr3a"} '+remwarnings+' $lt{"p_rmr3b"}')) { + if (!confirm('$js_lt{"p_rmr1"}\\n\\n$js_lt{"p_rmr3a"} '+remwarnings+' $js_lt{"p_rmr3b"}')) { + return false; + } + } + if (removalinfo > 0) { + if (!confirm('$js_lt{"p_rmr4"}\\n$js_lt{"p_rmr5"}\\n\\n$js_lt{"p_rmr3a"} '+removalinfo+' $js_lt{"p_rmr3b"}')) { return false; } } if (cutwarnings > 0) { - if (!confirm('$lt{"p_ctr1a"}\\n$lt{"p_ctr1b"}\\n\\n$lt{"p_ctr3a"} '+cutwarnings+' $lt{"p_ctr3b"}')) { + if (!confirm('$js_lt{"p_ctr1a"}\\n$js_lt{"p_ctr1b"}\\n\\n$js_lt{"p_ctr3a"} '+cutwarnings+' $js_lt{"p_ctr3b"}')) { return false; } } @@ -6171,12 +10276,12 @@ function checkSubmits() { } } if ((dosettings == 1) && (doactions == 1)) { - alert("$lt{'noor'}"); + alert("$js_lt{'noor'}"); } else { if (dosettings == 1) { - alert("$lt{'noch'}"); + alert("$js_lt{'noch'}"); } else { - alert("$lt{'noac'}"); + alert("$js_lt{'noac'}"); } } return false; @@ -6262,7 +10367,34 @@ function setBoxes(value) { return; } -ENDNEWSCRIPT +function validImportCrsRes() { + var path = document.crsresimportform.coursepath.options[document.crsresimportform.coursepath.selectedIndex].value; + var fname = document.crsresimportform.coursefile.options[document.crsresimportform.coursefile.selectedIndex].value; + if ((fname == '') || (fname == null)) { + alert("$js_lt{'nofi'}"); + return false; + } + var url = '/res/$coursedom/$coursenum/'; + if (path && path != '/') { + url += path+'/'; + } + if (fname != '') { + url += fname; + } + var title = document.crsresimportform.crsrestitle.value; + document.crsresimportform.importdetail.value=encodeURIComponent(title)+'='+encodeURIComponent(url); + return true; +} + +function validateNewRes(caller) { + if (caller == 'single') { + var role = document.courseresform.authorrole.options[document.courseresform.authorrole.selectedIndex].value; + var authorpath = document.courseresform.authorpath.options[document.courseresform.authorpath.selectedIndex].value; + var resname = document.courseresform.newresourcename.value; + } +} + +ENDSCRIPT } sub history_tab_js { @@ -6291,13 +10423,19 @@ ENDINJECT sub dump_switchserver_js { my @hosts = @_; - my %lt = &Apache::lonlocal::texthash( - dump => 'Dumping to Authoring Space requires switching server.', + my %js_lt = &Apache::lonlocal::texthash( + dump => 'Copying content to Authoring Space requires switching server.', swit => 'Switch server?', - duco => 'Dump content to Authoring Space', + ); + my %html_js_lt = &Apache::lonlocal::texthash( + swit => 'Switch server?', + duco => 'Copying uploaded content to Authoring Space', yone => 'You need to switch to a server housing an Authoring Space for which you are author or co-author.', chos => 'Choose server', ); + &js_escape(\%js_lt); + &html_escape(\%html_js_lt); + &js_escape(\%html_js_lt); my $role = $env{'request.role'}; my $js = <<"ENDSWJS";