File:  [LON-CAPA] / loncom / interface / portfolio.pm
Revision 1.266: download - view: text, annotated - select for diffs
Sun Sep 18 22:33:45 2022 UTC (19 months, 2 weeks ago) by raeburn
Branches: MAIN
CVS tags: HEAD
- URLs in direct links to LON-CAPA messages in notification email, and to
  publicly accessible syllabus or portfolio files can be set to use hostname
  of portal server instead of user's homeserver as first part of URL.

# The LearningOnline Network
# portfolio browser
#
# $Id: portfolio.pm,v 1.266 2022/09/18 22:33:45 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or 
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package Apache::portfolio;
use strict;
use Apache::Constants qw(:common :http);
use Apache::loncommon;
use Apache::lonnet;
use Apache::lontexconvert;
use Apache::lonfeedback;
use Apache::lonlocal;
use Apache::lonnet;
use Apache::longroup;
use Apache::lonhtmlcommon;
use HTML::Entities;
use LONCAPA qw(:DEFAULT :match);

sub group_args {
    my $output;
    if (defined($env{'form.group'})) {
        $output .= '&group='.$env{'form.group'};
	if (defined($env{'form.ref'})) {
	    $output .= '&ref='.$env{'form.ref'};
	}
    }
    return $output;
}

sub group_form_data {
    my $output;
    if (defined($env{'form.group'})) {
	$output = '<input type="hidden" name="group" value="'.$env{'form.group'}.'" />';
	if (exists($env{'form.ref'})) {
	    $output .= '<input type="hidden" name="ref" value="'.
		$env{'form.ref'}.'" />';
	}
    }
    return $output;
} 

# receives a filename and path stub from username/userfiles/portfolio/
# returns an anchor tag consisting encoding filename and currentpath
sub make_anchor {
    my ($url, $anchor_fields, $inner_text) = @_;
    if ($$anchor_fields{'continue'} ne 'true') {$$anchor_fields{'continue'} = 'false'};
    my $anchor = '<a href="'.$url.'?';
    foreach my $field_name (keys(%$anchor_fields)) {
        $anchor .= $field_name.'='.$$anchor_fields{$field_name}.'&amp;';
    }
    $anchor =~ s/&amp;$//;
    $anchor .= &group_args();
    $anchor .= '">'.$inner_text.'</a>';
    return $anchor;
}

