--- loncom/interface/loncommon.pm 2012/03/31 23:10:39 1.1062 +++ loncom/interface/loncommon.pm 2012/04/25 21:22:01 1.1073 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.1062 2012/03/31 23:10:39 raeburn Exp $ +# $Id: loncommon.pm,v 1.1073 2012/04/25 21:22:01 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -1374,7 +1374,7 @@ function helpMenu(target) { return; } function writeHelp(caller) { - caller.document.writeln('$start_page $end_page') + caller.document.writeln('$start_page\\n\\n\\n$end_page') caller.document.close() caller.focus() } @@ -3129,12 +3129,12 @@ sub noteswrapper { # ------------------------------------------------------------- Aboutme Wrapper sub aboutmewrapper { - my ($link,$username,$domain,$target)=@_; + my ($link,$username,$domain,$target,$class)=@_; if (!defined($username) && !defined($domain)) { return; } return ''.$link.''; + ($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.''; } # ------------------------------------------------------------ Syllabus Wrapper @@ -3990,9 +3990,7 @@ sub findallcourses { $udom = $env{'user.domain'}; } if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) { - my $extra = &Apache::lonnet::freeze_escape({'skipcheck' => 1}); - my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname,'.',undef, - $extra); + my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname); if (!%roles) { %roles = ( cc => 1, @@ -4406,8 +4404,9 @@ END_MYBLOCK my $text = &mt('Communication Blocked'); if ($activity eq 'docs') { $text = &mt('Content Access Blocked'); + } elsif ($activity eq 'printout') { + $text = &mt('Printing Blocked'); } - $output .= <<"END_BLOCK";
'; @@ -5013,7 +5013,7 @@ sub bodytag { sub dc_courseid_toggle { my ($dc_info) = @_; return ' '. - ''. + ''. &mt('(More ...)').''. '
'.$dc_info.'
'; } @@ -6915,6 +6915,8 @@ sub headtag { ''. &font_settings(); + my $inhibitprint = &print_suppression(); + if (!$args->{'frameset'}) { $result .= &Apache::lonhtmlcommon::htmlareaheaders(); } @@ -6960,6 +6962,7 @@ ADDMETA if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); } $result .= ' LON-CAPA '.$title.'' .'' + .$inhibitprint .$head_extra; return $result.''; } @@ -6985,6 +6988,82 @@ sub font_settings { =pod +=item * &print_suppression() + +In course context returns css which causes the body to be blank when media="print", +if printout generation is unavailable for the current resource. + +This could be because: + +(a) printstartdate is in the future + +(b) printenddate is in the past + +(c) there is an active exam block with "printout" +functionality blocked + +Users with pav, pfo or evb privileges are exempt. + +Inputs: none + +=cut + + +sub print_suppression { + my $noprint; + if ($env{'request.course.id'}) { + my $scope = $env{'request.course.id'}; + if ((&Apache::lonnet::allowed('pav',$scope)) || + (&Apache::lonnet::allowed('pfo',$scope))) { + return; + } + if ($env{'request.course.sec'} ne '') { + $scope .= "/$env{'request.course.sec'}"; + if ((&Apache::lonnet::allowed('pav',$scope)) || + (&Apache::lonnet::allowed('pfo',$scope))) { + return; + } + } + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $blocked = &blocking_status('printout',$cnum,$cdom); + if ($blocked) { + my $checkrole = "cm./$cdom/$cnum"; + if ($env{'request.course.sec'} ne '') { + $checkrole .= "/$env{'request.course.sec'}"; + } + unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) && + ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) { + $noprint = 1; + } + } + unless ($noprint) { + my $symb = &Apache::lonnet::symbread(); + if ($symb ne '') { + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + if (!$res->resprintable()) { + $noprint = 1; + } + } + } + } + } + if ($noprint) { + return <<"ENDSTYLE"; + +ENDSTYLE + } + } + return; +} + +=pod + =item * &xml_begin() Returns the needed doctype and @@ -7712,8 +7791,7 @@ role status: active, previous or future. sub check_user_status { my ($udom,$uname,$cdom,$crs,$role,$sec) = @_; - my $extra = &Apache::lonnet::freeze_escape({'skipcheck' => 1}); - my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname,'.',undef,$extra); + my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname); my @uroles = keys %userinfo; my $srchstr; my $active_chk = 'none'; @@ -9084,13 +9162,20 @@ sub get_env_multiple { sub ask_for_embedded_content { my ($actionurl,$state,$allfiles,$codebase,$args)=@_; - my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges); - my $num = 0; + my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges, + %currsubfile,%unused); + my $counter = 0; + my $numnew = 0; my $numremref = 0; my $numinvalid = 0; my $numpathchg = 0; my $numexisting = 0; - my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath); + my $numunused = 0; + my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum, + $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path); + my $heading = &mt('Upload embedded files'); + my $buttontext = &mt('Upload'); + if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { my $current_path='/'; if ($env{'form.currentpath'}) { @@ -9118,8 +9203,24 @@ sub ask_for_embedded_content { } } elsif ($actionurl eq '/adm/coursedocs') { if (ref($args) eq 'HASH') { - $url = $args->{'docs_url'}; - $toplevel = $url; + $url = $args->{'docs_url'}; + $toplevel = $url; + } + } elsif ($actionurl eq '/adm/dependencies') { + if ($env{'request.course.id'} ne '') { + $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + if (ref($args) eq 'HASH') { + $url = $args->{'docs_url'}; + $title = $args->{'docs_title'}; + $toplevel = "/$url"; + ($path) = + ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/}); + $fileloc = &Apache::lonnet::filelocation('',$toplevel); + $fileloc =~ s{^/}{}; + ($filename) = ($fileloc =~ m{.+/([^/]+)$}); + $heading = &mt('Status of dependencies in [_1]',"$title ($filename)"); + } } } my $now = time(); @@ -9158,25 +9259,44 @@ sub ask_for_embedded_content { } } } + my $dirptr = 16384; foreach my $path (keys(%subdependencies)) { - my %currsubfile; + $currsubfile{$path} = {}; if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { my ($sublistref,$listerror) = &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath); if (ref($sublistref) eq 'ARRAY') { foreach my $line (@{$sublistref}) { my ($file_name,$rest) = split(/\&/,$line,2); - $currsubfile{$file_name} = 1; + $currsubfile{$path}{$file_name} = 1; } } } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) { if (opendir(my $dir,$url.'/'.$path)) { my @subdir_list = grep(!/^\./,readdir($dir)); - map {$currsubfile{$_} = 1;} @subdir_list; + map {$currsubfile{$path}{$_} = 1;} @subdir_list; + } + } elsif ($actionurl eq '/adm/dependencies') { + if ($env{'request.course.id'} ne '') { + my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$}); + if ($dir ne '') { + my ($sublistref,$listerror) = + &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/'); + if (ref($sublistref) eq 'ARRAY') { + foreach my $line (@{$sublistref}) { + my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size, + undef,$mtime)=split(/\&/,$line,12); + unless (($testdir&$dirptr) || + ($file_name =~ /^\.\.?$/)) { + $currsubfile{$path}{$file_name} = [$size,$mtime]; + } + } + } + } } } foreach my $file (keys(%{$subdependencies{$path}})) { - if ($currsubfile{$file}) { + if (exists($currsubfile{$path}{$file})) { my $item = $path.'/'.$file; unless ($mapping{$item} eq $item) { $pathchanges{$item} = 1; @@ -9187,6 +9307,17 @@ sub ask_for_embedded_content { $newfiles{$path.'/'.$file} = 1; } } + if ($actionurl eq '/adm/dependencies') { + foreach my $path (keys(%currsubfile)) { + if (ref($currsubfile{$path}) eq 'HASH') { + foreach my $file (keys(%{$currsubfile{$path}})) { + unless ($subdependencies{$path}{$file}) { + $unused{$path.'/'.$file} = 1; + } + } + } + } + } } my %currfile; if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) { @@ -9203,9 +9334,27 @@ sub ask_for_embedded_content { my @dir_list = grep(!/^\./,readdir($dir)); map {$currfile{$_} = 1;} @dir_list; } + } elsif ($actionurl eq '/adm/dependencies') { + if ($env{'request.course.id'} ne '') { + my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$}); + if ($dir ne '') { + my ($dirlistref,$listerror) = + &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/'); + if (ref($dirlistref) eq 'ARRAY') { + foreach my $line (@{$dirlistref}) { + my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef, + $size,undef,$mtime)=split(/\&/,$line,12); + unless (($testdir&$dirptr) || + ($file_name =~ /^\.\.?$/)) { + $currfile{$file_name} = [$size,$mtime]; + } + } + } + } + } } foreach my $file (keys(%dependencies)) { - if ($currfile{$file}) { + if (exists($currfile{$file})) { unless ($mapping{$file} eq $file) { $pathchanges{$file} = 1; } @@ -9215,15 +9364,25 @@ sub ask_for_embedded_content { $newfiles{$file} = 1; } } + foreach my $file (keys(%currfile)) { + unless (($file eq $filename) || + ($file eq $filename.'.bak') || + ($dependencies{$file})) { + $unused{$file} = 1; + } + } foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) { + if ($actionurl eq '/adm/dependencies') { + next if ($embed_file =~ m{^\w+://}); + } $upload_output .= &start_data_table_row(). - ''.$embed_file.''; + ' '. + ''.$embed_file.''; unless ($mapping{$embed_file} eq $embed_file) { $upload_output .= '
'.&mt('changed from: [_1]',$mapping{$embed_file}).''; } $upload_output .= ''; - if ($args->{'ignore_remote_references'} - && $embed_file =~ m{^\w+://}) { + if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) { $upload_output.=''.&mt("URL points to other server.").''; $numremref++; } elsif ($args->{'error_on_invalid_names'} @@ -9232,24 +9391,84 @@ sub ask_for_embedded_content { $upload_output.=''.&mt('Invalid characters').''; $numinvalid++; } else { - $upload_output .= &embedded_file_element('upload_embedded',$num, + $upload_output .= &embedded_file_element('upload_embedded',$counter, $embed_file,\%mapping, - $allfiles,$codebase); - $num++; + $allfiles,$codebase,'upload'); + $counter ++; + $numnew ++; } $upload_output .= ''.&Apache::loncommon::end_data_table_row()."\n"; } foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) { - $upload_output .= &start_data_table_row(). - ''.$embed_file.''. - ''.&mt('Already exists').''. - &Apache::loncommon::end_data_table_row()."\n"; + if ($actionurl eq '/adm/dependencies') { + my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file); + $modify_output .= &start_data_table_row(). + ''. + ''. + ' '.$embed_file.''. + ''.$size.''. + ''.$mtime.''. + ''. + ''. + &end_data_table_row()."\n"; + $counter ++; + } else { + $upload_output .= &start_data_table_row(). + ''.$embed_file.''; + ''.&mt('Already exists').''. + &Apache::loncommon::end_data_table_row()."\n"; + } + } + my $delidx = $counter; + foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) { + my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile); + $delete_output .= &start_data_table_row(). + ''. + ' '.$oldfile.''. + ''.$size.''. + ''.$mtime.''. + ''. + &embedded_file_element('upload_embedded',$delidx, + $oldfile,\%mapping,$allfiles, + $codebase,'delete').''. + &end_data_table_row()."\n"; + $numunused ++; + $delidx ++; } if ($upload_output) { $upload_output = &start_data_table(). $upload_output. &end_data_table()."\n"; } + if ($modify_output) { + $modify_output = &start_data_table(). + &start_data_table_header_row(). + ''.&mt('File').''. + ''.&mt('Size (KB)').''. + ''.&mt('Modified').''. + ''.&mt('Upload replacement?').''. + &end_data_table_header_row(). + $modify_output. + &end_data_table()."\n"; + } + if ($delete_output) { + $delete_output = &start_data_table(). + &start_data_table_header_row(). + ''.&mt('File').''. + ''.&mt('Size (KB)').''. + ''.&mt('Modified').''. + ''.&mt('Delete?').''. + &end_data_table_header_row(). + $delete_output. + &end_data_table()."\n"; + } my $applies = 0; if ($numremref) { $applies ++; @@ -9260,15 +9479,37 @@ sub ask_for_embedded_content { if ($numexisting) { $applies ++; } - if ($num) { + if ($counter || $numunused) { $output = '
'."\n". - $state. - '

'.&mt('Upload embedded files'). - ':

'.$upload_output.'
'."\n". - ''."\n"; - if ($actionurl eq '') { + $state.'

'.$heading.'

'; + if ($actionurl eq '/adm/dependencies') { + if ($numnew) { + $output .= '

'.&mt('Missing dependencies').'

'. + '

'.&mt('The following files need to be uploaded.').'

'."\n". + $upload_output.'
'."\n"; + } + if ($numexisting) { + $output .= '

'.&mt('Uploaded dependencies (in use)').'

'. + '

'.&mt('Upload a new file to replace the one currently in use.').'

'."\n". + $modify_output.'
'."\n"; + $buttontext = &mt('Save changes'); + } + if ($numunused) { + $output .= '

'.&mt('Unused files').'

'. + '

'.&mt('The following uploaded files are no longer used.').'

'."\n". + $delete_output.'
'."\n"; + $buttontext = &mt('Save changes'); + } + } else { + $output .= $upload_output.'
'."\n"; + } + $output .= ''."\n"; + if ($actionurl eq '/adm/dependencies') { + $output .= ''."\n"; + } elsif ($actionurl eq '') { $output .= ''; } } elsif ($applies) { @@ -9296,13 +9537,13 @@ sub ask_for_embedded_content { $output .= $upload_output.'
'; } my ($pathchange_output,$chgcount); - $chgcount = $num; + $chgcount = $counter; if (keys(%pathchanges) > 0) { foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) { - if ($num) { + if ($counter) { $output .= &embedded_file_element('pathchange',$chgcount, $embed_file,\%mapping, - $allfiles,$codebase); + $allfiles,$codebase,'change'); } else { $pathchange_output .= &start_data_table_row(). @@ -9311,14 +9552,14 @@ sub ask_for_embedded_content { ''.$mapping{$embed_file}.''. ''.$embed_file. &embedded_file_element('pathchange',$numpathchg,$embed_file, - \%mapping,$allfiles,$codebase). + \%mapping,$allfiles,$codebase,'change'). ''.&end_data_table_row(); } $numpathchg ++; $chgcount ++; } } - if ($num) { + if ($counter) { if ($numpathchg) { $output .= ''."\n"; @@ -9328,9 +9569,10 @@ sub ask_for_embedded_content { $output .= ''."\n"; } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') { $output .= ''; + } elsif ($actionurl eq '/adm/dependencies') { + $output .= ''; } - $output .= ''."\n". - &mt('(only files for which a location has been provided will be uploaded)').'
'."\n"; + $output .= ''."\n".''."\n"; } elsif ($numpathchg) { my %pathchange = (); $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output); @@ -9338,15 +9580,15 @@ sub ask_for_embedded_content { $output .= '

'.&mt('or').'

'; } } - return ($output,$num,$numpathchg); + return ($output,$counter,$numpathchg); } sub embedded_file_element { - my ($context,$num,$embed_file,$mapping,$allfiles,$codebase) = @_; + my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_; return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') && (ref($codebase) eq 'HASH')); my $output; - if ($context eq 'upload_embedded') { + if (($context eq 'upload_embedded') && ($type ne 'delete')) { $output = ''."\n"; } $output .= ' 0) { + $showmtime = &Apache::lonlocal::locallocaltime($mtime); + } + } + return ($showsize,$showmtime); +} + +sub ask_embedded_js { + return <<"END"; + + +END +} + sub upload_embedded { my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota, $current_disk_usage,$hiddenstate,$actionurl) = @_; @@ -9431,7 +9717,6 @@ sub upload_embedded { $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).'
'; next; } - $env{'form.embedded_item_'.$i.'.filename'}=$fname; if ($context eq 'portfolio') { my $result; @@ -9488,13 +9773,15 @@ sub upload_embedded { if (!open($fh,'>'.$dest)) { &Apache::lonnet::logthis('Failed to create '.$dest); $output .= ''. - &mt('An error occurred while trying to upload [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). + &mt('An error occurred while trying to upload [_1] for embedded element [_2].', + $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). '
'; } else { if (!print $fh $env{'form.embedded_item_'.$i}) { &Apache::lonnet::logthis('Failed to write to '.$dest); $output .= ''. - &mt('An error occurred while writing the file [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). + &mt('An error occurred while writing the file [_1] for embedded element [_2].', + $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). '
'; } else { $output .= &mt('Uploaded [_1]',''. @@ -9516,15 +9803,17 @@ sub upload_embedded { } $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange); $returnflag = 'ok'; - if (keys(%pathchange) > 0) { + my $numpathchgs = scalar(keys(%pathchange)); + if ($numpathchgs > 0) { if ($context eq 'portfolio') { $output .= '

'.&mt('or').'

'; } elsif ($context eq 'testbank') { - $output .= '

'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).','','').'