my $dirptr=16384;
sub display_common {
    my ($r,$url,$current_path,$is_empty,$dir_list,$can_upload,$group)=@_;
    my $namespace = &get_namespace();
    my $port_path = &get_port_path();
    if ($can_upload) {
        my $groupitem = &group_form_data();

        my $iconpath= $r->dir_config('lonIconsURL') . "/";
        my %lt=&Apache::lonlocal::texthash(
                   'upload'          => 'Upload',
                   'upload_label'    => 'Upload file to current directory',
                   'createdir'       => 'Create Subdirectory',
                   'createdir_label' => 'Create subdirectory in current directory',
                   'parse'           => 'Upload embedded images/multimedia/css/linked files if HTML file',
               );
        my $escuri = &HTML::Entities::encode($r->uri,'&<>"');
	my $help_fileupload = &Apache::loncommon::help_open_topic('Portfolio AddFiles');
	my $help_createdir = &Apache::loncommon::help_open_topic('Portfolio CreateDirectory');
        my $help_portfolio = &Apache::loncommon::help_open_topic('Portfolio About', &mt('Help on the portfolio'));
        $r->print(&display_portfolio_usage($group,$help_portfolio));
        my $parse_check;
        if (!&suppress_embed_prompt()) {
            $parse_check = <<"END";
        <br />
        <span class="LC_nobreak">
         <label>
          <input type="checkbox" name="parserflag" checked="checked" />
          $lt{'parse'}
         </label>
        </span>
END
        }

        # Find space available before uploading
        my $free_space = &free_space($group);

        # Upload File
        $r->print('<div class="LC_left_float">'
                 .'<form method="post" enctype="multipart/form-data" action="'.$escuri.'">'
                 .'<fieldset>'
                 .'<legend>'.$lt{'upload_label'}.'</legend>'
                 .$groupitem 
                 .'<input name="uploaddoc" type="file" class="LC_flUpload" />'
                 .'<input type="hidden" id="LC_free_space" value="'.$free_space.'" />'
                 .'<input type="hidden" name="currentpath" value="'.$current_path.'" />'
                 .'<input type="hidden" name="action" value="'.$env{"form.action"}.'" />'
                 .'<input type="hidden" name="symb" value="'.$env{"form.symb"}.'" />'
                 .'<input type="hidden" name="fieldname" value="'.$env{"form.fieldname"}.'" />'
                 .'<input type="hidden" name="mode" value="'.$env{"form.mode"}.'" />'
                 .'<input type="submit" name="storeupl" value="'.$lt{'upload'}.'" />'
                 .$help_fileupload
                 .$parse_check
                 .'</fieldset>'
                 .'</form>'
                 .'</div>'
        );
        # Create Subdirectory
        $r->print('<div class="LC_left_float">'
                 .'<form method="post" action="'.$escuri.'">'
                 .'<fieldset>'
                 .'<legend>'.$lt{'createdir_label'}.'</legend>'
                 .'<input name="newdir" type="text" />'.$groupitem
                 .'<input type="hidden" name="currentpath" value="'.$current_path.'" />'
                 .'<input type="hidden" name="action" value="'.$env{"form.action"}.'" />'
                 .'<input type="hidden" name="symb" value="'.$env{"form.symb"}.'" />'
                 .'<input type="hidden" name="fieldname" value="'.$env{"form.fieldname"}.'" />'
                 .'<input type="hidden" name="mode" value="'.$env{"form.mode"}.'" />'
                 .'<input type="submit" name="createdir" value="'.$lt{'createdir'}.'" />'
                 .$help_createdir
                 .'</fieldset>'
                 .'</form>'
                 .'</div>'
        );
    } # end "if can_upload"

    my @tree = split (/\//,$current_path);
    my %anchor_fields = (
        'selectfile'    => $port_path,
        'currentpath'   => '/',
        'mode'          => $env{"form.mode"},
        'symb'          => $env{"form.symb"},
        'fieldname'     => $env{"form.fieldname"},
        'continue'      => $env{"form.continue"}
    );
    $r->print('<br clear="all" />');
    $r->print('<span class="LC_current_location">'.&make_anchor($url,\%anchor_fields,$port_path).'/');
    if (@tree > 1){
        my $newCurrentPath = '/';
        for (my $i = 1; $i< @tree; $i++){
            $newCurrentPath .= $tree[$i].'/';
            my %anchor_fields = (
                'selectfile' => $tree[$i],
                'currentpath' => $newCurrentPath,
                'mode' => $env{"form.mode"},
                'symb' => $env{"form.symb"},
                'fieldname' => $env{"form.fieldname"},
                'continue' => $env{"form.continue"}
            );
            $r->print(&make_anchor($url,\%anchor_fields,$tree[$i]).'/');
        }
    }
    $r->print('</span>');
    $r->print(&Apache::loncommon::help_open_topic('Portfolio ChangeDirectory'));
    &Apache::lonhtmlcommon::store_recent($namespace,$current_path,$current_path);
    $r->print('<br /><form method="post" action="'.$url.'?mode='.$env{"form.mode"}.'&amp;fieldname='.$env{"form.fieldname"}.'&amp;symb='.$env{'form.symb'}.&group_args());
    $r->print('">'.
	      &Apache::lonhtmlcommon::select_recent($namespace,'currentpath',
						    'this.form.submit();'));
    $r->print("</form>");
}

sub display_portfolio_usage {
    my ($group,$helpitem) = @_;
    my $disk_quota = &get_quota($group);
    my $getpropath = 1;
    my $portfolio_root = &get_portfolio_root();
    my ($uname,$udom) = &get_name_dom($group);
    my $current_disk_usage =
         &Apache::lonnet::diskusage($udom,$uname,$portfolio_root,$getpropath);
    return &Apache::loncommon::head_subbox(
                     '<div style="float:right;padding-top:0;margin-top;0">'
                    .$helpitem
                    .'</div>'
                    .'<div>'
                    .&Apache::lonhtmlcommon::display_usage($current_disk_usage,
                                                           $disk_quota,'portfolio')
                    .'</div>');
}

sub display_directory_line {
    my ($r,$select_mode, $filename, $mtime, $size, $css_class,
	$line, $access_controls, $curr_access, $now, $version_flag,
	$href_location, $url, $current_path, $access_admin_text, $versions)=@_;

    my $fullpath =  &prepend_group($current_path.$filename);
    $r->print(&Apache::loncommon::start_data_table_row());
    $r->print($line); # contains first two cells of table
    my $lock_info;
    if ($version_flag) { # versioned can't be versioned, so TRUE when root file
        $r->print('<td><img alt="" src="'.&Apache::loncommon::icon($filename).'" class="LC_fileicon" /></td>');
        $r->print('<td>'.$version_flag.'</td>');
    } else { # this is a graded or handed back file
        my ($user,$domain) = &get_name_dom($env{'form.group'});
        my $permissions_hash = &Apache::lonnet::get_portfile_permissions($domain,$user);
        if (defined($$permissions_hash{$fullpath})) {
            foreach my $array_item (@{$$permissions_hash{$fullpath}}) {
                if (ref($array_item) eq 'ARRAY') {
                    if ($$array_item[-1] eq 'handback') {
                        $lock_info = 'Handback';
                    } elsif ($$array_item[-1] eq 'graded') {
                        $lock_info = 'Graded';
                    }
                 }
            }
        }
	if ($lock_info) {
	    my %anchor_fields = ('lockinfo' => $fullpath);
	    if ($versions) { # hold the folder open
	        my ($fname,$version,$extension) = &Apache::lonnet::file_name_version_ext($fullpath);
	        $fname =~ s|^/||;
	        $anchor_fields{'showversions'} = $fname.'.'.$extension;
	    }
	    $lock_info = &make_anchor(undef,\%anchor_fields,$lock_info);
	}
	$r->print('<td colspan="2">'.$lock_info.'</td>');
    }
    # $r->print('<td>'.$$version_flag{$filename}.'</td><td>');
    $r->print('<td>'.&make_anchor($href_location.$filename,undef,$filename).'</td>'); 
    $r->print('<td>'.$size.'</td>');
    $r->print('<td>'.&Apache::lonlocal::locallocaltime($mtime).'</td>');
    if ($select_mode ne 'true') {
        $r->print('<td class="'.$css_class.'">&nbsp;&nbsp;</td>'); # Display status
        $r->print('<td><span class="LC_nobreak">'
                 .&mt($curr_access).'&nbsp;&nbsp;&nbsp;'
       );
        my %anchor_fields = (
            'access' => $filename,
            'currentpath' => $current_path
        );
	$r->print(&make_anchor($url, \%anchor_fields, $access_admin_text).'</span></td>');
    } else {
        $r->print('<td class="'.$css_class.'">&nbsp;&nbsp;</td>'); # Display status
    }
    $r->print(&Apache::loncommon::end_data_table_row().$/);
}

sub display_directory {
    my ($r,$url,$current_path,$is_empty,$dir_list,$group,$can_upload,
        $can_modify,$can_delete,$can_setacl)=@_;
    my $iconpath= $r->dir_config('lonIconsURL') . "/";
    my $select_mode;
    my $checked_files;
    my $port_path = &get_port_path();
    my ($uname,$udom) = &get_name_dom($group);
    my $access_admin_text = &mt('View Status');
    if ($can_setacl) {
        $access_admin_text = &mt('View/Change Status');
    }

    my $current_permissions = &Apache::lonnet::get_portfile_permissions($udom,
                                                                        $uname);
    my %locked_files = &Apache::lonnet::get_marked_as_readonly_hash(
                                                  $current_permissions,$group);
    my %access_controls = &Apache::lonnet::get_access_controls($current_permissions,$group);
    my $now = time;
    if ($env{"form.mode"} eq 'selectfile') {
        &select_files($r,$dir_list);
        $checked_files =&Apache::lonnet::files_in_path($uname,$env{'form.currentpath'});
        $select_mode = 'true';
    }
    if ($select_mode eq 'true') {
        $r->print('<form method="post" name="checkselect" action="'.$url.'">');
        $r->print(&Apache::loncommon::start_data_table()
                 .&Apache::loncommon::start_data_table_header_row()
                 .'<th>'.&mt('Select').'</th>'
                 .'<th>&nbsp;</th>'
                 .'<th>&nbsp;</th>'
                 .'<th>'.&mt('Name').'</th>'
                 .'<th>'.&mt('Size').'</th>'
                 .'<th>'.&mt('Last Modified').'</th>'
                 .'<th>&nbsp;</th>'
                 .&Apache::loncommon::end_data_table_header_row()
        );
    } else {
        $r->print('<form method="post" action="'.$url.'">');
        $r->print(
            '<p>'
           .&Apache::loncommon::help_open_topic(
                'Portfolio FileList',
                &mt('Using the portfolio file list'))
           .'</p>'
        );
        $r->print(&Apache::loncommon::start_data_table()
                 .&Apache::loncommon::start_data_table_header_row()
                 .'<th colspan="2">'.&mt('Actions'). &Apache::loncommon::help_open_topic('Portfolio FileAction').'</th>'
                 .'<th>&nbsp;</th>'
                 .'<th>&nbsp;</th>'
                 .'<th>'.&mt('Name').&Apache::loncommon::help_open_topic('Portfolio OpenFile').'</th>'
                 .'<th>'.&mt('Size').'</th>'
                 .'<th>'.&mt('Last Modified').'</th>'
                 .'<th>&nbsp;</th>'
                 .'<th>'.&mt('Current Access Status').&Apache::loncommon::help_open_topic('Portfolio ShareFile').'</th>'
                 .&Apache::loncommon::end_data_table_header_row());
    }

    # Empty directory?
    if ($is_empty && ($current_path ne '/') && $can_delete) {
        my $cols = ($select_mode eq 'true') ? 7 : 9;
        # Empty message
        $r->print(
            &Apache::loncommon::start_data_table_row()
           .'<td colspan="'.$cols.'">'
           .'<p class="LC_info">'
           .&mt('This directory is empty.')
           .'</p>'
           .'</td>'
           .&Apache::loncommon::end_data_table_row()
           .&Apache::loncommon::end_data_table()
           .'</form>'
        );
        # Delete button
        $r->print(
            '<form method="post" action="'.$url.'">'.
            &group_form_data().
            '<input type="hidden" name="action" value="deletedir" />'.
            '<p>'.
            '<input type="submit" name="deletedir" value="'.&mt("Delete Directory").'" />'.
            '</p>'.
            '<input type="hidden" name="selectfile" value="" />'.
            '<input type="hidden" name="currentpath" value="'.$current_path.'" />'.
            '</form>'
        );
        # Directory is empty, so nothing else to display
        return;
    }

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

    my $href_location="/uploaded/$udom/$uname/$port_path".$current_path;
    my $href_edit_location="/editupload/$udom/$uname/$port_path".$current_path;
    my @dir_lines;
    my %versioned;
    if (ref($dir_list) eq 'ARRAY') { 
        foreach my $dir_line (sort 
		          { 
			      my ($afile)=split('&',$a,2);
			      my ($bfile)=split('&',$b,2);
			      return (lc($afile) cmp lc($bfile));
		          } (@{$dir_list})) {
    	    my ($filename,$dom,undef,$testdir,undef,undef,undef,undef,$size,undef,$mtime,undef,undef,undef,$obs,undef)=split(/\&/,$dir_line,16); 
    	    $filename =~ s/\s+$//;
    	    my ($fname,$version,$extension) = &Apache::lonnet::file_name_version_ext($filename);
    	    if ($version) {
	        my $fullpath = &prepend_group($current_path.$fname.'.'.$extension);
    	        push(@{ $versioned{$fullpath} },
		     [$filename,$dom,$testdir,$size,$mtime,$obs,]);
    	    } else {
    	        push(@dir_lines, [$filename,$dom,$testdir,$size,$mtime,$obs]);
    	    }
        }
    }
    my $zerobyte;
    foreach my $dir_line (@dir_lines) {
        my ($filename,$dom,$testdir,$size,$mtime,$obs) = @$dir_line;
        my ($fname,$version,$extension) = &Apache::lonnet::file_name_version_ext($filename);
    	if (($filename ne '.') && ($filename ne '..') && ($filename !~ /\.meta$/ ) && ($filename !~ /(.*)\.(\d+)\.([^\.]*)$/)) {
    	    my $version_flag;
    	    my $show_versions;
	    my $fullpath =  &prepend_group($current_path.$filename);
    	    if ($env{'form.showversions'} =~ /$filename/) {
    	        $show_versions = 'true';
    	    }
    	    if (exists($versioned{$fullpath})) {
    	        my %anchor_fields = (
    	            'selectfile' => $fullpath,
    	            'continue' => 'false',
    	            'currentpath' => $current_path,
    	        );
    	        if ($show_versions) {
    	            # Must preserve other possible showversion files
    	            my $version_remainder = $env{'form.showversions'};
    	            $version_remainder =~ s/$filename//g;    	            
    	            $anchor_fields{'showversions'} = $version_remainder;
                    $version_flag = &make_anchor('portfolio',\%anchor_fields,
                        '<img class="LC_icon" alt="'.&mt('opened folder').'" src="'.$iconpath.'folder_pointer_opened.gif" />');
    	        } else {
    	            # allow multiple files to show versioned
    	            $anchor_fields{'showversions'} = $env{'form.showversions'}.','.$filename;
                    $version_flag = &make_anchor('portfolio',\%anchor_fields,
                        '<img class="LC_icon" alt="'.&mt('closed folder').'" src="'.$iconpath.'folder_pointer_closed.gif" />');
                }
    	    } else {
    	        $version_flag = '&nbsp;';
    	    }
            if ($dirptr&$testdir) {
		my $colspan_folder='';
		my $colspan_fill='';
                if ($select_mode eq 'true'){
                    $colspan_fill=' colspan="3"';
                } else {
                    $colspan_folder=' colspan="2"';
                    $colspan_fill=' colspan="4"';
                }
		$r->print('<tr class="LC_browser_folder">');
                $r->print('<td'.$colspan_folder.'><img alt="'.&mt('closed folder').'" src="'.$iconpath.'navmap.folder.closed.gif" class="LC_fileicon" /></td>'
                         .'<td>'.&mt('Go to ...').'</td>');
                my %anchor_fields = (
                    'selectfile'    => $filename.'/',
                    'currentpath'   => $current_path.$filename.'/',
                    'mode'          => $env{"form.mode"},
                    'symb'          => $env{"form.symb"},
                    'fieldname'     => $env{"form.fieldname"},
                    'continue'      => $env{"form.continue"}
                );  
                $r->print('<td>'.$version_flag.'</td>'
                         .'<td>'.&make_anchor($url,\%anchor_fields,$filename.'/').'</td>'); 
                $r->print('<td'.$colspan_fill.'>&nbsp;</td>');
                $r->print('</tr>'); 
            } else {
		my $css_class = 'LC_browser_file';
		my $line;
                if ($select_mode eq 'true') {
                    if ($size > 0) {
                        $line='<td><input type="checkbox" name="checkfile" value="'.$filename.'"';
		        if ($$checked_files{$filename} eq 'selected') {
                            $line.=' checked="checked" ';
                        }
		        $line.=' /></td>';
                    } else {
                        $line = '<td>&nbsp;</td>';
                        $zerobyte ++;
                    }
                } else {
                    if (exists $locked_files{$fullpath}) {
                        my %anchor_fields = (
                            'lockinfo' => $fullpath
                        );
                        $line.='<td colspan="2">'.&make_anchor($url,\%anchor_fields,&mt('Locked')).'</td>';
			$css_class= 'LC_browser_file_locked';
                    } else {
                        if (!$can_modify) {
                            $line .= '<td colspan="2">';
                        } else {
                            $line .= '<td>';
                        }
                        if ($can_delete) {
                            $line .= '<input type="checkbox" name="selectfile" value="'.$filename.'" />';
                        }
                        if ($can_modify) {
                            my $cat='<img class="LC_icon" alt="'.&mt('Metadata').'" title="'.&mt('Metadata').'" src="'.&Apache::loncommon::lonhttpdurl('/res/adm/pages/catalog.png').'" />';
                            my %anchor_fields = (
                                'rename' => $filename,
                                currentpath => $current_path
                            );
                            $line .= &make_anchor($url,\%anchor_fields,&mt('Rename'));
                            $line .= '</td><td>'.&make_anchor($href_edit_location.$filename.'.meta',\%anchor_fields,$cat);
                            # '<a href="'.$href_edit_location.$filename.'.meta">'.$cat.'</a>';
                        }
                        $line .= '</td>';
                    }
                }
		my $curr_access;
		if ($select_mode ne 'true') {
		    my $pub_access = 0;
		    my $guest_access = 0;
		    my $cond_access = 0;
		    foreach my $key (sort(keys(%{$access_controls{$fullpath}}))) {
			my ($num,$scope,$end,$start) = &unpack_acc_key($key);
			if (($now > $start) && (!$end || $end > $now)) {
			    if ($scope eq 'public')  {
				$pub_access = 1;
			    } elsif ($scope eq 'guest') {
				$guest_access = 1;
			    } else {
				$cond_access = 1;
			    }
			}
		    }
		    if (!$pub_access && !$guest_access && !$cond_access) {
			$curr_access = &mt('Private');
		    } else {
			my @allaccesses; 
			if ($pub_access) {
			    push(@allaccesses,&mt('Public'));
			}
			if ($guest_access) {
			    push(@allaccesses,&mt('Passphrase-protected'));
			}
			if ($cond_access) {
			    push(@allaccesses,&mt('Conditional'));
			}
			$curr_access = join('+ ',@allaccesses);
		    }
		}
                &display_directory_line($r,$select_mode, $filename, $mtime, $size, $css_class, $line, 
                                        \%access_controls, $curr_access,$now, $version_flag, $href_location, 
                                        $url, $current_path, $access_admin_text);
		if ($show_versions) {
		    foreach my $dir_line (@{ $versioned{$fullpath} }) {
		        my ($v_filename,$dom,$testdir,$size,$mtime,$obs) =
			    @$dir_line;
                        $line = '<td colspan="2">&nbsp;</td>';
			&display_directory_line($r,$select_mode, $v_filename, $mtime, $size, 
						$css_class, $line, \%access_controls, $curr_access, $now,
						undef, $href_location, $url, $current_path, $access_admin_text, 1);
		    }
		}
            }
        }
    }
    if ($select_mode eq 'true') {
        $r->print(&Apache::loncommon::end_data_table());
        if ($zerobyte) {
            $r->print('<p class="LC_warning">'.&mt('[quant,_1,file] in list not selectable as file size is 0 bytes.',$zerobyte).'</p>');
        }
        $r->print('
            <input type="hidden" name="continue" value="true" />
            <input type="hidden" name="fieldname" value="'.$env{'form.fieldname'}.'" />
            <input type="hidden" name="symb" value="'.$env{'form.symb'}.'" />
            <input type="hidden" name="mode" value="selectfile" />
            <p>
            <input type="submit" name="submit" value="'.&mt('Select checked files, and continue selecting').'" /><br />
            <input type="button" name="doit" onclick="finishSelect();" value="'.&mt('Select checked files, and close window').'" />
            </p>
            <input type="hidden" name="currentpath" value="'.$current_path.'" />
        </form>');        
    } else {
        $r->print(&Apache::loncommon::end_data_table());
        if ($can_delete) {
            $r->print('
        <p>
        <input type="submit" name="doit" value="'.&mt('Delete Selected').'" />'.
	&Apache::loncommon::help_open_topic('Portfolio DeleteFile').'
        </p>
        <input type="hidden" name="action" value="delete" />
        <input type="hidden" name="currentpath" value="'.$current_path.'" />
        </form>'
            );
        }
    }
}

sub open_form {
    my ($r,$url)=@_;
    my @files=&Apache::loncommon::get_env_multiple('form.selectfile');
    $r->print('<form name="portform" method="post" action="'.$url.'">');
    $r->print('<input type="hidden" name="action" value="'.
	      $env{'form.action'}.'" />');
    $r->print('<input type="hidden" name="confirmed" value="1" />');
    foreach (@files) {
        $r->print('<input type="hidden" name="selectfile" value="'.
	      $_.'" />');
    }
    $r->print('<input type="hidden" name="currentpath" value="'.
	      $env{'form.currentpath'}.'" />');
}

sub close_form {
    my ($r,$url,$button_text)=@_;
    if (!defined($button_text)) {
        $button_text = {
                         'continue' => &mt('Continue'),
                         'cancel'   => &mt('Cancel'),
                       };
    }
    $r->print('<p><input type="submit" value="'.$button_text->{'continue'}.'" />');
    $r->print(&group_form_data().'</p></form>');
    $r->print('<form action="'.$url.'" method="post">
               <p>
              <input type="hidden" name="currentpath" value="'.
	      $env{'form.currentpath'}.'" />'.
	      &group_form_data());
    $r->print("\n".'   <input type="submit" value="'.$button_text->{'cancel'}.'" />
               </p></form>'); 
}

sub display_file {
    my ($path,$filename)=@_;
    my $display_file_text;
    my $file_start='<span class="LC_filename">';
    my $file_end='</span>';
    if (!defined($path)) { $path=$env{'form.currentpath'}; }
    if (!defined($filename)) { 
        $filename=$env{'form.selectfile'};
        $display_file_text = $file_start.$path.$filename.$file_end;
    } elsif (ref($filename) eq "ARRAY") {
        foreach my $file (@$filename) {
            $display_file_text .= $file_start.$path.$file.$file_end.'<br />';
        }
    } elsif (ref($filename) eq "SCALAR") {
        $display_file_text = $file_start.$path.$$filename.$file_end;
    } else {
	$display_file_text = $file_start.$path.$filename.$file_end;
    }
    return $display_file_text;
}

sub done {
    my ($linktext,$url)=@_;
    unless (defined($linktext)) {
        $linktext='Return to directory';
    }
    my %anchor_fields = (
        'showversions' => $env{'form.showversions'},
        'currentpath' => $env{'form.currentpath'},
        'fieldname' => $env{'form.fieldname'},
        'symb'      => $env{'form.symb'},
        'mode'      => $env{'form.mode'}
    );
    my $result = &Apache::lonhtmlcommon::actionbox(
                     [&make_anchor($url,\%anchor_fields,&mt($linktext))]);
    return $result;
}

sub delete {
    my ($r,$url,$group)=@_;
    my @check;
    my $file_name = $env{'form.currentpath'}.$env{'form.selectfile'};
    $file_name = &prepend_group($file_name);
    my @files=&Apache::loncommon::get_env_multiple('form.selectfile');
    my ($uname,$udom) = &get_name_dom($group);
    if (&Apache::lonnet::is_locked($file_name,$udom,$uname) eq 'true') {
        $r->print(
            '<p class="LC_warning">'
           .&mt('The file is locked and cannot be deleted.')
           .'</p>'
           .&done(undef,$url)
        );
    } else {
        if (scalar(@files)) {
            &open_form($r,$url);
            $r->print('<p>'.&mt('Delete [_1]?',&display_file(undef,\@files)).'</p>');
            &close_form($r,$url);
        } else {
            $r->print('<p class="LC_warning">'.&mt('No file was checked to delete.').'</p>');
            $r->print(&done(undef,$url));
        }
    }
} 

sub delete_confirmed {
    my ($r,$url,$group)=@_;
    my @files=&Apache::loncommon::get_env_multiple('form.selectfile');
    my $result;
    my ($uname,$udom) = &get_name_dom($group);
    my $port_path = &get_port_path();
    my $current_permissions = &Apache::lonnet::get_portfile_permissions($udom,
                                                                        $uname);
    my @msg;
    foreach my $delete_file (@files) {
        $result =
            &Apache::lonnet::removeuserfile(
                $uname,$udom,$port_path.
                $env{'form.currentpath'}.
                $delete_file);
        if ($result ne 'ok') {
            push(@msg, &Apache::lonhtmlcommon::confirm_success(
                &mt('An error occurred ([_1]) while trying to delete [_2].'
                    ,$result,&display_file(undef, $delete_file)),1));
        } else {
            push(@msg, &Apache::lonhtmlcommon::confirm_success(
                &mt('File: [_1] deleted.'
                    ,&display_file(undef,$delete_file))));
            my $file_name = $env{'form.currentpath'}.$delete_file;
            $file_name = &prepend_group($file_name);
            my %access_controls = 
                    &Apache::lonnet::get_access_controls($current_permissions,
                                                         $group,$file_name);
            if (keys(%access_controls) > 0) {
                my %changes; 
                foreach my $key (keys(%{$access_controls{$file_name}})) {
                    $changes{'delete'}{$key} = 1;
                }
                if (keys(%changes) > 0) {
                    my ($outcome,$deloutcome,$new_values,$translation) =
                    &Apache::lonnet::modify_access_controls($file_name,\%changes,
                                                            $udom,$uname);
                    if ($outcome ne 'ok') {
                        push(@msg, &Apache::lonhtmlcommon::confirm_success(
                            &mt('An error occurred ([_1]) while '.
                                'trying to delete access controls for the file.',$outcome),1));
                    } else {
                        if ($deloutcome eq 'ok') {
                            push(@msg, &mt('Access controls also deleted for the file.')); # FIXME: Does the user really need this message?
                        } else {
                            push(@msg, &Apache::lonhtmlcommon::confirm_success(
                                &mt('An error occurred ([_1]) while '.
                                    'trying to delete access controls for the file.'
                                    ,$deloutcome),1));
                        }
                    }
                }
            }
        }
    }
    $r->print(&Apache::loncommon::confirmwrapper(join('<br />',@msg)));
    $r->print(&done(undef,$url));
}

sub delete_dir {
    my ($r,$url)=@_;
    &open_form($r,$url);
     $r->print('<p>'.&mt('Delete [_1]?',&display_file()).'</p>');
    &close_form($r,$url);
} 

sub delete_dir_confirmed {
    my ($r,$url,$group)=@_;
    my $directory_name = $env{'form.currentpath'};
    $directory_name =~ s|/$||; # remove any trailing slash
    my ($uname,$udom) = &get_name_dom($group);
    my $namespace = &get_namespace();
    my $port_path = &get_port_path();
    my $result=&Apache::lonnet::removeuserfile($uname,$udom,$port_path.
					       $directory_name);
       
    if ($result ne 'ok') {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('An error occurred (dir) ([_1]) while trying to delete [_2].'
                        ,$result,$directory_name),1)));
        $r->print(&done(undef,$url));
        return;
    } else {
        # now remove from recent
        &Apache::lonhtmlcommon::remove_recent($namespace,[$directory_name.'/']);
        my @dirs = split m!/!, $directory_name;
        $directory_name='/';
        for (my $i=1; $i < (@dirs - 1); $i ++){
            $directory_name .= $dirs[$i].'/';
        }
        $env{'form.currentpath'} = $directory_name;
    }
    $r->print(
        &Apache::loncommon::confirmwrapper(
            &Apache::lonhtmlcommon::confirm_success(
                &mt('Directory successfully deleted'))));
    $r->print(&done(undef,$url));
}

sub rename {
    my ($r,$url,$group)=@_;
    my $file_name = $env{'form.currentpath'}.$env{'form.rename'};
    my ($uname,$udom) = &get_name_dom($group);
    $file_name = &prepend_group($file_name);
    if (&Apache::lonnet::is_locked($file_name,$udom,$uname) eq 'true') {
        $r->print(
            '<p class="LC_error">'
           .&mt('The file is locked and cannot be renamed.')
           .'</p>'
        );
        $r->print(&done(undef,$url));
    } else {
        &open_form($r,$url);
        $r->print('<p>'.&mt('Rename [_1] to [_2]?', &display_file()
                  , '<input name="filenewname" type="text" size="50" />').'</p>');
        &close_form($r,$url);
    }
}

sub rename_confirmed {
    my ($r,$url,$group)=@_;
    my $filenewname=&Apache::lonnet::clean_filename($env{'form.filenewname'});
    my ($uname,$udom) = &get_name_dom($group);
    my $port_path = &get_port_path();

    # Display warning in case of filename cleaning has changed the filename
    if ($filenewname ne $env{'form.filenewname'}) {
        $r->print(
            '<p><span class="LC_warning">'
           .&mt('Invalid characters')
           .'</span><br />'
           .&mt('The new filename was changed from [_1] to [_2].'
               ,'<span class="LC_filename">'.&display_file('',$env{'form.filenewname'}).'</span>'
               ,'<span class="LC_filename">'.&display_file('',$filenewname).'</span>')
           .'</p>'
        );
                
    }

    # Filename empty?
    if ($filenewname eq '') {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('Error: no valid filename was provided to rename to.'),1)));
        $r->print(&done(undef,$url));
        return;
    } 

   # Rename the file
    my $chg_access;
    my $result=
	&Apache::lonnet::renameuserfile($uname,$udom,
            $port_path.$env{'form.currentpath'}.$env{'form.selectfile'},
            $port_path.$env{'form.currentpath'}.$filenewname);
    if ($result eq 'ok') {
        $chg_access = &access_for_renamed($filenewname,$group,$udom,$uname);
    } else {      
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('An error occurred ([_1]) while trying to rename [_2] to [_3].'
                        ,$result,&display_file(),&display_file('',$filenewname))
                    ,1)));
        $r->print(&done(undef,$url));
        return;
    }
    $r->print($chg_access);
    $r->print(
        &Apache::loncommon::confirmwrapper(
            &Apache::lonhtmlcommon::confirm_success(
                &mt('File successfully renamed'))));
    $r->print(&done(undef,$url));
}

sub access_for_renamed {
    my ($filenewname,$group,$udom,$uname) = @_;
    my $oldfile = $env{'form.currentpath'}.$env{'form.selectfile'};
    $oldfile = &prepend_group($oldfile);
    my $newfile = $env{'form.currentpath'}.$filenewname;
    $newfile = &prepend_group($newfile);
    my $current_permissions =
	&Apache::lonnet::get_portfile_permissions($udom,$uname);
    my %access_controls =
	&Apache::lonnet::get_access_controls($current_permissions,
					     $group,$oldfile);
    my $chg_text;
    if (keys(%access_controls) > 0) {
        my %change_old;
        my %change_new;
        foreach my $key (keys(%{$access_controls{$oldfile}})) {
            $change_old{'delete'}{$key} = 1;
            $change_new{'activate'}{$key} = $access_controls{$oldfile}{$key};
        }
        my ($outcome,$deloutcome,$new_values,$translation) =
            &Apache::lonnet::modify_access_controls($oldfile,\%change_old,
						    $udom,$uname);
        if ($outcome ne 'ok') {
            $chg_text ='<br /><br />'.&mt("An error occurred ([_1]) while ".
                "trying to delete access control records for the old name.",$outcome).
                '</span><br />';
        } else {
            if ($deloutcome ne 'ok') {
                $chg_text = '<br /><br /><span class="LC_error"><br />'.
		    &mt("An error occurred ([_1]) while ".
			"trying to delete access control records for the old name.",$deloutcome).
			'</span><br />';
            }
        }
        ($outcome,$deloutcome,$new_values,$translation) =
            &Apache::lonnet::modify_access_controls($newfile,\%change_new,
                                                    $udom,$uname);
        if ($outcome ne 'ok') {
            $chg_text .= '<br /><br />'.
		&mt("An error occurred ([_1]) while ".
                "trying to update access control records for the new name.",$outcome).
                '</span><br />';
        }
        if ($chg_text eq '') {
            $chg_text = '<br /><br />'.&mt('Access controls updated to reflect the name change.');
        }
    }
    return $chg_text;
}

sub display_access {
    my ($r,$url,$group,$can_setacl,$port_path,$action) = @_;
    my ($uname,$udom) = &get_name_dom($group);
    my $file_name = $env{'form.currentpath'}.$env{'form.access'};
    $file_name = &prepend_group($file_name);
    my $current_permissions = &Apache::lonnet::get_portfile_permissions($udom,
                                                                        $uname);
    my %access_controls = &Apache::lonnet::get_access_controls($current_permissions,$group,$file_name);
    my $aclcount = keys(%access_controls);
    my ($header,$info);
    if ($action eq 'chgaccess') {
        my $uhome = &Apache::lonnet::homeserver($uname,$udom);
        my $prefix = &Apache::lonnet::url_prefix($r,$udom,$uhome,'web');
        $header =
            '<h2>'
            .&mt('Allowing others to retrieve file: [_1]'
                 ,'<span class="LC_filename">'
                 .$port_path.$env{'form.currentpath'}.$env{'form.access'}
                 .'</span>')
            .'</h2>';
        $info .= &mt('Access to this file by others can be set to be one or more of the following types: public, passphrase-protected or conditional.');
        $info .= '<br /><ul><li>'.&mt('Public files are available to anyone without the need for login.');
        $info .= '</li><li>'.&mt('Passphrase-protected files do not require log-in, but will require the viewer to enter the passphrase you set.');
        $info .= '</li><li>'.&explain_conditionals();
        $info .= '</li></ul>'.
                  &mt('A listing of files viewable without log-in is available at: ')."<a href=\"$prefix/adm/$udom/$uname/aboutme/portfolio\">$prefix/adm/$udom/$uname/aboutme/portfolio</a>.<br />";
        if ($group eq '') {
            $info .= &mt("For logged in users a 'Display file listing' link will also appear (when there are viewable files) on your personal information page:");
        } else {
            $info .= &mt("For logged in users a 'Display file listing' link will also appear (when there are viewable files) on the course information page:");
        }
        $info .= "<br /><a href=\"$prefix/adm/$udom/$uname/aboutme\">$prefix/adm/$udom/$uname/aboutme</a><br />";
        if ($group ne '') {
            $info .= &mt("Users with course editing rights may add a 'Group Portfolio' item using the Course Editor (Collaboration tab), to provide access to viewable group portfolio files.").'<br />';
        }
    } else {
        $header = '<h3>'.&mt('Conditional access controls for file: [_1]',$port_path.$env{'form.currentpath'}.$env{'form.access'}).'</h3>'.
                  &explain_conditionals().'<br />';
    }
    if ($can_setacl) {
        &open_form($r,$url);
        $r->print($header.$info);
	$r->print('<br />'.&Apache::loncommon::help_open_topic('Portfolio ShareFile SetAccess', &mt('Help on setting up share access')));
	$r->print(&Apache::loncommon::help_open_topic('Portfolio ShareFile ChangeSetting', &mt('Help on changing settings')));
	$r->print(&Apache::loncommon::help_open_topic('Portfolio ShareFile StopAccess', &mt('Help on removing share access')));
        &access_setting_table($r,$url,$file_name,$access_controls{$file_name},
                              $action);
        my $button_text = {
                        'continue' => &mt('Proceed'),
                        'cancel' => &mt('Return to directory'),
                      };
        &close_form($r,$url,$button_text);
    } else {
        $r->print($header);
        if ($aclcount) {  
            $r->print($info);
        }
        &view_access_settings($r,$url,$access_controls{$file_name},$aclcount);
    }
}

sub explain_conditionals {
    return
        &mt('Conditional files are accessible to users who satisfy the conditions you set.').'<br /><ul>'.
        '<li>'.&mt('Conditions can be IP-based, in which case no log-in is required').'</li>'.
        '<li>'.&mt("Conditions can also be based on a user's status, in which case the user needs an account in the LON-CAPA network, and needs to be logged in.").'<br />'."\n".
        &mt('The status-based conditions can include affiliation with a particular course or community, or a user account in a specific domain.').'<br />'."\n".
        &mt('Alternatively access can be granted to people with specific LON-CAPA usernames and domains.').'</li></ul>';
}

sub view_access_settings {
    my ($r,$url,$access_controls,$aclcount) = @_;
    my ($showstart,$showend);
    my %todisplay;
    foreach my $key (sort(keys(%{$access_controls}))) {
        my ($num,$scope,$end,$start) = &unpack_acc_key($key);
        $todisplay{$scope}{$key} = $$access_controls{$key};
    }
    if ($aclcount) {
        $r->print('<h4>'.&mt('Current access controls defined for this file:').'</h4>');
        $r->print(&Apache::loncommon::start_data_table());
        $r->print(&Apache::loncommon::start_data_table_header_row());
        $r->print('<th>'.&mt('Access control').'</th><th>'.&mt('Dates available').
                  '</th><th>'.&mt('Additional information').'</th>');
        $r->print(&Apache::loncommon::end_data_table_header_row());
        my $count = 1;
        my $chg = 'none';
        &build_access_summary($r,$count,$chg,%todisplay);
        $r->print(&Apache::loncommon::end_data_table());
    } else {
        $r->print(&mt('No access control settings currently exist for this file.').'<br />');
    }
    my %anchor_fields = (
        'currentpath' => $env{'form.currentpath'}
    );
    $r->print('<br />'.&make_anchor($url, \%anchor_fields, &mt('Return to directory')));
    return;
}

sub build_access_summary {
    my ($r,$count,$chg,%todisplay) = @_; 
    my ($showstart,$showend);
    my %scope_desc = (
                      public => 'Public',
                      guest => 'Passphrase-protected',
                      domains => 'Conditional: domain-based',
                      users => 'Conditional: user-based',
                      course => 'Conditional: course/community-based',
                      ip     => 'Conditional: IP-based',
                     );
    my @allscopes = ('public','guest','domains','users','course','ip');
    foreach my $scope (@allscopes) {
        if ((!(exists($todisplay{$scope}))) || (ref($todisplay{$scope}) ne 'HASH')) {
            next;
        }
        foreach my $key (sort(keys(%{$todisplay{$scope}}))) {
            if ($count) {
                $r->print(&Apache::loncommon::start_data_table_row());
            }
            my ($num,$scope,$end,$start) = &unpack_acc_key($key);
            my $content = $todisplay{$scope}{$key};
            if ($chg eq 'delete') {
                $showstart = &mt('Deleted');
                $showend = $showstart;
            } else {
                $showstart = &Apache::lonlocal::locallocaltime($start);
                if ($end == 0) {
                    $showend = &mt('No end date');
                } else {
                    $showend = &Apache::lonlocal::locallocaltime($end);
                }
            }
            $r->print('<td>'.&mt($scope_desc{$scope}));
            my $crstype;
            if ($scope eq 'course') {
                if ($chg ne 'delete') {
                    my $cid = $content->{'domain'}.'_'.$content->{'number'};
                    my %course_description = &Apache::lonnet::coursedescription($cid);
                    $r->print('<br />('.$course_description{'description'}.')');
                    $crstype = 'Course';
                    if ($course_description{'type'} ne '') {
                        $crstype = $course_description{'type'};
                    }
                }
            }
            $r->print('</td><td>'.&mt('Start: ').$showstart.
                  '<br />'.&mt('End: ').$showend.'</td><td>');
            if ($chg ne 'delete') {
                if ($scope eq 'guest') {
                    $r->print(&mt('Passphrase').': '.$content->{'password'});
                } elsif ($scope eq 'course') {
                    $r->print('<table width="100%"><tr>');
                    $r->print('<th>'.&mt('Roles').'</th><th>'.
                          &mt('Access').'</th><th>'.
                                          &mt('Sections').'</th>');
                    $r->print('<th>'.&mt('Groups').'</th>');
                    $r->print('</tr>');
                    foreach my $id (sort(keys(%{$content->{'roles'}}))) {
                        $r->print('<tr>');
                        foreach my $item ('role','access','section','group') {
                            $r->print('<td>');
                            if ($item eq 'role') {
                                my $role_output;
                                foreach my $role (@{$content->{'roles'}{$id}{$item}}) {
                                    if ($role eq 'all') {
                                        $role_output .= $role.',';
                                    } elsif ($role =~ /^cr/) {
                                        $role_output .= (split('/',$role))[3].',';
                                    } else {
                                        $role_output .= &Apache::lonnet::plaintext($role,$crstype).',';
                                    }
                                }
                                $role_output =~ s/,$//;
                                $r->print($role_output);
                            } else {
                                $r->print(join(',',@{$content->{'roles'}{$id}{$item}}));
                            }
                            $r->print('</td>');
                        }
			$r->print('</tr>');
                    }
		    $r->print('</table>');
                } elsif ($scope eq 'domains') {
                    $r->print(&mt('Domains: ').join(',',@{$content->{'dom'}}));
                } elsif ($scope eq 'users') {
                    my $curr_user_list = &sort_users($content->{'users'});
                    $r->print(&mt('Users: ').$curr_user_list);
                } elsif ($scope eq 'ip') {
                    my $curr_ips_list = &sort_ips($content->{'ip'});
                    $r->print(&mt('IP(s):').' '.$curr_ips_list);
                } else {
                    $r->print('&nbsp;');
                }
            } else {
                $r->print('&nbsp;');
            }
            $r->print('</td>');
            $r->print(&Apache::loncommon::end_data_table_row());
            $count ++;
        }
    }
}