'; + $output .= '

'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).', + '','').'

'; $returnflag = 'modify_orightml'; } } - return ($output.$footer,$returnflag); + return ($output.$footer,$returnflag,$numpathchgs); } sub modify_html_form { @@ -9559,7 +9848,7 @@ sub modify_html_form { ''. &end_data_table_row(); - } + } } } else { $modifyform = $pathchgtable; @@ -9570,6 +9859,9 @@ sub modify_html_form { } } if ($modifyform) { + if ($actionurl eq '/adm/dependencies') { + $hiddenstate .= ''; + } return '

'.&mt('Changes in content of HTML file required').'

'."\n". '

'.&mt('Changes need to be made to the reference(s) used for one or more of the dependencies, if your HTML file is to work correctly:').'

    '."\n". '
  1. '.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'
  2. '."\n". @@ -9598,23 +9890,55 @@ sub modify_html_refs { $container = $env{'form.container'}; } elsif ($context eq 'coursedoc') { $container = $env{'form.primaryurl'}; + } elsif ($context eq 'manage_dependencies') { + (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'}); + $container = "/$container"; } else { $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'}; } my (%allfiles,%codebase,$output,$content); my @changes = &get_env_multiple('form.namechange'); - return unless (@changes > 0); - if (($context eq 'portfolio') || ($context eq 'coursedoc')) { - return unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}); + unless (@changes > 0) { + if (wantarray) { + return ('',0,0); + } else { + return; + } + } + if (($context eq 'portfolio') || ($context eq 'coursedoc') || + ($context eq 'manage_dependencies')) { + unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) { + if (wantarray) { + return ('',0,0); + } else { + return; + } + } $content = &Apache::lonnet::getfile($container); - return if ($content eq '-1'); + if ($content eq '-1') { + if (wantarray) { + return ('',0,0); + } else { + return; + } + } } else { - return unless ($container =~ /^\Q$dir_root\E/); + unless ($container =~ /^\Q$dir_root\E/) { + if (wantarray) { + return ('',0,0); + } else { + return; + } + } if (open(my $fh,"<$container")) { $content = join('', <$fh>); close($fh); } else { - return; + if (wantarray) { + return ('',0,0); + } else { + return; + } } } my ($count,$codebasecount) = (0,0); @@ -9648,13 +9972,14 @@ sub modify_html_refs { } if ($count || $codebasecount) { my $saveresult; - if ($context eq 'portfolio' || $context eq 'coursedoc') { + if (($context eq 'portfolio') || ($context eq 'coursedoc') || + ($context eq 'manage_dependencies')) { my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult); if ($url eq $container) { my ($fname) = ($container =~ m{/([^/]+)$}); $output = '

    '.&mt('Updated [quant,_1,reference] in [_2].', $count,''. - $fname.'').'

    '; + $fname.'').'

    '; } else { $output = '

    '. &mt('Error: update failed for: [_1].', @@ -9681,7 +10006,11 @@ sub modify_html_refs { ' to modify references: '.$parse_result); } } - return $output; + if (wantarray) { + return ($output,$count,$codebasecount); + } else { + return $output; + } } sub check_for_existing { @@ -9848,43 +10177,226 @@ sub is_archive_file { } sub decompress_form { - my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements) = @_; + my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_; my %lt = &Apache::lonlocal::texthash ( this => 'This file is an archive file.', + camt => 'This file is a Camtasia archive file.', + itsc => 'Its contents are as follows:', youm => 'You may wish to extract its contents.', - camt => 'Extraction of contents is recommended for Camtasia zip files.', - perm => 'Permanently remove archive file after extraction of contents?', extr => 'Extract contents', + auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.', + proa => 'Process automatically?', yes => 'Yes', no => 'No', + fold => 'Title for folder containing movie', + movi => 'Title for page containing embedded movie', ); - my $output = '

    '.$lt{'this'}.' '.$lt{'youm'}.'
    '; + my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl); + my ($is_camtasia,$topdir,%toplevel,@paths); + my $info = &list_archive_contents($fileloc,\@paths); + if (@paths) { + foreach my $path (@paths) { + $path =~ s{^/}{}; + if ($path =~ m{^([^/]+)/$}) { + $topdir = $1; + } + if ($path =~ m{^([^/]+)/}) { + $toplevel{$1} = $path; + } else { + $toplevel{$path} = $path; + } + } + } if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) { - $output .= $lt{'camt'}; + my @camtasia = ("$topdir/","$topdir/index.html", + "$topdir/media/", + "$topdir/media/$topdir.mp4", + "$topdir/media/FirstFrame.png", + "$topdir/media/player.swf", + "$topdir/media/swfobject.js", + "$topdir/media/expressInstall.swf"); + my @diffs = &compare_arrays(\@paths,\@camtasia); + if (@diffs == 0) { + $is_camtasia = 1; + } } - $output .= '

    '; - $output .= <<"START"; -
    -
    - -START + my $output; + if ($is_camtasia) { + $output = <<"ENDCAM"; + +

    $lt{'camt'}

    +ENDCAM + } else { + $output = '

    '.$lt{'this'}; + if ($info eq '') { + $output .= ' '.$lt{'youm'}.'

    '."\n"; + } else { + $output .= ' '.$lt{'itsc'}.'

    '."\n". + '
    '.$info.'
    '; + } + } + $output .= ''."\n"; + my $duplicates; + my $num = 0; + if (ref($dirlist) eq 'ARRAY') { + foreach my $item (@{$dirlist}) { + if (ref($item) eq 'ARRAY') { + if (exists($toplevel{$item->[0]})) { + $duplicates .= + &start_data_table_row(). + ''. + ' '. + ''."\n". + ''.$item->[0].''; + if ($item->[2]) { + $duplicates .= ''.&mt('Directory').''; + } else { + $duplicates .= ''.&mt('File').''; + } + $duplicates .= ''.$item->[3].''. + ''. + &Apache::lonlocal::locallocaltime($item->[4]). + ''. + &end_data_table_row(); + $num ++; + } + } + } + } + my $itemcount; + if (@paths > 0) { + $itemcount = scalar(@paths); + } else { + $itemcount = 1; + } + if ($is_camtasia) { + $output .= $lt{'auto'}.'
    '. + ''.$lt{'proa'}.' 
    '. + '
    '. + &Apache::lonhtmlcommon::start_pick_box(). + &Apache::lonhtmlcommon::row_title($lt{'fold'}). + ''."\n". + &Apache::lonhtmlcommon::row_closure(). + &Apache::lonhtmlcommon::row_title($lt{'movi'}). + ''."\n". + &Apache::lonhtmlcommon::row_closure(1). + &Apache::lonhtmlcommon::end_pick_box(). + '
    '; + } + $output .= + ''. + ''. + "\n"; + if ($duplicates ne '') { + $output .= '

    '. + &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'
    '. + &start_data_table(). + &start_data_table_header_row(). + ''.&mt('Overwrite?').''. + ''.&mt('Name').''. + ''.&mt('Type').''. + ''.&mt('Size').''. + ''.&mt('Last modified').''. + &end_data_table_header_row(). + $duplicates. + &end_data_table(). + '

    '; + } + $output .= ''."\n"; if (ref($hiddenelements) eq 'HASH') { foreach my $hidden (sort(keys(%{$hiddenelements}))) { $output .= ''."\n"; } } $output .= <<"END"; -$lt{'perm'}  -   -
    +
    $noextract -
    END return $output; } +sub decompression_utility { + my ($program) = @_; + my @utilities = ('tar','gunzip','bunzip2','unzip'); + my $location; + if (grep(/^\Q$program\E$/,@utilities)) { + foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/', + '/usr/sbin/') { + if (-x $dir.$program) { + $location = $dir.$program; + last; + } + } + } + return $location; +} + +sub list_archive_contents { + my ($file,$pathsref) = @_; + my (@cmd,$output); + my $needsregexp; + if ($file =~ /\.zip$/) { + @cmd = (&decompression_utility('unzip'),"-l"); + $needsregexp = 1; + } elsif (($file =~ m/\.tar\.gz$/) || + ($file =~ /\.tgz$/)) { + @cmd = (&decompression_utility('tar'),"-ztf"); + } elsif ($file =~ /\.tar\.bz2$/) { + @cmd = (&decompression_utility('tar'),"-jtf"); + } elsif ($file =~ m|\.tar$|) { + @cmd = (&decompression_utility('tar'),"-tf"); + } + if (@cmd) { + undef($!); + undef($@); + if (open(my $fh,"-|", @cmd, $file)) { + while (my $line = <$fh>) { + $output .= $line; + chomp($line); + my $item; + if ($needsregexp) { + ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/); + } else { + $item = $line; + } + if ($item ne '') { + unless (grep(/^\Q$item\E$/,@{$pathsref})) { + push(@{$pathsref},$item); + } + } + } + close($fh); + } + } + return $output; +} + sub decompress_uploaded_file { my ($file,$dir) = @_; &Apache::lonnet::appenv({'cgi.file' => $file}); @@ -9914,8 +10426,6 @@ sub process_decompression { } else { my @ids=&Apache::lonnet::current_machine_ids(); my $currdir = "$dir_root/$destination"; - my ($currdirlistref,$currlisterror) = - &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1); if (grep(/^\Q$docuhome\E$/,@ids)) { $dir = &LONCAPA::propath($docudom,$docuname). "$dir_root/$destination"; @@ -9926,47 +10436,61 @@ sub process_decompression { $error = &mt('Archive file not found.'); } } - if ($dir eq '') { + my (@to_overwrite,@to_skip); + if ($env{'form.archive_overwrite_total'} > 0) { + my $total = $env{'form.archive_overwrite_total'}; + for (my $i=0; $i<$total; $i++) { + if ($env{'form.archive_overwrite_'.$i} == 1) { + push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i}); + } elsif ($env{'form.archive_overwrite_'.$i} == 0) { + push(@to_skip,$env{'form.archive_overwrite_name_'.$i}); + } + } + } + my $numskip = scalar(@to_skip); + if (($numskip > 0) && + ($numskip == $env{'form.archive_itemcount'})) { + $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.'); + } elsif ($dir eq '') { $error = &mt('Directory containing archive file unavailable.'); } elsif (!$error) { - my ($decompressed,$display) = &decompress_uploaded_file($file,$dir); + my ($decompressed,$display); + if ($numskip > 0) { + my $tempdir = time.'_'.$$.int(rand(10000)); + mkdir("$dir/$tempdir",0755); + system("mv $dir/$file $dir/$tempdir/$file"); + ($decompressed,$display) = + &decompress_uploaded_file($file,"$dir/$tempdir"); + foreach my $item (@to_skip) { + if (($item ne '') && ($item !~ /\.\./)) { + if (-f "$dir/$tempdir/$item") { + unlink("$dir/$tempdir/$item"); + } elsif (-d "$dir/$tempdir/$item") { + system("rm -rf $dir/$tempdir/$item"); + } + } + } + system("mv $dir/$tempdir/* $dir"); + rmdir("$dir/$tempdir"); + } else { + ($decompressed,$display) = + &decompress_uploaded_file($file,$dir); + } if ($decompressed eq 'ok') { - $output = &mt('Files extracted successfully from archive.').'
    '; + $output = '

    '. + &mt('Files extracted successfully from archive.'). + '

    '."\n"; my ($warning,$result,@contents); my ($newdirlistref,$newlisterror) = &Apache::lonnet::dirlist($currdir,$docudom, $docuname,1); my (%is_dir,%changes,@newitems); my $dirptr = 16384; - if (ref($currdirlistref) eq 'ARRAY') { - my @curritems; - foreach my $dir_line (@{$currdirlistref}) { - my ($item,$rest)=split(/\&/,$dir_line,2); - unless ($item =~ /\.+$/) { - push(@curritems,$item); - } - } - if (ref($newdirlistref) eq 'ARRAY') { - foreach my $dir_line (@{$newdirlistref}) { - my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,4); - unless ($item =~ /^\.+$/) { - if ($dirptr&$testdir) { - $is_dir{$item} = 1; - } - push(@newitems,$item); - } - } - my @diffs = &compare_arrays(\@curritems,\@newitems); - if (@diffs > 0) { - foreach my $item (@diffs) { - $changes{$item} = 1; - } - } - } - } elsif (ref($newdirlistref) eq 'ARRAY') { + if (ref($newdirlistref) eq 'ARRAY') { foreach my $dir_line (@{$newdirlistref}) { my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5); - unless ($item =~ /\.+$/) { + unless (($item =~ /^\.+$/) || ($item eq $file) || + ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) { push(@newitems,$item); if ($dirptr&$testdir) { $is_dir{$item} = 1; @@ -9983,8 +10507,11 @@ sub process_decompression { } } if (@contents > 0) { + my $wantform; + unless ($env{'form.autoextract_camtasia'}) { + $wantform = 1; + } my (%children,%parent,%dirorder,%titles); - my $wantform = 1; my ($count,$datatable) = &get_extracted($docudom,$docuname, $currdir,\%is_dir, \%children,\%parent, @@ -9993,10 +10520,54 @@ sub process_decompression { if ($datatable ne '') { $output .= &archive_options_form('decompressed',$datatable, $count,$hiddenelem); - my $startcount = 4; + my $startcount = 6; $output .= &archive_javascript($startcount,$count, \%titles,\%children); } + if ($env{'form.autoextract_camtasia'}) { + my %displayed; + my $total = 1; + $env{'form.archive_directory'} = []; + foreach my $i (sort { $a <=> $b } keys(%dirorder)) { + my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}}); + $path =~ s{/$}{}; + my $item; + if ($path ne '') { + $item = "$path/$titles{$i}"; + } else { + $item = $titles{$i}; + } + $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item"; + if ($item eq $contents[0]) { + push(@{$env{'form.archive_directory'}},$i); + $env{'form.archive_'.$i} = 'display'; + $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'}; + $displayed{'folder'} = $i; + } elsif ($item eq "$contents[0]/index.html") { + $env{'form.archive_'.$i} = 'display'; + $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'}; + $displayed{'web'} = $i; + } else { + if ($item eq "$contents[0]/media") { + push(@{$env{'form.archive_directory'}},$i); + } + $env{'form.archive_'.$i} = 'dependency'; + } + $total ++; + } + for (my $i=1; $i<$total; $i++) { + next if ($i == $displayed{'web'}); + next if ($i == $displayed{'folder'}); + $env{'form.archive_dependent_on_'.$i} = $displayed{'web'}; + } + $env{'form.phase'} = 'decompress_cleanup'; + $env{'form.archivedelete'} = 1; + $env{'form.archive_count'} = $total-1; + $output .= + &process_extracted_files('coursedocs',$docudom, + $docuname,$destination, + $dir_root,$hiddenelem); + } } else { $warning = &mt('No new items extracted from archive file.'); } @@ -10122,6 +10693,9 @@ sub archive_row { my $offset = 0; foreach my $action ('display','dependency','discard') { $offset ++; + if ($action ne 'display') { + $offset ++; + } $output .= ''. '