sub update_access {
    my ($r,$url,$group,$port_path) = @_;
    my $totalprocessed = 0;
    my %processing;
    my %title  = (
                         'activate' => 'New control(s) added',
                         'delete'   => 'Existing control(s) deleted',
                         'update'   => 'Existing control(s) modified',
                     );
    my $changes;
    foreach my $chg (sort(keys(%title))) {     
        @{$processing{$chg}} = &Apache::loncommon::get_env_multiple('form.'.$chg);
        $totalprocessed += @{$processing{$chg}};
        foreach my $num (@{$processing{$chg}}) {
            my $scope = $env{'form.scope_'.$num};
            my ($start,$end) = &get_dates_from_form($num);
            my $newkey = $num.':'.$scope.'_'.$end.'_'.$start;
            if ($chg eq 'delete') {
                $$changes{$chg}{$newkey} = 1;
            } else {
                $$changes{$chg}{$newkey} = 
                            &build_access_record($num,$scope,$start,$end,$chg);
            }
        }
    }
    my $file_name = $env{'form.currentpath'}.$env{'form.selectfile'};
    $r->print('<h2>'.&mt('Allowing others to retrieve file: [_1]',
              '<span class="LC_filename">'.$port_path.$file_name.'</span>').'</h2>'."\n");
    $file_name = &prepend_group($file_name);
    my ($uname,$udom) = &get_name_dom($group);
    my ($errors,$outcome,$deloutcome,$new_values,$translation);
    if ($totalprocessed) {
        ($outcome,$deloutcome,$new_values,$translation) =
        &Apache::lonnet::modify_access_controls($file_name,$changes,$udom,
                                                $uname);
    }
    my $current_permissions = &Apache::lonnet::get_portfile_permissions($udom,
                                                                       $uname);
    my %access_controls = 
	&Apache::lonnet::get_access_controls($current_permissions,
					     $group,$file_name);
    if ($totalprocessed) {
        if ($outcome eq 'ok') {
            my $updated_controls = $access_controls{$file_name};
            my ($showstart,$showend);
            $r->print(&Apache::loncommon::start_data_table());
            $r->print(&Apache::loncommon::start_data_table_header_row());
            $r->print('<th>'.&mt('Type of change').'</th><th>'.
                      &mt('Access control').'</th><th>'.&mt('Dates available').
                      '</th><th>'.&mt('Additional information').'</th>');
            $r->print(&Apache::loncommon::end_data_table_header_row());
            foreach my $chg (sort(keys(%processing))) {
                if (@{$processing{$chg}} > 0) {
                    if ($chg eq 'delete') {
                        if (!($deloutcome eq 'ok')) {
                            $errors .='<span class="LC_error">'.
				&mt('A problem occurred deleting access controls: [_1]',$deloutcome).
				'</span>';
                            next;
                        }
                    }
                    my $numchgs = @{$processing{$chg}};
                    $r->print(&Apache::loncommon::start_data_table_row());
                    $r->print('<td rowspan="'.$numchgs.'">'.&mt($title{$chg}).
                              '.</td>');
                    my $count = 0;
                    my %todisplay;
                    foreach my $key (sort(keys(%{$$changes{$chg}}))) {
                        my ($num,$scope,$end,$start) = &unpack_acc_key($key);
                        my $newkey = $key;
                        if ($chg eq 'activate') {
                            $newkey =~ s/^(\d+)/$$translation{$1}/;
                        }
                        $todisplay{$scope}{$newkey} = $$updated_controls{$newkey};
                    }
                    &build_access_summary($r,$count,$chg,%todisplay);  
                }
            }
            $r->print(&Apache::loncommon::end_data_table());
        } else {
            if ((@{$processing{'activate'}} > 0) || (@{$processing{'update'}} > 0)) {
                $errors .= '<span class="LC_error">'.
		    &mt('A problem occurred saving access control settings: [_1]',$outcome).
		    '</span>';
            }
        }
        if ($errors) { 
            $r->print($errors);
        }
    }
    my $allnew = 0;
    my $totalnew = 0;
    my $status = 'new';
    my ($firstitem,$lastitem);
    my @types = ('course','domains','users','ip');
    foreach my $newitem (@types) {
        $allnew += $env{'form.new'.$newitem};
    }
    if ($allnew > 0) {
        my $now = time;
        my $then = $now + (60*60*24*180); # six months approx.
        &open_form($r,$url);
        my %showtypes = (
           course  => 'course/community',
           domains => 'domain',
           users   => 'user',
           ip      => 'IP',
        );
        foreach my $newitem (@types) {
            next if ($env{'form.new'.$newitem} <= 0);
            $r->print(
                '<p>'
               .&mt('Add new [_1]'.$showtypes{$newitem}.'-based[_2] access control for portfolio file: [_3]',
                    '<b>','</b>',
                    '<span class="LC_filename"><b>'
                   .$env{'form.currentpath'}.$env{'form.selectfile'}.'</b></span>')
               .'</p>');
            $firstitem = $totalnew;
            $lastitem = $totalnew + $env{'form.new'.$newitem};
            $totalnew = $lastitem;
            my @numbers;   
            for (my $i=$firstitem; $i<$lastitem; $i++) {
                push(@numbers,$i);
            }
            &display_access_row($r,$status,$newitem,\@numbers,
                                $access_controls{$file_name},$now,$then);
        }
        &close_form($r,$url);
    } else {
        my %anchor_fields = (
            'currentpath' => $env{'form.currentpath'},
            'access' => $env{'form.selectfile'}
        );
        my @actions;
        push(@actions, &make_anchor($url, \%anchor_fields, &mt('Display all access settings for this file')));
        delete $anchor_fields{'access'};
        push(@actions, &make_anchor($url,\%anchor_fields,&mt('Return to directory')));
        $r->print('<br />'.&Apache::lonhtmlcommon::actionbox(\@actions));
    }
    return;
}

sub build_access_record {
    my ($num,$scope,$start,$end,$chg) = @_;
    my $record = {
	type => $scope,
	time => {
	    start => $start,
	    end   => $end
	    },
	    };
		
    if ($scope eq 'guest') {	
        $record->{'password'} = $env{'form.password'};
    } elsif ($scope eq 'course') {
        $record->{'domain'} = $env{'form.crsdom_'.$num};
	$record->{'number'} = $env{'form.crsnum_'.$num};
        my @role_ids;
        my @delete_role_ids =
            &Apache::loncommon::get_env_multiple('form.delete_role_'.$num);
	my @preserves =
	    &Apache::loncommon::get_env_multiple('form.preserve_role_'.$num);
	if (@delete_role_ids) {
	    foreach my $id (@preserves) {
		if (grep {$_ = $id} (@delete_role_ids)) {
		    next;
		}
		push(@role_ids,$id); 
	    }
	} else {
	    push(@role_ids,@preserves);
	}

	my $next_id = $env{'form.add_role_'.$num};
	if ($next_id) {
	    push(@role_ids,$next_id);
	}

        foreach my $id (@role_ids) {
            my (@roles,@accesses,@sections,@groups);
            if (($id == $next_id) && ($chg eq 'update')) {
                @roles    = split(/,/,$env{'form.role_'.$num.'_'.$next_id});
                @accesses = split(/,/,$env{'form.access_'.$num.'_'.$next_id});
                @sections = split(/,/,$env{'form.section_'.$num.'_'.$next_id});
                @groups   = split(/,/,$env{'form.group_'.$num.'_'.$next_id});
            } else {
                @roles = &Apache::loncommon::get_env_multiple('form.role_'.$num.'_'.$id);
                @accesses = &Apache::loncommon::get_env_multiple('form.access_'.$num.'_'.$id);
                @sections = &Apache::loncommon::get_env_multiple('form.section_'.$num.'_'.$id);
                @groups = &Apache::loncommon::get_env_multiple('form.group_'.$num.'_'.$id);
            }
	    $record->{'roles'}{$id}{'role'}    = \@roles;
	    $record->{'roles'}{$id}{'access'}  = \@accesses;
	    $record->{'roles'}{$id}{'section'} = \@sections;
	    $record->{'roles'}{$id}{'group'}   = \@groups;
        }
    } elsif ($scope eq 'domains') {
        my @doms = &Apache::loncommon::get_env_multiple('form.dom_'.$num);
	$record->{'dom'} = \@doms;
    } elsif ($scope eq 'users') {
        my $userlist = $env{'form.users_'.$num};
        $userlist =~ s/\s+//sg;
	my %userhash = map { ($_,1) } (split(/,/,$userlist));
        foreach my $user (keys(%userhash)) {
            my ($uname,$udom) = split(/:/,$user);
	    push(@{$record->{'users'}}, {
		'uname' => $uname,
		'udom'  => $udom
		});
	}
    } elsif ($scope eq 'ip') {
        my $ipslist = $env{'form.ips_'.$num};
        $ipslist =~ s/\s+//sg;
        my %ipshash = map { ($_,1) } (split(/,/,$ipslist));
        foreach my $ip (keys(%ipshash)) {
            push(@{$record->{'ip'}},$ip);
        }
    }
    return $record;
}

sub get_dates_from_form {
    my ($id) = @_;
    my $startdate;
    my $enddate;
    $startdate = &Apache::lonhtmlcommon::get_date_from_form('startdate_'.$id);
    $enddate   = &Apache::lonhtmlcommon::get_date_from_form('enddate_'.$id);
    if ( exists ($env{'form.noend_'.$id}) ) {
        $enddate = 0;
    }
    return ($startdate,$enddate);
}

sub sort_users {
    my ($users) = @_; 
    my @curr_users = map {
	$_->{'uname'}.':'.$_->{'udom'}
    } (@{$users});
    my $curr_user_list = join(",\n",sort(@curr_users));
    return $curr_user_list;
}

sub sort_ips {
    my ($ips) = @_;
    if (ref($ips) eq 'ARRAY') {
        return join(",\n",sort(@{$ips}));
    }
}

sub access_setting_table {
    my ($r,$url,$filename,$access_controls,$action) = @_;
    my ($public,$publictext);
    $publictext ='Off';
    my ($guest,$guesttext);
    $guesttext = 'Off';
    my @courses = ();
    my @domains = ();
    my @users = ();
    my @ips = ();
    my $now = time;
    my $then = $now + (60*60*24*180); # six months approx.
    my ($num,$scope,$publicnum,$guestnum);
    my (%acl_count,%end,%start,%conditionals);
    foreach my $key (sort(keys(%{$access_controls}))) {
        ($num,$scope,$end{$key},$start{$key}) = &unpack_acc_key($key);
        if ($scope eq 'public') {
            $public = $key;
            $publicnum = $num;
            $publictext = &acl_status($start{$key},$end{$key},$now);
        } elsif ($scope eq 'guest') {
            $guest=$key;
            $guestnum = $num;  
            $guesttext = &acl_status($start{$key},$end{$key},$now);
        } else {
            $conditionals{$scope}{$key} = $$access_controls{$key};
            if ($scope eq 'course') {
                push(@courses,$key);
            } elsif ($scope eq 'domains') {
                push(@domains,$key);
            } elsif ($scope eq 'users') {
                push(@users,$key);
            } elsif ($scope eq 'ip') {
                push(@ips,$key);
            }
        }
        $acl_count{$scope} ++;
    }
    $r->print('<table border="0"><tr><td valign="top">');
    if ($action eq 'chgaccess') {
        &standard_settings($r,$now,$then,$url,$filename,\%acl_count,\%start,
                           \%end,$public,$publicnum,$publictext,$guest,$guestnum,
                           $guesttext,$access_controls,%conditionals);
    } else {
        &condition_setting($r,$access_controls,$now,$then,\%acl_count,
                           \@domains,\@users,\@courses,\@ips);
    }
    $r->print('</td></tr></table>');
}

sub standard_settings {
    my ($r,$now,$then,$url,$filename,$acl_count,$start,$end,$public,$publicnum,
      $publictext,$guest,$guestnum,$guesttext,$access_controls,%conditionals)=@_;
    $r->print('<h3>'.&mt('Public access: [_1]',&mt($publictext)).'</h3>');
    $r->print(&Apache::loncommon::start_data_table());
    $r->print(&Apache::loncommon::start_data_table_header_row());
    $r->print('<th>'.&mt('Action').'</th><th>'.&mt('Dates available').'</th>');
    $r->print(&Apache::loncommon::end_data_table_header_row());
    $r->print(&Apache::loncommon::start_data_table_row());
    if ($public) {
        $r->print('<td>'.&actionbox('old',$publicnum,'public').'</td><td>'.
             &dateboxes($publicnum,$start->{$public},$end->{$public}).'</td>');
    } else {
        $r->print('<td>'.&actionbox('new','0','public').'</td><td>'.
                  &dateboxes('0',$now,$then).'</td>');
    }
    $r->print(&Apache::loncommon::end_data_table_row());
    $r->print(&Apache::loncommon::end_data_table());
    $r->print('</td><td width="40">&nbsp;</td><td valign="top">');
    $r->print('<h3>'.&mt('Passphrase-protected access: [_1]',&mt($guesttext)).'</h3>');
    $r->print(&Apache::loncommon::start_data_table());
    $r->print(&Apache::loncommon::start_data_table_header_row());
    $r->print('<th>'.&mt('Action').'</th><th>'.&mt('Dates available').
              '</th><th>'. &mt('Passphrase').'</th>');
    $r->print(&Apache::loncommon::end_data_table_header_row());
    $r->print(&Apache::loncommon::start_data_table_row());
    my $passwd;
    if ($guest) {
        $passwd = $$access_controls{$guest}{'password'};
        $r->print('<td>'.&actionbox('old',$guestnum,'guest').'</td><td>'.
              &dateboxes($guestnum,$start->{$guest},$end->{$guest}).'</td>');
    } else {
        $r->print('<td>'.&actionbox('new','1','guest').'</td><td>'.
                  &dateboxes('1',$now,$then).'</td>');
    }
    $r->print('<td><input type="text" size="15" name="password" value="'.
              $passwd.'" /></td>');
    $r->print(&Apache::loncommon::end_data_table_row());
    $r->print(&Apache::loncommon::end_data_table());
    $r->print('</td></tr><tr><td colspan="3">&nbsp;</td></tr>'.
              '<tr><td colspan="3" valign="top">');
    my $numconditionals = 0;
    my $conditionstext;
    my %cond_status;
    foreach my $scope ('domains','users','course','ip') {
        $numconditionals += $acl_count->{$scope}; 
        if ($acl_count->{$scope} > 0) {
            if ($conditionstext ne 'Active') {
                foreach my $key (keys(%{$conditionals{$scope}})) {
                    $conditionstext = &acl_status($start->{$key},$end->{$key},$now);
                    if ($conditionstext eq 'Active') {
                       last;
                    }
                }
            }
        }
    }
    if ($conditionstext eq '') {
        $conditionstext = 'Off';
    }
    my %anchor_fields = (
            'access' => $env{'form.selectfile'},
            'action' => 'chgconditions',
            'currentpath' => $env{'form.currentpath'},
        );
    $r->print('<h3>'.&mt('Conditional access: [_1]',&mt($conditionstext)).'</h3>');
    if ($numconditionals > 0) {
        my $count = 1;
        my $chg = 'none';
        $r->print(&mt('You have previously set [_1] conditional access controls.',$numconditionals).' '.&make_anchor($url,\%anchor_fields,&mt('Change Conditions')).'<br /><br />');
        $r->print(&Apache::loncommon::start_data_table());
        $r->print(&Apache::loncommon::start_data_table_header_row());
        $r->print('<th>'.&mt('Access control').'</th><th>'.&mt('Dates available').
                  '</th><th>'.&mt('Additional information').'</th>');
        $r->print(&Apache::loncommon::end_data_table_header_row());
        &build_access_summary($r,$count,$chg,%conditionals);
        $r->print(&Apache::loncommon::end_data_table());
    } else {
        $r->print(&make_anchor($url,\%anchor_fields,&mt('Add conditional access')).' '.&mt("based on domain, username, course/community affiliation or user's IP address."));
    }
}

sub condition_setting {
    my ($r,$access_controls,$now,$then,$acl_count,$domains,$users,$courses,$ips) = @_;
    $r->print('<tr><td valign="top">');
    &access_element($r,'domains',$acl_count,$domains,$access_controls,$now,$then);
    $r->print('</td><td>&nbsp;</td><td valign="top">');
    &access_element($r,'users',$acl_count,$users,$access_controls,$now,$then);
    $r->print('</td></tr><tr><td colspan="3"></td></tr><tr><td valign="top">');
    &access_element($r,'course',$acl_count,$courses,$access_controls,$now,$then);
    $r->print('</td><td>&nbsp;</td><td valign="top">');
    &access_element($r,'ip',$acl_count,$ips,$access_controls,$now,$then);
    $r->print('</td></tr></table>');
}

sub acl_status {
    my ($start,$end,$now) = @_;
    if ($start > $now) {
        return 'Inactive';
    }
    if ($end && $end<$now) {
        return 'Inactive';
    }
    return 'Active';
}

sub access_element {
    my ($r,$type,$acl_count,$items,$access_controls,$now,$then) = @_;
    my %typetext = (
        domains => 'Domain',
        users   => 'User',
        course  => 'Course/Community',
        ip      => 'IP',
    );
    $r->print('<h3>'.&mt($typetext{$type}.'-based conditional access:').' ');
    if ($$acl_count{$type}) {
        $r->print(&mt('[quant,_1,condition]',$$acl_count{$type}));
    } else {
        $r->print(&mt('Off'));
    }
    $r->print('</h3>');
    &display_access_row($r,'old',$type,$items,$access_controls,$now,$then);
    return;
}

sub display_access_row {
    my ($r,$status,$type,$items,$access_controls,$now,$then) = @_;
    my ($showtype, $infotype);
    if ($type eq 'course') {
        $showtype = &mt('Courses/Communities');
        $infotype = 'Course/Community';
    } elsif ($type eq 'domains') {
        $showtype = &mt('Domains');
        $infotype = 'Domain';
    } elsif ($type eq 'users') {
        $showtype = &mt('Users');
        $infotype = 'User';
    } elsif ($type eq 'ip') {
        $showtype = &mt('IP-based');
        $infotype = 'IP';  
    }
    if (@{$items} > 0) {
        my @all_doms;
        my $colspan = 3;
        $r->print(&Apache::loncommon::start_data_table());
        $r->print(&Apache::loncommon::start_data_table_header_row());
        $r->print('<th>'.&mt('Action?').'</th><th>'.$showtype.'</th><th>'.
              &mt('Dates available').'</th>');
        if ($type eq 'course' && $status eq 'old') {
            $r->print('<th>'.&mt('Allowed course/community affiliations').
                      '</th>');
            $colspan ++;
        } elsif ($type eq 'domains') {
            @all_doms = sort(&Apache::lonnet::all_domains());
        }
        $r->print(&Apache::loncommon::end_data_table_header_row());
        foreach my $key (@{$items}) {
	    $r->print(&Apache::loncommon::start_data_table_row());
            if ($type eq 'course') {
                &course_row($r,$status,$type,$key,$access_controls,$now,$then);
            } elsif ($type eq 'domains') {
                &domains_row($r,$status,$key,\@all_doms,$access_controls,$now,
                            $then);
            } elsif ($type eq 'users') {
                &users_row($r,$status,$key,$access_controls,$now,$then);
            } elsif ($type eq 'ip') {
                &ips_row($r,$status,$key,$access_controls,$now,$then);
            }
	    $r->print(&Apache::loncommon::end_data_table_row());
        }
        if ($status eq 'old') {
	    $r->print(&Apache::loncommon::start_data_table_row());
            $r->print('<td colspan="',$colspan.'">'.&additional_item($type).
                      '</td>');
	    $r->print(&Apache::loncommon::end_data_table_row());
        }
        $r->print(&Apache::loncommon::end_data_table());
    } else {
        $r->print(
            '<p class="LC_info">'
           .&mt('No '.$infotype.'-based conditions defined')
           .'</p>'
           .&additional_item($type)
        );
    }
    return;
}

sub course_js {
    return qq|
<script type="text/javascript">
// <![CDATA[
function setRoleOptions(num,roleid,cdom,cnum,type) {
    updateIndexNum = getIndexByValue('update',num);
    var addItem = 'add_role_'+num;
    var addIndexNum = getIndexByName(addItem);
    if (document.portform.elements[addItem].checked) {
        document.portform.elements[updateIndexNum].checked = true;
        var url = '/adm/portfolio?action=rolepicker&setroles='+num+'_'+roleid+'&cnum='+cnum+'&cdom='+cdom+'&type='+type;
        var title = 'Roles_Chooser';
        var options = 'scrollbars=1,resizable=1,menubar=0';
        options += ',width=700,height=600';
        rolebrowser = open(url,title,options,'1');
        rolebrowser.focus();
    } else {
        addArray = new Array ('role','access','section','group');
        for (var j=0;j<addArray.length;j++) {
            var itemIndex = getIndexByName(addArray[j]+'_'+num+'_'+roleid);
            document.portform.elements[itemIndex].value = '';
        }
    }
}

function getIndexByName(item) {
    for (var i=0;i<document.portform.elements.length;i++) {
        if (document.portform.elements[i].name == item) {
            return i;
        }
    }
    return -1;
}

function getIndexByValue(name,value) {
    for (var i=0;i<document.portform.elements.length;i++) {
        if (document.portform.elements[i].name == name && document.portform.elements[i].value == value) {
            return i;
        }
    }
    return -1;
}

// ]]>
</script>
|;
}

sub course_row {
    my ($r,$status,$type,$item,$access_controls,$now,$then) = @_;
    my $content;
    my $defdom = $env{'user.domain'};
    if ($status eq 'old') {
        $content = $$access_controls{$item}; 
        $defdom =  $content->{'domain'};
    }
    my $js = &Apache::loncommon::coursebrowser_javascript($defdom)
	.&course_js();
    my $showtype = &mt('Course/Community');
    my $crstype = 'Course';
    my ($num,$scope,$end,$start) = &set_identifiers($status,$item,$now,$then,
                                                    $type);
    $r->print('<td>'.$js.&actionbox($status,$num,$scope).'</td>');
    if ($status eq 'old') {
        my $cid = $content->{'domain'}.'_'.$content->{'number'};
        my %course_description = &Apache::lonnet::coursedescription($cid);
        if ($course_description{'type'} ne '') {
            $crstype = $course_description{'type'};
        }
        $r->print('<td><input type="hidden" name="crsdom_'.$num.'" value="'.$content->{'domain'}.'" /><input type="hidden" name="crsnum_'.$num.'" value="'.$content->{'number'}.'" />'.$course_description{'description'}.'</td>');
    } elsif ($status eq 'new') {
        $r->print('<td>'.&Apache::loncommon::selectcourse_link('portform','crsnum_'.$num,'crsdom_'.$num,'description_'.$num,$num.'_1',undef,$showtype).'&nbsp;&nbsp;<input type="text" name="description_'.$num.'" size="30" /><input type="hidden" name="crsdom_'.$num.'" /><input type="hidden" name="crsnum_'.$num.'" /></td>');
    }
    $r->print('<td>'.&dateboxes($num,$start,$end));
    my $newrole_id = 1;
    if ($status eq 'old') {
        $r->print('</td><td>');
        my $max_id = 0;
        if (keys(%{$content->{'roles'}}) > 0) {
            $r->print('<table><tr><th>'.&mt('Action').'</th>'.
                      '<th>'.&mt('Roles').'</th>'.
                      '<th>'.&mt('Access').'</th>'.
                      '<th>'.&mt('Sections').'</th>'.
                      '<th>'.&mt('Groups').'</th></tr>');
            foreach my $role_id (sort(keys(%{$content->{'roles'}}))) {
                if ($role_id > $max_id) {
                    $max_id = $role_id;
                }
                $max_id ++;
                my $role_selects = &role_selectors($num,$role_id,$crstype,$content,'display');
                $r->print('<tr><td><span class="LC_nobreak"><label><input type="checkbox" name="delete_role_'.$num.'" value="'.$role_id.'" />'.&mt('Delete').'</label></span><br /><input type="hidden" name="preserve_role_'.$num.'" value="'.$role_id.'" /></td>'.$role_selects.'</tr>');
            }
            $r->print('</table>');
        }
        $r->print('<br />'.&mt('Add a roles-based condition').
                  '&nbsp;<input type="checkbox" name="add_role_'.
                  $num.'" onclick="javascript:setRoleOptions('."'$num',
                  '$max_id','$content->{'domain'}','$content->{'number'}',
                  '$showtype'".')" value="" />');
        $newrole_id = $max_id;
    } else {
        $r->print('<input type="hidden" name="add_role_'.$num.'" value="" />');
    }
    $r->print(&add_course_role($num,$newrole_id));
    $r->print('</td>');
    return;
}

sub add_course_role {
    my ($num,$max_id) = @_;
    my $output;
    $output .='<input type="hidden" name="role_'.$num.'_'.$max_id.'" />'.
              '<input type="hidden" name="access_'.$num.'_'.$max_id.'" />'.
              '<input type="hidden" name="section_'.$num.'_'.$max_id.'" />'.
              '<input type="hidden" name="group_'.$num.'_'.$max_id.'" />';
    return $output;
}

sub domains_row {
    my ($r,$status,$item,$all_doms,$access_controls,$now,$then) = @_;
    my ($num,$scope,$end,$start) = &set_identifiers($status,$item,$now,$then,
                                                    'domains');
    my $dom_select = '<select name="dom_'.$num.'" size="4" multiple="multiple">'.
                     ' <option value="">'.&mt('Please select').'</option>';
    if ($status eq 'old') {
        my $content =  $$access_controls{$item};
	foreach my $dom (@{$all_doms}) {
            if ((@{$content->{'dom'}} > 0) 
		&& (grep(/^\Q$dom\E$/,@{$content->{'dom'}}))) {
                $dom_select .= '<option value="'.$dom.'" selected="selected">'.
                               $dom.'</option>';
            } else {
                $dom_select .= '<option value="'.$dom.'">'.$dom.'</option>';
            }
        }
    } else {
        foreach my $dom (@{$all_doms}) {
            $dom_select .= '<option value="'.$dom.'">'.$dom.'</option>';
        }
    }
    $dom_select .= '</select>';
    $r->print('<td>'.&actionbox($status,$num,$scope).'</td><td>'.$dom_select.
              '</td><td>'.&dateboxes($num,$start,$end).'</td>');
}

sub users_row {
    my ($r,$status,$item,$access_controls,$now,$then) = @_;
    my ($num,$scope,$end,$start) = &set_identifiers($status,$item,$now,$then,
                                                    'users');
    my $curr_user_list;
    if ($status eq 'old') {
        my $content = $$access_controls{$item};
        $curr_user_list = &sort_users($content->{'users'});
    }
    $r->print('<td>'.&actionbox($status,$num,$scope).'</td><td>'.&mt("Format for users' username:domain information:").'<br /><tt>sparty:msu,illini:uiuc  ... etc.</tt><br /><textarea name="users_'.$num.'" cols="30"  rows="5">'.$curr_user_list.'</textarea></td><td>'.&dateboxes($num,$start,$end).'</td>');
}

sub ips_row {
    my ($r,$status,$item,$access_controls,$now,$then) = @_;
    my ($num,$scope,$end,$start) = &set_identifiers($status,$item,$now,$then,
                                                    'ip');
    my $curr_ips_list;
    if ($status eq 'old') {
        my $content = $$access_controls{$item};
        $curr_ips_list = &sort_ips($content->{'ip'});
    }
    $r->print('<td>'.&actionbox($status,$num,$scope).'</td><td>'.&mt('Format for IP controls').'<br />'.
              &mt('[_1] or [_2] or [_3] or [_4] or [_5]','<tt>35.8.*</tt>','<tt>35.8.3.[34-56]</tt>',
                  '<tt>*.msu.edu</tt>','<tt>35.8.3.34</tt>','<tt>somehostname.pa.msu.edu</tt>').'<br />'.
              &mt('Use a comma to separate different ranges.').'</br/>'.
              '<textarea name="ips_'.$num.'" cols="30"  rows="5">'.$curr_ips_list.'</textarea></td>'.
              '<td>'.&dateboxes($num,$start,$end).'</td>');
}

sub additional_item {
    my ($type) = @_;
    my $showtype;
    if ($type eq 'course') {
        $showtype = 'course/community';
    } elsif ($type eq 'domains') {
        $showtype = 'domain';
    } elsif ($type eq 'users') {
        $showtype = 'user';
    } elsif ($type eq 'ip') {
        $showtype = 'IP';
    }
    return
        &mt('Add new '.$showtype.'-based condition(s)?')
       .'&nbsp;'.&mt('Number to add: ')
       .'<input type="text" name="new'.$type.'" size="3" value="0" />';
}

sub actionbox {
    my ($status,$num,$scope) = @_;
    my $output = '<span class="LC_nobreak"><label>';
    if ($status eq 'new') {
        my $checkstate;
        if ($scope eq 'domains' || $scope eq 'users' || $scope eq 'course' || $scope eq 'ip') {
            $checkstate = 'checked="checked"';
        }
        $output .= '<input type="checkbox" name="activate" value="'.$num.'" '.
                   $checkstate.'  />'.
        &mt('Activate');
    } else {
        $output .= '<input type="checkbox" name="delete" value="'.$num.
                   '" />'.&mt('Delete').'</label></span><br /><span class="LC_nobreak">'.
                   '<label><input type="checkbox" name="update" value="'.
                   $num.'" />'.&mt('Update');
    }
    $output .= '</label></span><input type="hidden" name="scope_'.$num.'" value="'.$scope.'" />';
    return $output;
}
                                                                                   
sub dateboxes {
    my ($num,$start,$end) = @_;
    my $noend;
    if ($end == 0) {
        $noend = 'checked="checked"';
    }
    my $startdate = &Apache::lonhtmlcommon::date_setter('portform',
                           'startdate_'.$num,$start,undef,undef,undef,1,undef,
                            undef,undef,1);
    my $enddate = &Apache::lonhtmlcommon::date_setter('portform',
                               'enddate_'.$num,$end,undef,undef,undef,1,undef,
                                undef,undef,1). '&nbsp;&nbsp;<span class="LC_nobreak"><label>'.
                                '<input type="checkbox" name="noend_'.
                                $num.'" '.$noend.' />'.&mt('No end date').
                                '</label></span>';
                                                                                   
    my $output = &mt('Start: ').$startdate.'<br />'.&mt('End: ').$enddate;
    return $output;
}

sub unpack_acc_key {
    my ($acc_key) = @_;
    my ($num,$scope,$end,$start) = ($acc_key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
    return ($num,$scope,$end,$start);
}

sub set_identifiers {
    my ($status,$item,$now,$then,$scope) = @_;
    if ($status eq 'old') {
        return(&unpack_acc_key($item));
    } else {
        return($item,$scope,$then,$now);
    }
} 

sub role_selectors {
    my ($num,$role_id,$type,$content,$caller) = @_;
    my ($output,$cdom,$cnum,$longid);
    if ($caller eq 'display') {
        $longid = '_'.$num.'_'.$role_id;
        $cdom = $$content{'domain'};
        $cnum = $$content{'number'};
    } elsif ($caller eq 'rolepicker') {
         $cdom = $env{'form.cdom'};
         $cnum = $env{'form.cnum'};
    }
    my $crstype = 'Course';
    if ($cnum =~ /^$match_community$/) {
        $crstype = 'Community'
    }
    my ($sections,$groups,$allroles,$rolehash,$accesshash) =
            &Apache::loncommon::get_secgrprole_info($cdom,$cnum,1,$crstype);
    if (!@{$sections}) {
        @{$sections} = ('none');
    } else {
        unshift(@{$sections},('all','none'));
    }
    if (!@{$groups}) {
        @{$groups} = ('none');
    } else {
        unshift(@{$groups},('all','none'));
    }
    my @allacesses = sort(keys(%{$accesshash}));
    my (%sectionhash,%grouphash);
    foreach my $sec (@{$sections}) {
        $sectionhash{$sec} = $sec;
    }
    foreach my $grp (@{$groups}) {
        $grouphash{$grp} = $grp;
    }
    my %lookup = (
                   'role' => $rolehash,
                   'access' => $accesshash,
                   'section' => \%sectionhash,
                   'group' => \%grouphash,
                 );
    my @allaccesses = sort(keys(%{$accesshash}));
    my %allitems = (
                    'role' => $allroles,
                    'access' => \@allaccesses,
                    'section' => $sections,
                    'group' => $groups,
                   );
    foreach my $item ('role','access','section','group') {
        $output .= '<td><select name="'.$item.$longid.'" multiple="multiple" size="4">'."\n";
        foreach my $entry (@{$allitems{$item}}) {
            if ($caller eq 'display') {
                if ((@{$$content{'roles'}{$role_id}{$item}} > 0) && 
                    (grep(/^\Q$entry\E$/,@{$$content{'roles'}{$role_id}{$item}}))) {
                    $output .= '  <option value="'.$entry.'" selected="selected">'.
                                  $lookup{$item}{$entry}.'</option>';
                    next;
                }
            }
            $output .= '  <option value="'.$entry.'">'.
                       $lookup{$item}{$entry}.'</option>';
        }
        $output .= '</select>';
    }
    $output .= '</td>';
    return $output;
}

sub role_options_window {
    my ($r) = @_;
    my $type = $env{'form.type'};
    my $rolenum = $env{'form.setroles'};
    my ($num,$role_id) = ($rolenum =~ /^([\d_]+)_(\d+)$/);
    my $role_elements;
    foreach my $item ('role','access','section','group') {
        $role_elements .= "'".$item.'_'.$rolenum."',";
    }
    $role_elements =~ s/,$//; 
    my $role_selects = &role_selectors($num,$role_id,$type,undef,
                                       'rolepicker');
    $r->print(<<"END_SCRIPT");
<script type="text/javascript">
function setRoles() {
    var role_elements = new Array($role_elements);
    for (var i=0; i<role_elements.length; i++) {
        var copylist = '';
        for (var j=0; j<document.rolepicker.elements[i].length; j++) {
            if (document.rolepicker.elements[i].options[j].selected) {
                copylist = copylist + document.rolepicker.elements[i].options[j].value + ',';
            }
        }
        copylist = copylist.substr(0,copylist.length-1);
        var openerItem = getIndexByName(role_elements[i]);
        opener.document.portform.elements[openerItem].value = copylist; 
    }
    var roleAdder = getIndexByName('add_role_$num');
    opener.document.portform.elements[roleAdder].value = '$role_id';
    self.close();
}

function getIndexByName(item) {
    for (var i=0;i<opener.document.portform.elements.length;i++) {
        if (opener.document.portform.elements[i].name == item) {
            return i;
        }
    }
    return -1;
}

</script>
END_SCRIPT
    $r->print(
        '<p>'
       .&mt('Select roles, '.lc($type).' status, section(s) and group(s) for users'
           .' who will be able to access the portfolio file.')
       .'</p>'
    );
    $r->print(
        '<form name="rolepicker" action="/adm/portfolio" method="post">'
       .'<table><tr>'
       .'<th>'.&mt('Roles').'</th>'
       .'<th>'.&mt("$type status").'</th>'
       .'<th>'.&mt('Sections').'</th>'
       .'<th>'.&mt('Groups').'</th>'
       .'</tr><tr>'.$role_selects.'</tr>'
       .'</table><br />'
       .'<input type="button" name="rolepickbutton" value="'.&mt('Save').'" onclick="setRoles()" />'
    );
    return;
}

sub select_files {
    my ($r,$dir_list) = @_;
    if ($env{'form.continue'} eq 'true') {
        # here we update the selections for the currentpath
        # eventually, have to handle removing those not checked, but . . . 
        my @items=&Apache::loncommon::get_env_multiple('form.checkfile');
        if (scalar(@items)){
            my @ok_items;
            if (ref($dir_list) eq 'ARRAY') {
                foreach my $dir_line (@{$dir_list}) {
                    my ($filename,undef,undef,undef,undef,undef,undef,undef,$size)=split(/\&/,$dir_line,10);
                    if (grep(/^\Q$filename\E$/,@items)) {
                        if ($size) {
                            push(@ok_items,$filename); 
                        }
                    }
                }
            }
            &Apache::lonnet::save_selected_files($env{'user.name'}, $env{'form.currentpath'}, @ok_items);
        }
    } else {
            #empty the file for a fresh start
            &Apache::lonnet::clear_selected_files($env{'user.name'});
    }
    my @files = &Apache::lonnet::files_not_in_path($env{'user.name'}, $env{'form.currentpath'});
    my $java_files = join ",", @files;
    if ($java_files) {
        $java_files.=',';
    }
    my $javascript =(<<ENDSMP);
        <script type="text/javascript">
        function finishSelect() {
ENDSMP
    $javascript .= 'fileList = "'.$java_files.'";';
    $javascript .= (<<ENDSMP);
            for (i=0;i<document.forms.checkselect.length;i++) { 
                if (document.forms.checkselect[i].checked){
                    fileList = fileList + document.forms.checkselect.currentpath.value + document.forms.checkselect[i].value + "," ;
                }
            }
            var hwfield = opener.document.getElementsByName('$env{'form.fieldname'}');
            hwfield[0].value = fileList;
            self.close();
        }
        </script>
ENDSMP
    $r->print($javascript);
    $r->print("<h1>".&mt('Select portfolio files')."</h1>");
    my @otherfiles=&Apache::lonnet::files_not_in_path($env{'user.name'}, $env{'form.currentpath'});
    if (@otherfiles) {
	$r->print(&Apache::loncommon::start_data_table()
                 .&Apache::loncommon::start_data_table_header_row()
                 .'<th>'.&mt('Files selected from other directories:')."</th>"
                 .&Apache::loncommon::end_data_table_header_row()
        );
	foreach my $file (@otherfiles) {
	    $r->print(&Apache::loncommon::start_data_table_row()
                     .'<td>'.$file."</td>"
                     .&Apache::loncommon::end_data_table_row()
            );
	}
        $r->print(&Apache::loncommon::end_data_table()
                 .'<br />'
        );
    }
    $r->print('<div>'
             .&mt('Check as many files as you wish in response to the problem:')
             .'</div>'
    );
}

sub upload {
    my ($r,$url,$group)=@_;
    my $formname = 'uploaddoc';
    my $fname = &Apache::lonnet::clean_filename($env{'form.'.$formname.'.filename'});
    my ($state,$msg);
    if ($fname eq '') {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('Invalid filename: [_1]; the name of the uploaded file did not contain any letters, '.
                      'so after eliminating special characters there was nothing left.',
                      '<span class="LC_filename">'.$env{'form.uploaddoc.filename'}.'</span>'),1)));

        $r->print(&done(undef,$url));
        return;
    }
    my $disk_quota = &get_quota($group);
    my $portfolio_root = &get_portfolio_root();
    my $port_path = &get_port_path();
    my ($uname,$udom) = &get_name_dom($group);
    my $getpropath = 1;
    my $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$portfolio_root,$getpropath);
    ($state,$msg) = 
        &Apache::loncommon::check_for_upload($env{'form.currentpath'},$fname,
		                             $group,$formname,$portfolio_root,
                                             $port_path,$disk_quota,
                                             $current_disk_usage,$uname,$udom);
    if ($state eq 'will_exceed_quota'
	|| $state eq 'file_locked'
        || $state eq 'zero_bytes') {
	$r->print($msg.&done(undef,$url));
	return;
    }

    my (%allfiles,%codebase,$mode,$mimetype);
    if ($env{'form.'.$formname.'.filename'} =~ m/(\.htm|\.html|\.shtml)$/i) {
        if ($env{'form.parserflag'}) {
	    $mode = 'parse';
        }
    }
    my $context;
    if ($state eq 'existingfile') {
        $context = $state;
    }
    my $subdir = $port_path.$env{'form.currentpath'};
    $subdir =~ s{(/)$}{};
    my ($result,$timestamp) =
	&Apache::lonnet::userfileupload($formname,$context,$subdir,
					$mode,\%allfiles,\%codebase,undef,undef,
                                        undef,undef,undef,undef,\$mimetype);
    if ($state eq 'existingfile') {
        my $group_elem;
        my $rootdir = $r->dir_config('lonDaemons').'/tmp/overwrites';
        if ($group eq '') {
            $rootdir .= '/'.$env{'user.domain'}.'/'.$env{'user.name'};
        } else {
            $rootdir .= '/'.$env{'course.'.$env{'request.course.id'}.'.domain'}.
                        '/'.$env{'course.'.$env{'request.course.id'}.'.num'};
            $group_elem = '<input type="hidden" name="group" value="'.$group.'" />';
        }
        if (($result eq $rootdir.'/'.$port_path.$env{'form.currentpath'}.$fname) && ($timestamp =~ /^\d+$/)) {
            my $showfname = &HTML::Entities::encode($fname,'&<>"');
            my %lt = &Apache::lonlocal::texthash (
                                                   over => 'Overwrite existing file?',
                                                   yes  => 'Yes',
                                                   no   => 'No',
                                                   undo => 'This action can not be undone.',
                                                   conf => 'Are you sure you want to overwrite an existing file?',
                                                   cont => 'Continue',
                                                 );
            my $parserflag;
            my $hidden = &hidden_elems();
            if ($mode eq 'parse') {
                $parserflag = '<input type="hidden" name="parserflag" value="1" />';
            }
            $r->print(<<"END");
<script type="text/javascript">
// <![CDATA[
function confirmOverwrite() {
    var chosen;
    if (document.existingfile.overwrite.length) {
        for (var i=0; i<document.existingfile.overwrite.length; i++) {
            if (document.existingfile.overwrite[i].checked) {
                chosen = document.existingfile.overwrite[i].value;
            }
        }
    }
    if (chosen == 1) {
        if (confirm('$lt{'conf'}')) {
            document.existingfile.action.value = "process_overwrite";
            return true;
        } else {
            document.existingfile.action.value = "cancel_overwrite";
            if (document.existingfile.overwrite.length) {
                for (var i=0; i<document.existingfile.overwrite.length; i++) {
                    if (document.existingfile.overwrite[i].value == "0") {
                        document.existingfile.overwrite[i].checked = true;
                    }
                }
            }
            return false;
        }
    } else {
        document.existingfile.action.value = "cancel_overwrite";
        return true;
    }
}
// ]]>
</script>
<p>
$msg
</p>
<form method="post" action="$url" name="existingfile" onsubmit="return confirmOverwrite();">
<p class="LC_nobreak">$lt{'over'}
<label><input type="radio" name="overwrite" value="1" />
$lt{'yes'}</label>&nbsp;
<label><input type="radio" name="overwrite" value="0" checked="checked" />$lt{'no'}</label></p>
<p>
<input type="hidden" name="action" value="cancel_overwrite" />
<input type="hidden" name="filename" value="$showfname" />
<input type="hidden" name="timestamp" value="$timestamp" />
$hidden
$parserflag
$group_elem
<input type="submit" name="process" value="$lt{'cont'}" />
</p>
</form>
END
        } else {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('An error occurred ([_1]) while trying to upload [_2].'
                        ,$result,&display_file(undef,$fname)),1)));
            $r->print(&done(undef,$url));
        }
    } elsif ($result !~ m|^/uploaded/|) {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('An error occurred ([_1]) while trying to upload [_2].'
                        ,$result,&display_file(undef,$fname)),1)));
	$r->print(&done(undef,$url));
    } else {
        if (!&suppress_embed_prompt()) {
            if ($mimetype eq 'text/html') {
	        if (keys(%allfiles) > 0) {
                    &print_dependency_form($r,$url,\%allfiles,\%codebase,$result);
                    return;
	        } else {
                    $r->print('<p class="LC_warning">'.&mt('No embedded items identified.').'</p>');
                }
            }
        }
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('File successfully uploaded'))));
	$r->print(&done(undef,$url));
    }
    return;
}

sub hidden_elems {
    my $contelem;
    if ($env{'form.mode'} eq 'selectfile') {
        $contelem = '<input type="hidden" name="continue" value="true" />';
    }
    return <<END;
<input type="hidden" name="currentpath" value="$env{'form.currentpath'}" />
<input type="hidden" name="symb" value="$env{'form.symb'}" />
<input type="hidden" name="fieldname" value="$env{'form.fieldname'}" />
<input type="hidden" name="mode" value="$env{'form.mode'}" />
<input type="hidden" name="showversions" value="$env{'form.showversions'}" />
$contelem
END
}

sub print_dependency_form {
    my ($r,$url,$allfiles,$codebase,$result) = @_;
    my $container = &HTML::Entities::encode($result,'<>"&');
    my $state = &embedded_form_elems($container);
    my ($embedded,$num,$pathchg) = &Apache::loncommon::ask_for_embedded_content($url,$state,$allfiles,$codebase,
                                  {'error_on_invalid_names'   => 1,
                                   'ignore_remote_references' => 1,});
    if ($embedded) {
        if ($num || $pathchg) {
            $r->print('<h3>'.&mt("Reference Warning").'</h3>');
        } else {
            $r->print('<h3>'.&mt("Reference Information").'</h3>');
        }
        if ($num) {
            $r->print('<p>'.&mt('Completed upload of the file.').' '.
                      &mt('This file contained references to other files.').' '.
                      &mt('You must upload the referenced files or else the uploaded file may not work properly.').
                      '</p>'.
                      '<p>'.&mt("Please select the locations from which the referenced files are to be uploaded.").'</p>'.
                       $embedded.
                       '<p>'.&mt('or').'</p>'.&done('Return to directory',$url));
        } else {
            $r->print('<p>'.&mt("Completed upload of the file. This file contained references to other files.").'</p>'.
                      $embedded.
                      '<p>'.&done('Return to directory',$url).'</p>');
        }
    } else {
        $r->print(&done(undef,$url));
    }
    return;
}

sub overwrite {
    my ($r,$url,$group)=@_;
    my $formname = 'existingfile';
    my $port_path = &get_port_path();
    my $fname = &Apache::lonnet::clean_filename($env{'form.filename'});
    my (%allfiles,%codebase,$mode,$mimetype);
    unless (&suppress_embed_prompt()) {
        if ($env{'form.parserflag'}) {
            if ($fname =~ /\.s?html?$/i) {
                $mode = 'parse';
            }
        }
    }
    if ($fname eq '') {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('Invalid filename: [_1]; the name of the uploaded file did not contain any letters, '.
                      'so after eliminating special characters there was nothing left.',
                      '<span class="LC_filename">'.$env{'form.filename'}.'</span>'),1)));
        $r->print(&done(undef,$url));
        return;
    }
    $env{'form.'.$formname.'.filename'} = $fname;
    my $subdir = $port_path.$env{'form.currentpath'};
    $subdir =~ s{(/)$}{};
    my $result=
        &Apache::lonnet::userfileupload($formname,'overwrite',$subdir,$mode,
                                        \%allfiles,\%codebase,undef,undef,undef,
                                        undef,undef,undef,\$mimetype);
    if ($result !~ m|^/uploaded/|) {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('An error occurred ([_1]) while trying to overwrite [_2].'
                       ,$result,&display_file(undef,$fname)),1)));
    } else {
        if ($mode eq 'parse') {
            if ($mimetype eq 'text/html') {
                if (keys(%allfiles) > 0) {
                    &print_dependency_form($r,$url,\%allfiles,\%codebase,$result);
                    return;
                } else {
                    $r->print(
                        &Apache::loncommon::confirmwrapper(
                            &Apache::lonhtmlcommon::confirm_success(
                                &mt('Overwriting completed.'))
                           .'<br />'.&mt('No embedded items identified.')));
                }
            }
        } else {
            $r->print(
                &Apache::loncommon::confirmwrapper(
                    &Apache::lonhtmlcommon::confirm_success(
                        &mt('Overwriting completed.'))));
        }
    }

    my $group_elem;
    if (defined($env{'form.group'})) {
        $group_elem = '<input type="hidden" name="group" value="'.$env{'form.group'}.'" />';
        if (defined($env{'form.ref'})) {
            $group_elem .= '<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />'."\n";
        }
    }
    my $hidden = &hidden_elems();
    $r->print(
        &Apache::lonhtmlcommon::actionbox(
            ['<a href="javascript:document.overwritedone.submit();">'
            .&mt('Return to directory')
            .'</a>'])
       .'<form name="overwritedone" method="post" action="'.$url.'">'
       .$hidden
       .$group_elem
       .'</form>'
    );
    return;
}

sub lock_info {
    my ($r,$url,$group) = @_;
    my ($uname,$udom) = &get_name_dom($group);
    my $current_permissions = &Apache::lonnet::get_portfile_permissions($udom,
                                                                       $uname);
    my $file_name = $env{'form.lockinfo'};
    $file_name = &prepend_group($file_name);
    if (defined($file_name) && defined($$current_permissions{$file_name})) {
        foreach my $array_item (@{$$current_permissions{$file_name}}) {
            next if (ref($array_item) ne 'ARRAY');

	    my $filetext;
	    if (defined($group)) {
		$filetext = '<strong>'.$env{'form.lockinfo'}.
		    '</strong> (group: '.$group.')'; 
	    } else {
		$filetext = '<span class="LC_filename">'.$file_name.'</span>';
	    } 
	    
	    my $title ='<strong>'.&Apache::lonnet::gettitle($$array_item[0]).
		'</strong><br />';
	    if ($$array_item[-1] eq 'graded') {
		$r->print(&mt('[_1] was submitted in response to problem: [_2]',
                              $filetext,$title));
	    } elsif ($$array_item[-1] eq 'handback') {
		$r->print(&mt('[_1] was handed back in response to problem: [_2]',
                              $filetext,$title));
	    } else {
		# submission style lock
		$r->print(&mt('[_1] was submitted in response to problem: [_2]',
                              $filetext,$title));
	    }
	    my %course_description = 
		&Apache::lonnet::coursedescription($$array_item[1]);
	    if ( $course_description{'description'} ne '') {
		$r->print(&mt('In the course:').' <strong>'.$course_description{'description'}.'</strong><br />');
	    }
        }
    }
    $r->print(&done(undef,$url));
    return 'ok';
}

sub createdir {
    my ($r,$url,$group)=@_;
    my $newdir=&Apache::lonnet::clean_filename($env{'form.newdir'});
    # Display warning in case of directory name cleaning has changed the directory name
    if ($newdir ne $env{'form.newdir'}) {
        $r->print(
            '<p><span class="LC_warning">'
           .&mt('Invalid characters')
           .'</span><br />'
           .&mt('The new directory name was changed from [_1] to [_2].'
                      ,'<span class="LC_filename">'.$env{'form.newdir'}.'</span>'
                      ,'<span class="LC_filename">'.$newdir.'</span>')
           .'</p>'
        );
    }

    # Directory name empty?
    if ($newdir eq '') {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('Error: no directory name was provided.'),1)));
            $r->print(&done(undef,$url));
            return;
    }

    my $portfolio_root = &get_portfolio_root(); 
    my ($dirlistref,$listerror) = &get_dir_list($portfolio_root,undef,$group);
    my $found_file = 0;
    if (ref($dirlistref) eq 'ARRAY') {
        foreach my $line (@{$dirlistref}) {
            my ($filename)=split(/\&/,$line,2);
            if ($filename eq $newdir){
                $found_file = 1;
            }
        }
    }
    if ($found_file) {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('Unable to create a directory named [_1].'
                        ,'<span class="LC_filename">'.$newdir.'</span>'),1)
               .'<br />'.&mt('A file or directory by that name already exists.')));
    } else {
        my ($uname,$udom) = &get_name_dom($group);
        my $port_path = &get_port_path();
        my $result=&Apache::lonnet::mkdiruserfile($uname,$udom,
	         $port_path.$env{'form.currentpath'}.$newdir);
        if ($result ne 'ok') {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('An error occurred ([_1]) while trying to create a new directory [_2].'
                        ,$result,&display_file()),1)));

        } else {
        $r->print(
            &Apache::loncommon::confirmwrapper(
                &Apache::lonhtmlcommon::confirm_success(
                    &mt('Directory successfully created'))));
        }
    }
    $r->print(&done(undef,$url));
}

sub get_portfolio_root {
    my ($udom,$uname,$group) = @_;
    if (!(defined($udom)) || !(defined($uname))) {
        ($uname,$udom) = &get_name_dom($group);
    }
    my $path = '/userfiles/portfolio';
    if (!defined($group)) { 
        if (defined($env{'form.group'})) {
            $group = $env{'form.group'};      
        }
    }
    if (defined($group)) {
        $path = '/userfiles/groups/'.$group.'/portfolio';
    } 
    return $path;
}

sub get_group_quota {
    my ($group) = @_;
    my $group_quota; 
    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
    my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum,$group);
    if (%curr_groups) {
        my %group_info =  &Apache::longroup::get_group_settings(
                                                    $curr_groups{$group});
        $group_quota = $group_info{'quota'}; #expressed in MB
        if ($group_quota) {
            $group_quota = 1000 * $group_quota; #expressed in k
        }
    }
    return $group_quota;
}

sub get_dir_list {
    my ($portfolio_root,$path,$group) = @_;
    $path ||= $env{'form.currentpath'};
    my ($uname,$udom) = &get_name_dom($group);
    my $getpropath = 1;
    return &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
}

sub get_name_dom {
    my ($group) = @_;
    my ($uname,$udom);
    if (defined($group)) {
        $udom = $env{'course.'.$env{'request.course.id'}.'.domain'};
        $uname = $env{'course.'.$env{'request.course.id'}.'.num'};
    } else {
        $udom = $env{'user.domain'};
        $uname = $env{'user.name'};
    }
    return ($uname,$udom);
}

sub prepend_group {
    my ($filename) = @_;
    if (defined($env{'form.group'})) {
        $filename = $env{'form.group'}.$filename;
    }
    return $filename;
}

sub get_namespace {
    my $namespace = 'portfolio';
    if (defined($env{'form.group'})) {
        my ($uname,$udom) = &get_name_dom($env{'form.group'});
        $namespace .= '_'.$udom.'_'.$uname.'_'.$env{'form.group'};
    }
    return $namespace;
}

sub get_port_path {
    my $port_path;
    if (defined($env{'form.group'})) {
       $port_path = "groups/$env{'form.group'}/portfolio";
    } else {
       $port_path = 'portfolio';
    }
    return $port_path;
}

sub missing_priv {
    my ($r,$url,$priv) = @_;
    my %longtext = 
        &Apache::lonlocal::texthash(
                      upload => 'upload files',
                      delete => 'delete files',
                      rename => 'rename files',
                      setacl => 'set access controls for files',
    );
    my $escpath = &HTML::Entities::encode($env{'form.currentpath'},'&<>"');
    my $rtnlink = '<a href="'.$url;
    if ($url =~ /\?/) {
        $rtnlink .= '&';
    } else {
        $rtnlink .= '?';
    }
    $rtnlink .= 'currentpath='.$escpath;
    $r->print('<h3>'.&mt('Action disallowed').'</h3>');
    $r->print(&mt('You do not have sufficient privileges to [_1]',
                  $longtext{$priv}));
    if (defined($env{'form.group'})) {
        $r->print(' '.&mt("in the group's group portfolio."));
        $rtnlink .= &group_args()
    } else {
        $r->print(' '.&mt('in this portfolio.'));
    }
    $rtnlink .= '">'.&mt('Return to directory').'</a>';
    $r->print('<br />'.$rtnlink);
    return;
}

sub coursegrp_portfolio_header {
    my ($cdom,$cnum,$grp_desc)=@_;
    my $gpterm  = &Apache::loncommon::group_term();
    my $ucgpterm = $gpterm;
    $ucgpterm =~ s/^(\w)/uc($1)/e;
    if ($env{'form.ref'}) {
        &Apache::lonhtmlcommon::add_breadcrumb
            ({href=>"/adm/coursegroups",
              text=>"Groups",
              title=>"Course Groups"});
    }
    &Apache::lonhtmlcommon::add_breadcrumb
        ({href=>"/adm/$cdom/$cnum/$env{'form.group'}/smppg?ref=$env{'form.ref'}",
          text=>&mt('Group').": $grp_desc",
          title=>&mt("Go to group's home page"),
          no_mt=>1},
         {href=>"/adm/coursegrp_portfolio?".&group_args(),
          text=>"Group Portfolio",
          title=>"Display group portfolio"});
    my $output = &Apache::lonhtmlcommon::breadcrumbs(
                         &mt('Group portfolio files - [_1]',$grp_desc),
                                                     undef,undef,undef,undef,1);
    return $output;
}

sub get_quota {
    my ($group) = @_;
    my $disk_quota;
    if (defined($group)) {
        my $grp_quota = &get_group_quota($group); # quota expressed in k
        if ($grp_quota ne '') {
            $disk_quota = $grp_quota;
        } else {
            $disk_quota = 0;
        }
    } else {
        $disk_quota = &Apache::loncommon::get_user_quota($env{'user.name'},
                                    $env{'user.domain'}); #expressed in MB
        $disk_quota = 1024 * $disk_quota; # convert from MB to kB
    }
    return $disk_quota;
}

sub suppress_embed_prompt {
    my $suppress_prompt = 0;
    if (($env{'request.role'} =~ /^st/) && ($env{'request.course.id'} ne '')) {
        if ($env{'course.'.$env{'request.course.id'}.'.suppress_embed_prompt'} eq 'yes') {
            $suppress_prompt = 1;
        }
    }
    return $suppress_prompt;
}

sub embedded_form_elems {
    my ($container) = @_;
    my $state = <<STATE;
    <input type="hidden" name="currentpath" value="$env{'form.currentpath'}" />
    <input type="hidden" name="symb" value="$env{'form.symb'}" />
    <input type="hidden" name="fieldname" value="$env{'form.fieldname'}" />
    <input type="hidden" name="mode" value="$env{'form.mode'}" />
    <input type="hidden" name="container" value="$container" />
STATE
    if ($env{'form.group'} ne '') {
        $state .= '<input type="hidden" name="group" value="'.$env{'form.group'}.'" />'."\n";
    }
    return $state;
}

# Find space available in a user's portfolio (convert to bytes)
sub free_space {
    my ($group) = @_;
    my $disk_quota = &get_quota($group); # Expressed in kB
    my ($uname,$udom) = &get_name_dom($group);
    my $portfolio_root = &get_portfolio_root();
    my $getpropath = 1;
    my $current_disk_usage = &Apache::lonnet::diskusage($udom, $uname,
                             $portfolio_root, $getpropath); # Expressed in kB
    my $free_space = 1024 * ($disk_quota - $current_disk_usage);
    return $free_space;
}

sub valid_container {
    my ($uname,$udom,$group) = @_;
    my $container_prefix;
    if ($group ne '') {
        $container_prefix = "/uploaded/$udom/$uname/groups/$group/portfolio";
    } else {
        $container_prefix = "/uploaded/$udom/$uname/portfolio";
    }
    if ($env{'form.currentpath'}) {
        $container_prefix .= $env{'form.currentpath'};
    } else {
        $container_prefix .= '/';
    }
    if ($env{'form.container'} =~ m{^\Q$container_prefix\E(.+)$}) {
        my $filename = $1;
        if ($filename eq &Apache::lonnet::clean_filename($filename)) {
            return 1;
        }
    }
    return;
}

sub invalid_parms {
    my ($r,$url,$currentpath) = @_;
    my $escpath = &HTML::Entities::encode($currentpath,'&<>"');
    my $rtnlink = '<a href="'.$url;
    if ($url =~ /\?/) {
        $rtnlink .= '&';
    } else {
        $rtnlink .= '?';
    }
    $rtnlink .= 'currentpath='.$escpath;
    $rtnlink .= '">'.&mt('Return to directory').'</a>';
    $r->print('<h3>'.&mt('Action disallowed').'</h3>');
    $r->print(&mt('Some of the data included with this request were invalid'));
    $r->print('<br />'.$rtnlink);
    return;
}

sub handler {
    # this handles file management
    my $r = shift;
    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
         ['selectfile','currentpath','meta','lockinfo','currentfile','action',
	  'fieldname','mode','rename','continue','group','access','setnum',
          'cnum','cdom','type','setroles','showversions','ref','symb']);
    my ($uname,$udom,$portfolio_root,$url,$caller,$title,$group,$grp_desc);
    if ($r->uri =~ m|^(/adm/)([^/]+)|) {
        $url = $1.$2;
        $caller = $2;
    }
    my ($can_modify,$can_delete,$can_upload,$can_setacl);
    if ($caller eq 'coursegrp_portfolio') {
    #  Needs to be in a course
        if (! ($env{'request.course.fn'})) {
        # Not in a course
            $env{'user.error.msg'}=
     "/adm/coursegrp_portfolio:rgf:0:0:Cannot view group portfolio";
            return HTTP_NOT_ACCEPTABLE;
        }
        my $earlyout = 0;
        my $view_permission = 
           &Apache::lonnet::allowed('vcg',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''));
        $env{'form.group'} =~ s/\W//g;
	$group = $env{'form.group'};
        if ($group ne '') {
            ($uname,$udom) = &get_name_dom($group);
            my %curr_groups = &Apache::longroup::coursegroups($udom,$uname,
							       $group); 
            if (%curr_groups) {
                my %grp_content = &Apache::longroup::get_group_settings(
                                                         $curr_groups{$group});
                $grp_desc = &unescape($grp_content{'description'});
                if (($view_permission) || (&Apache::lonnet::allowed('rgf',
                                      $env{'request.course.id'}.'/'.$group))) {
                    $portfolio_root = &get_portfolio_root();
                } else {
                    $r->print(&mt('You do not have the privileges required to access the shared files space for this group.'));
                    $earlyout = 1;
                }
            } else {
                $r->print(&mt('Not a valid group for this course'));
                $earlyout = 1;
            }
            $title = &mt('Group portfolio for [_1]', $group); 
        } else {
            $r->print(&mt('Invalid group'));
            $earlyout = 1;
        }
        if ($earlyout) { return OK; }
        if (&Apache::lonnet::allowed('mdg',$env{'request.course.id'})) {
            $can_modify = 1;
            $can_delete = 1;
            $can_upload = 1;
            $can_setacl = 1;
        } else {
            if (&Apache::lonnet::allowed('agf',$env{'request.course.id'}.'/'.$group)) {
                $can_setacl = 1;
            }
            if (&Apache::lonnet::allowed('ugf',$env{'request.course.id'}.'/'.$group)) {
                $can_upload = 1;
            }
            if (&Apache::lonnet::allowed('mgf',$env{'request.course.id'}.'/'.$group)) {
                $can_modify = 1;
            }
            if (&Apache::lonnet::allowed('dgf',$env{'request.course.id'}.'/'.$group)) {
                $can_delete = 1;
            }
        }
    } else {
        ($uname,$udom) = &get_name_dom();
        $portfolio_root = &get_portfolio_root();
        $title = 'My Space';
        $can_modify = 1;
        $can_delete = 1;
        $can_upload = 1;
        $can_setacl = 1;
    }

    my $port_path = &get_port_path();
    &Apache::loncommon::no_cache($r);
    &Apache::loncommon::content_type($r,'text/html');
    $r->send_http_header;
    # Give the LON-CAPA page header
    my $brcrum = [{href=>"/adm/portfolio",text=>"Portfolio Manager"}];

    my $js = '<script type="text/javascript"
                src="/res/adm/includes/file_upload.js"></script>';
    
    if ($env{"form.mode"} eq 'selectfile'){
        $r->print(&Apache::loncommon::start_page($title, $js,
						 {'only_body' => 1}));
    } elsif ($env{'form.action'} eq 'rolepicker') {
        $r->print(&Apache::loncommon::start_page('New role-based condition', $js,
                                                 {'no_nav_bar'  => 1, }));
    } elsif ($caller eq 'coursegrp_portfolio') {
        $r->print(&Apache::loncommon::start_page($title, $js));
    } else {
        $r->print(&Apache::loncommon::start_page($title, $js,
                                                 {'bread_crumbs' => $brcrum}));
        if (!&Apache::lonnet::usertools_access($uname,$udom,'portfolio')) {
            $r->print('<h2>'.&mt('No user portfolio available') .'</h2>'.
                      &mt('This is a result of one of the following:').'<ul>'.
                      '<li>'.&mt('The administrator of this domain has disabled portfolio functionality for this specific user.').'</li>'.
                      '<li>'.&mt('The domain has been configured to disable, by default, portfolio functionality for all users in the domain.').'</li>'.
                      '</ul>');
            $r->print(&Apache::loncommon::end_page());
            return OK;
        }
    }
    $r->rflush();
    # Check if access to portfolio is blocked by one or more blocking events in courses.
    my $clientip = &Apache::lonnet::get_requestor_ip($r);
    my ($blocked,$blocktext) = 
        &Apache::loncommon::blocking_status('port',$clientip,$uname,$udom);
    if ($blocked) {
        my $evade_block;
        # If portfolio display is in a window popped up from a "Select Portfolio Files"
        # link in a .task resource, check if access to the task included proctor validation
        # of check-in to a slot limited by IP.
        # If so, and the slot is between its open and close dates, override the block. 
        if ($env{'request.course.id'} && $env{'form.symb'}) {
            (undef,undef,my $res) = &Apache::lonnet::decode_symb($env{'form.symb'});
            if ($res =~ /\.task$/i) {
                my %history =
                    &Apache::lonnet::restore($env{'form.symb'},$env{'request.course.id'},
                                             $env{'user.domain'},$env{'user.name'});
                my $version = $history{'resource.0.version'};
                if ($history{'resource.'.$version.'.0.checkedin'}) {
                    if ($history{'resource.'.$version.'.0.checkedin.slot'}) {
                        my %slot = &Apache::lonnet::get_slot($history{'resource.'.$version.'.0.checkedin.slot'});
                        if ($slot{'ip'}) {
                            if (&Apache::loncommon::check_ip_acc($slot{'ip'})) {
                                my $now = time;
                                if (($slot{'slottime'} < $now) && ($slot{'endtime'} > $now)) {
                                    $evade_block = 1;
                                }
                            }
                        }
                    }
                }
            }
        }
        unless ($evade_block) {
            $r->print($blocktext);
            $r->print(&Apache::loncommon::end_page());
            return OK;
        }
    }
    if (($env{'form.currentpath'}) && ($env{'form.currentpath'} ne '/')) {
        my $clean_currentpath = '/'.&Apache::loncommon::clean_path($env{'form.currentpath'}).'/';
        unless ($env{'form.currentpath'} eq $clean_currentpath) {
            &invalid_parms($r,$url);
            $r->print(&Apache::loncommon::end_page());
            return OK;
        }
    }
    if ($env{'form.container'}) {
        unless (&valid_container($uname,$udom,$group)) {
            &invalid_parms($r,$url,$env{'form.currentpath'});
            $r->print(&Apache::loncommon::end_page());
            return OK;
        }
    }
    if (($env{'form.storeupl'}) & (!$env{'form.uploaddoc.filename'})){
   	$r->print(
            '<p><span class="LC_warning">'
           .&mt('No file was selected to upload.')
           .'</span><br />'
           .&mt('To upload a file, click [_1]Browse...[_2] and select a file, then click [_1]Upload[_2].'
                ,'<strong>','</strong>')
           .'</p>'
        );
    }
    if ($env{'form.meta'}) {
        &open_form($r,$url);
        $r->print(&mt('Edit Metadata').'<br />');
        &close_form($r,$url);
    }
    if ($env{'form.uploaddoc.filename'}) {
        if ($can_upload) {
	    &upload($r,$url,$group);
        } else {
            &missing_priv($r,$url,'upload');
        }
    } elsif ($env{'form.action'} eq 'process_overwrite') {
        if ($can_upload) {
            &overwrite($r,$url,$group);
        } else {
            &missing_priv($r,$url,'existingfile');
        }
    } elsif ($env{'form.action'} eq 'upload_embedded') {
	if ($can_upload) {
            my $disk_quota = &get_quota($group);
            my $getpropath = 1;
            my $current_disk_usage = 
                &Apache::lonnet::diskusage($udom,$uname,$portfolio_root,$getpropath);
            my $container = &HTML::Entities::encode($env{'form.container'},'<>&"');
            my $state = &embedded_form_elems($container).
                        '<input type="hidden" name="action" value="modify_orightml" />';
	    my ($result,$flag) =
                &Apache::loncommon::upload_embedded('portfolio',$port_path,$uname,$udom,
                    $group,$portfolio_root,$group,$disk_quota,$current_disk_usage,$state,$url);
            $r->print($result.&done('Return to directory',$url));
        } else {
            &missing_priv($r,$url,'upload');
        }
    } elsif ($env{'form.action'} eq 'modify_orightml') {
        if ($can_upload) {
            my $result = 
                &Apache::loncommon::modify_html_refs('portfolio',$port_path,$uname,
                                                     $udom,$portfolio_root);
            $r->print($result.
                      &done('Return to directory',$url));
        } else {
            &missing_priv($r,$url,'upload');
        }
    } elsif ($env{'form.action'} eq 'delete' && $env{'form.confirmed'}) {
        if ($can_delete) {
	    &delete_confirmed($r,$url,$group);
        } else {
            &missing_priv($r,$url,'delete');
        }
    } elsif ($env{'form.action'} eq 'delete') {
        if ($can_delete) {
	    &delete($r,$url,$group);
        } else {
            &missing_priv($r,$url,'delete');
        }
    } elsif ($env{'form.action'} eq 'deletedir' && $env{'form.confirmed'}) {
        if ($can_delete) {
	    &delete_dir_confirmed($r,$url,$group);
        } else {
            &missing_priv($r,$url,'delete');
        }
    } elsif ($env{'form.action'} eq 'deletedir') {
        if ($can_delete) {
	    &delete_dir($r,$url);
        } else {
            &missing_priv($r,$url,'delete');
        }
    } elsif ($env{'form.action'} eq 'rename' && $env{'form.confirmed'}) {
        if ($can_modify) {
	    &rename_confirmed($r,$url,$group);
        } else {
            &missing_priv($r,$url,'rename');
        }
    } elsif ($env{'form.rename'}) {
        $env{'form.selectfile'} = $env{'form.rename'};
        $env{'form.action'} = 'rename';
        if ($can_modify) {
	    &rename($r,$url,$group);
        } else {
            &missing_priv($r,$url,'rename');
        }
    } elsif ($env{'form.access'}) {
        $env{'form.selectfile'} = $env{'form.access'};
        if (!defined($env{'form.action'})) { 
            $env{'form.action'} = 'chgaccess';
        }
        &display_access($r,$url,$group,$can_setacl,$port_path,$env{'form.action'});
    } elsif (($env{'form.action'} eq 'chgaccess') || 
             ($env{'form.action'} eq 'chgconditions')) {
        if ($can_setacl) {
            &update_access($r,$url,$group,$port_path);
        } else {
            &missing_priv($r,$url,'setacl');
        }
    } elsif ($env{'form.action'} eq 'rolepicker') {
        if ($can_setacl) { 
            &role_options_window($r);
        } else {
            &missing_priv($r,$url,'setacl');
        }
    } elsif ($env{'form.createdir'}) {
        if ($can_upload) {
	    &createdir($r,$url,$group);
        } else {
            &missing_priv($r,$url,'upload');
        }
    } elsif ($env{'form.lockinfo'}) {
        &lock_info($r,$url,$group);
    } else {
        if ($env{'form.action'} eq 'cancel_overwrite') {
            if ($can_upload) {
                my $formname = 'existingfile';
                my $fname = &Apache::lonnet::clean_filename($env{'form.filename'});
                $env{'form.'.$formname.'.filename'} = $fname;
                my $subdir = $port_path.$env{'form.currentpath'};
                $subdir =~ s{(/)$}{};
                &Apache::lonnet::userfileupload($formname,'canceloverwrite',$subdir);
            }
        }
	my $current_path='/';
	if ($env{'form.currentpath'}) {
	    $current_path = $env{'form.currentpath'};
	}
        if ($caller eq 'coursegrp_portfolio') {
            &Apache::lonhtmlcommon::clear_breadcrumbs();
            $r->print(&coursegrp_portfolio_header($udom,$uname,$grp_desc));
        }
        my ($dirlistref,$listerror) =
            &get_dir_list($portfolio_root,$current_path,$group);
	if ($listerror eq 'no_such_dir'){
	    # two main reasons for this:
            #    1) never been here, so directory structure not created
	    #    2) back-button navigation after deleting a directory
	    if ($current_path eq '/'){
	        &Apache::lonnet::mkdiruserfile($uname,$udom,
					       &get_port_path());
	    } else {
                # some directory that snuck in get rid of the directory
                # from the recent pulldown, just in case
		&Apache::lonhtmlcommon::remove_recent('portfolio',
						      [$current_path]);
		$current_path = '/'; # force it back to the root        
	    }
	    # now grab the directory list again, for the first time
            ($dirlistref,$listerror) =
                &get_dir_list($portfolio_root,$current_path,$group);
        }
	# need to know if directory is empty so it can be removed if desired
        my $is_empty;
        if ($listerror eq 'empty') {
            $is_empty = 1;
        } elsif (ref($dirlistref) eq 'ARRAY') {
            if ((scalar(@{$dirlistref}) == 2) && ($dirlistref->[0] =~ /^\.+\&/)
                && ($dirlistref->[1] =~ /^\.+\&/))  {
                $is_empty = 1;
            }
        }
	&display_common($r,$url,$current_path,$is_empty,$dirlistref,
			$can_upload,$group);
        &display_directory($r,$url,$current_path,$is_empty,$dirlistref,$group,
                           $can_upload,$can_modify,$can_delete,$can_setacl);
    }
    $r->print(&Apache::loncommon::end_page());
    return OK;
}

1;
__END__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>