[LON-CAPA-cvs] cvs: loncom /auth lonacc.pm publiccheck.pm /interface lonhtmlcommon.pm portfolio.pm /lonnet/perl lonnet.pm

raeburn lon-capa-cvs@mail.lon-capa.org
Fri, 16 Jun 2006 22:37:35 -0000


This is a MIME encoded message

--raeburn1150497455
Content-Type: text/plain

raeburn		Fri Jun 16 18:37:35 2006 EDT

  Modified files:              
    /loncom/interface	portfolio.pm lonhtmlcommon.pm 
    /loncom/auth	lonacc.pm publiccheck.pm 
    /loncom/lonnet/perl	lonnet.pm 
  Log:
  Enable users to set access controls on portfolio files to permit public viewing. Third iteration of data structure in file_permissions.db for storage of access contol records. Now uses key = value for individual access control records and a key = anonymous hash for fast look-ups of all records for a single file.   Use of a temporary lock record to prevent others modifying access control records for a specific portfolio file while the record for the same file is being updated. Additional argument added to call to lonhtmlcommon::datesetter() to optionally suppress display of calendar link.
  
  
--raeburn1150497455
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20060616183735.txt"

Index: loncom/interface/portfolio.pm
diff -u loncom/interface/portfolio.pm:1.103 loncom/interface/portfolio.pm:1.104
--- loncom/interface/portfolio.pm:1.103	Wed Jun  7 17:15:01 2006
+++ loncom/interface/portfolio.pm	Fri Jun 16 18:37:10 2006
@@ -211,17 +211,16 @@
                 my $curr_access;
                 my $pub_access = 0;
                 foreach my $key (sort(keys(%{$access_controls{$fullpath}}))) {
-                    my ($scope,$end,$start) = ($key =~ /^\d+:(\w+)_(\d*)_?(\d*)$/);
+                    my ($scope,$end,$start) = ($key =~ /^[^:]+:([a-z]+)_(\d*)_?(\d*)$/);
                     if (($now > $start) && (!$end || $end > $now)) {
                         if ($scope eq 'public')  {
                             $pub_access = 1;
-                            last;
                         }
                     }
                 }
                 if (!$pub_access) {
                     $curr_access = 'Private'
-                } elsif ($pub_access) {
+                } else {
                     $curr_access = 'Public';
                 }
                 $r->print('<td><img src="'.&Apache::loncommon::icon($filename).'"></td>');
@@ -442,8 +441,213 @@
     $r->print(&done(undef,$url,$group));
 }
 
+sub display_access {
+    my ($r,$url,$group) = @_;
+    my ($uname,$udom) = &get_name_dom($group);
+    my $file_name = $env{'form.currentpath'}.$env{'form.access'};
+    $file_name = &prepend_group($file_name,$group);
+    my $current_permissions = &Apache::lonnet::get_portfile_permissions($udom,
+                                                                        $uname);
+    my %access_controls = &Apache::lonnet::get_access_controls($current_permissions,$group,$file_name);
+    &open_form($r,$url);
+    $r->print('<h3>'.&mt('Allowing others to retrieve portfolio file: [_1]',$env{'form.currentpath'}.$env{'form.access'}).'</h3>'."\n");
+    $r->print(&mt('Access to this file by others can be set to be one the following types: public.').'<br /><ul><li>'.&mt('Public files are available to anyone without the need for login.').'</li></ul><br />');
+    &access_setting_table($r,$access_controls{$file_name});
+    &close_form($r,$url,$group);
+}
+
+sub update_access {
+    my ($r,$url,$group) = @_;
+    my $totalprocessed = 0;
+    my %processing;
+    my %title  = (
+                         'activate' => 'New controls added',
+                         'delete'   => 'Existing controls deleted',
+                         'update'   => 'Existing controls 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);
+            }
+        }
+    }
+    my $file_name = $env{'form.currentpath'}.$env{'form.selectfile'};
+    $r->print('<h3>'.&mt('Allowing others to retrieve portfolio file: [_1]',$file_name).'</h3>'."\n");
+    $file_name = &prepend_group($file_name,$group);
+    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_row());
+            $r->print('<th>'.&mt('Type of change').'</th><th>'.&mt('Access control').'</th><th>'.&mt('Start date').'</th><th>'.&mt('End date').'</th>');
+            $r->print(&Apache::loncommon::end_data_table_row());
+            foreach my $chg (sort(keys(%processing))) {
+                if (@{$processing{$chg}} > 0) {
+                    if ($chg eq 'delete') {
+                        if (!($deloutcome eq 'ok')) {
+                            $errors .= &mt('A problem occurred deleting access controls: [_1]',$deloutcome);
+                            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;
+                    foreach my $key (sort(keys(%{$$changes{$chg}}))) {
+                        if ($count) {
+                            $r->print(&Apache::loncommon::start_data_table_row());
+                        }
+                        my ($num,$scope,$end,$start) = 
+                                   ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
+                        my $newkey = $key;
+                        if ($chg eq 'activate') {
+                            $newkey =~ s/^(\d+)/$$translation{$1}/;
+                        }
+                        my %content = &Apache::lonnet::parse_access_controls(
+                                                     $$updated_controls{$newkey});
+                        if ($chg eq 'delete') {
+                            $showstart = &mt('Deleted');
+                            $showend = $showstart;
+                        } else {
+                            $showstart = localtime($start);
+                            if ($end == 0) {
+                                $showend = &mt('No end date');
+                            } else {
+                                $showend = localtime($end);
+                            }
+                        }
+                        $r->print('<td>'.&mt($scope).'</td><td>'.$showstart.
+                                  '</td><td>'. $showend.'</td>');
+                        $r->print(&Apache::loncommon::end_data_table_row());
+                        $count ++;
+                    }
+                }
+            }
+            $r->print(&Apache::loncommon::end_data_table());
+        } else {
+            if ((@{$processing{'activate'}} > 0) || (@{$processing{'update'}} > 0)) {
+                $errors .= &mt('A problem occurred storing access control settings: [_1]',$outcome);
+            }
+        }
+        if ($errors) { 
+            $r->print($errors);
+        }
+    }
+    $r->print('<br /><a href="'.$url.'?access='.$env{'form.selectfile'}.'&currentpath='.$env{'form.currentpath'}.'">'.&mt('Display all access settings for this file').'</a>');
+    return;
+}
+
+sub build_access_record {
+    my ($num,$scope,$start,$end) = @_;
+    my $record = '<scope type="'.$scope.'"><start>'.$start.'</start><end>'.$end.
+                 '</end></scope>';
+    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 access_setting_table {
+    my ($r,$access_controls) = @_;
+    my ($public,$publictext);
+    $publictext = '<b>'.&mt('Off').'</b>';
+    my ($guest,$guesttext);
+    $guesttext = '<b>'.&mt('Off').'</b>';
+    my @courses = ();
+    my @groups = ();
+    my @domains = ();
+    my @users = ();
+    my $now = time;
+    my $then = $now + (60*60*24*180); # six months approx.
+    foreach my $key (sort(keys(%{$access_controls}))) {
+        my ($scope) = ($key =~ /^[^:]+:([a-z]+)_\d*_?\d*$/);
+        if ($scope eq 'public') {
+            $public = $key;
+            $publictext = '<b>'.&mt('On').'</b>';
+        }
+    }
+    $r->print(&Apache::loncommon::start_data_table());
+    $r->print(&Apache::loncommon::start_data_table_row());
+    $r->print('<th>'.&mt('Access Type').'</th><th colspan="3">'.
+              &mt('Settings').'</th>'."\n");
+    $r->print(&Apache::loncommon::end_data_table_row());
+    $r->print(&Apache::loncommon::start_data_table_row());
+    $r->print('<td><b>Public</b><br />'.$publictext.'</td><td colspan="3">');
+    $r->print(&Apache::loncommon::start_data_table());
+    $r->print(&Apache::loncommon::start_data_table_row());
+    my ($pub_startdate,$pub_enddate,$pub_action,$pub_noend);
+    if ($public) {
+        my ($num,$end,$start) = ($public =~ /^([^:]+):[a-z]+_(\d*)_?(\d*)$/);
+        if ($end == 0) {
+            $pub_noend = 'checked="checked"';
+        }
+        $pub_action = '<td><input type="checkbox" name="delete" value="'.$num.
+                      '" />'.&mt('Delete').'<br /><input type="checkbox" '.
+                      'name="update" value="'.$num.'" />'.&mt('Update').
+                      '<input type="hidden" name="scope_'.$num.'"'.
+                      ' value="public" /></td>';
+        $pub_startdate = &Apache::lonhtmlcommon::date_setter('portform',
+                          'startdate_'.$num,$start,undef,undef,undef,1,undef,
+                          undef,undef,1);
+        $pub_enddate = &Apache::lonhtmlcommon::date_setter('portform',
+                          'enddate_'.$num,$end,undef,undef,undef,1,undef,
+                          undef,undef,1).
+                       '&nbsp;&nbsp;<nobr><input type="checkbox" name="noend_'.
+                       $num.'" '.$pub_noend.' />'.&mt('No end date').'</nobr>';
+    } else {
+        $pub_action = '<input type="checkbox" name="activate" value="0" />'.
+                      &mt('Activate').
+                      '<input type="hidden" name="scope_0" value="public" />';
+        $pub_startdate = &Apache::lonhtmlcommon::date_setter('portform',
+                          'startdate_0',$now,undef,undef,undef,1,undef,
+                          undef,undef,1);
+        $pub_enddate = &Apache::lonhtmlcommon::date_setter('portform',
+                          'enddate_0',$then,,undef,undef,undef,1,undef,
+                          undef,undef,1).
+                       '&nbsp;&nbsp<nobr><input type="checkbox" '.
+                       'name="noend_0" />'.&mt('No end date').
+                       '</nobr>';
+
+    }
+    $r->print('<td>'.$pub_action.'</td><td>'.&mt('Start: ').$pub_startdate.
+              '<br />'.&mt('End: ').$pub_enddate.'</td>');
+    $r->print(&Apache::loncommon::end_data_table_row());
+    $r->print(&Apache::loncommon::end_data_table());
+    $r->print('</td>');
+    $r->print(&Apache::loncommon::end_data_table_row());
+    $r->print(&Apache::loncommon::end_data_table());
+}
+
 sub select_files {
-    my ($r,$group)=@_;
+    my ($r,$group) = @_;
     if ($env{'form.continue'} eq 'true') {
         # here we update the selections for the currentpath
         # eventually, have to handle removing those not checked, but . . . 
@@ -771,6 +975,12 @@
         $env{'form.selectfile'} = $env{'form.rename'};
         $env{'form.action'} = 'rename';
 	&rename($r,$url,$group);
+    } elsif ($env{'form.access'}) {
+        $env{'form.selectfile'} = $env{'form.access'};
+        $env{'form.action'} = 'chgaccess';
+        &display_access($r,$url,$group);
+    } elsif ($env{'form.action'} eq 'chgaccess') {
+        &update_access($r,$url,$group);
     } elsif ($env{'form.createdir'}) {
 	&createdir($r,$url,$group);
     } elsif ($env{'form.lockinfo'}) {
Index: loncom/interface/lonhtmlcommon.pm
diff -u loncom/interface/lonhtmlcommon.pm:1.133 loncom/interface/lonhtmlcommon.pm:1.134
--- loncom/interface/lonhtmlcommon.pm:1.133	Wed Jun 14 14:34:46 2006
+++ loncom/interface/lonhtmlcommon.pm	Fri Jun 16 18:37:10 2006
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common html routines
 #
-# $Id: lonhtmlcommon.pm,v 1.133 2006/06/14 18:34:46 albertel Exp $
+# $Id: lonhtmlcommon.pm,v 1.134 2006/06/16 22:37:10 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -288,7 +288,7 @@
 ##############################################
 sub date_setter {
     my ($formname,$dname,$currentvalue,$special,$includeempty,$state,
-        $no_hh_mm_ss,$defhour,$defmin,$defsec) = @_;
+        $no_hh_mm_ss,$defhour,$defmin,$defsec,$nolink) = @_;
     my $wasdefined=1;
     if (! defined($state) || $state ne 'disabled') {
         $state = '';
@@ -436,17 +436,24 @@
     $hourselector .= "  </select>\n";
     my $minuteselector = qq{<input type="text" name="$dname\_minute" $special $state value="$min" size="3" />};
     my $secondselector= qq{<input type="text" name="$dname\_second" $special $state value="$sec" size="3" />};
-    my $cal_link = qq{<a href="javascript:$dname\_opencalendar()">};
+    my $cal_link;
+    if (!$nolink) {
+        $cal_link = qq{<a href="javascript:$dname\_opencalendar()">};
+    }
     #
     if ($no_hh_mm_ss) {
-        $result .= &mt('[_1] [_2] [_3] [_4]Select Date[_5]',
-                       $monthselector,$dayselector,$yearselector,
-                       $cal_link,'</a>');
+        $result .= &mt('[_1] [_2] [_3] ',
+                       $monthselector,$dayselector,$yearselector);
+        if (!$nolink) {
+            $result .= &mt('[_4]Select Date[_5]',$cal_link,'</a>');
+        }
     } else {
-        $result .= &mt('[_1] [_2] [_3] [_4] [_5]m [_6]s [_7]Select Date[_8]',
-                       $monthselector,$dayselector,$yearselector,
-                       $hourselector,$minuteselector,$secondselector,
-                       $cal_link,'</a>');
+        $result .= &mt('[_1] [_2] [_3] [_4] [_5]m [_6]s ',
+                      $monthselector,$dayselector,$yearselector,
+                      $hourselector,$minuteselector,$secondselector);
+        if (!$nolink) {
+            $result .= &mt('[_7]Select Date[_8]',$cal_link,'</a>');
+        }
     }
     $result .= "</nobr>\n<!-- end $dname date setting form -->\n";
     return $result;
Index: loncom/auth/lonacc.pm
diff -u loncom/auth/lonacc.pm:1.78 loncom/auth/lonacc.pm:1.79
--- loncom/auth/lonacc.pm:1.78	Fri Jun  2 15:38:21 2006
+++ loncom/auth/lonacc.pm	Fri Jun 16 18:37:29 2006
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Cookie Based Access Handler
 #
-# $Id: lonacc.pm,v 1.78 2006/06/02 19:38:21 albertel Exp $
+# $Id: lonacc.pm,v 1.79 2006/06/16 22:37:29 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -135,6 +135,34 @@
     $r->headers_in->unset('Content-length');
 }
 
+sub portfolio_access {
+    my ($udom,$unum,$file_name,$group) = @_;
+    my $current_perms = &Apache::lonnet::get_portfile_permissions($udom,$unum);
+    my %access_controls = &Apache::lonnet::get_access_controls(
+                                             $current_perms,$group,$file_name);
+    my ($public);
+    my $now = time;
+    my $access_hash = $access_controls{$file_name};
+    if (ref($access_hash) eq 'HASH') {
+        foreach my $key (keys(%{$access_hash})) {
+            my ($num,$scope,$end,$start) = ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
+            if ($start > $now) {
+                next;
+            }
+            if ($end && $end<$now) {
+                next;
+            }
+            if ($scope eq 'public') {
+                $public = $key;
+                last;
+            }
+        }
+        if ($public) {
+            return 'ok';
+        }
+    }
+    return;
+}
 
 sub handler {
     my $r = shift;
@@ -197,7 +225,18 @@
 	    &Apache::lonacc::get_posted_cgi($r);
 
 # ---------------------------------------------------------------- Check access
-
+            my $now = time;
+            if ($requrl =~ m#/+uploaded/([^/]+)/([^/]+)/portfolio(/.+)$#) {
+                my $result = &portfolio_access($1,$2,$3);
+                if ($result eq 'ok') {
+                    return OK;
+                }
+            } elsif ($requrl =~ m#/+uploaded/([^/]+)/([^/]+)/groups/([^/]+)/portfolio/(.+)$#) {
+                my $result = &portfolio_access($1,$2,$4.'/'.$3,$3);
+                if ($result eq 'ok') {
+                    return OK;
+                }
+            }
             if ($requrl!~/^\/adm|public|prtspool\//) {
 		my $access=&Apache::lonnet::allowed('bre',$requrl);
                 if ($access eq '1') {
@@ -310,6 +349,18 @@
     if ($requrl=~m|^/+adm/+help/+|) {
 	return OK;
     }
+# ------------------------------------- See if this is a viewable portfolio file
+    if ($requrl =~ m#/+uploaded/([^/]+)/([^/]+)/portfolio(/.+)$#) {
+        my $result = &portfolio_access($1,$2,$3);
+        if ($result eq 'ok') {
+            return OK;
+        }
+    } elsif ($requrl =~ m#/+uploaded/([^/]+)/([^/]+)/groups/([^/]+)/portfolio/(.+)$#) {
+        my $result = &portfolio_access($1,$2,$4.'/'.$3,$3);
+        if ($result eq 'ok') {
+            return OK;
+        }
+    }
 # -------------------------------------------------------------- Not authorized
     $requrl=~/\.(\w+)$/;
 #    if ((&Apache::loncommon::fileembstyle($1) eq 'ssi') ||
Index: loncom/auth/publiccheck.pm
diff -u loncom/auth/publiccheck.pm:1.2 loncom/auth/publiccheck.pm:1.3
--- loncom/auth/publiccheck.pm:1.2	Thu Apr 13 15:07:33 2006
+++ loncom/auth/publiccheck.pm	Fri Jun 16 18:37:29 2006
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Cookie Based Access Handler
 #
-# $Id: publiccheck.pm,v 1.2 2006/04/13 19:07:33 albertel Exp $
+# $Id: publiccheck.pm,v 1.3 2006/06/16 22:37:29 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -57,24 +57,60 @@
     }
     if ($requrl=~m|^/public/|
 	|| (&Apache::lonnet::metadata($requrl,'copyright') eq 'public')) {
-        &Apache::lonnet::logthis('Granting public access: '.$requrl);
-	if ($env{'user.name'} ne 'public' 
-	    && $env{'user.domain'} ne 'public') {
-	    my $cookie=
-		&Apache::lonauth::success($r,'public','public','public');
-	    my $lonidsdir=$r->dir_config('lonIDsDir');
-	    &Apache::lonnet::transfer_profile_to_env($lonidsdir,$cookie);
-	    $r->header_out('Set-cookie',"lonID=$cookie; path=/");
-	}
-	&Apache::lonacc::get_posted_cgi($r);
-        $env{'request.state'} = "published";
-        $env{'request.publicaccess'} = 1;
-        $env{'request.filename'} = $r->filename;
+        &process_public($r,$requrl);
         return OK;
+    } elsif ($requrl =~ m#/+uploaded/([^/]+)/([^/]+)/portfolio(/.+)$#) {
+        if (&process_portfolio($1,$2,$3)) {
+            &process_public($r,$requrl);
+            return OK;
+        } 
+    } elsif ($requrl =~ m#/+uploaded/([^/]+)/([^/]+)/groups/([^/]+)/portfolio/(.+)$#) {
+        if (&process_portfolio($1,$2,$3.'/'.$4,$3)) {
+            &process_public($r,$requrl);
+            return OK;
+        }
     }
     return DECLINED;
 }
 
+sub process_public {
+    my ($r,$requrl) = @_;
+    &Apache::lonnet::logthis('Granting public access: '.$requrl);
+    if ($env{'user.name'} ne 'public' && $env{'user.domain'} ne 'public') {
+        my $cookie=&Apache::lonauth::success($r,'public','public','public');
+        my $lonidsdir=$r->dir_config('lonIDsDir');
+        &Apache::lonnet::transfer_profile_to_env($lonidsdir,$cookie);
+        $r->header_out('Set-cookie',"lonID=$cookie; path=/");
+    }
+    &Apache::lonacc::get_posted_cgi($r);
+    $env{'request.state'} = "published";
+    $env{'request.publicaccess'} = 1;
+    $env{'request.filename'} = $r->filename;
+    return;
+}
+
+sub process_portfolio {
+    my ($udom,$unum,$file_name,$group) = @_;
+    my $current_perms = &Apache::lonnet::get_portfile_permissions($udom,$unum);
+    my %access_controls = &Apache::lonnet::get_access_controls($current_perms,$group,$file_name);
+    my $public_access = 0;
+    my $now = time;
+    foreach my $key (keys(%{$access_controls{$file_name}})) {
+        my ($num,$scope,$end,$start) = ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/);
+        if ($start > $now) {
+            next;
+        }
+        if ($end && $end<$now) {
+            next;
+        }
+        if ($scope eq 'public') {
+            $public_access = 1;
+            last;
+        }
+    }
+    return $public_access;
+}
+
 1;
 
 __END__
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.748 loncom/lonnet/perl/lonnet.pm:1.749
--- loncom/lonnet/perl/lonnet.pm:1.748	Thu Jun  8 16:53:34 2006
+++ loncom/lonnet/perl/lonnet.pm	Fri Jun 16 18:37:35 2006
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.748 2006/06/08 20:53:34 albertel Exp $
+# $Id: lonnet.pm,v 1.749 2006/06/16 22:37:35 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -4664,31 +4664,140 @@
 
 #---------------------------------------------Get portfolio file access controls
 
-sub get_access_controls  {
+sub get_access_controls {
     my ($current_permissions,$group,$file) = @_;
-    my @access_checks = ();
     my %access; 
     if (defined($file)) {
-        @access_checks = ($file);
+        if (ref($$current_permissions{$file."\0".'accesscontrol'}) eq 'HASH') {
+            foreach my $control (keys(%{$$current_permissions{$file."\0".'accesscontrol'}})) {
+                $access{$file}{$control} = $$current_permissions{$file."\0".$control};
+            }
+        }
     } else {
-        @access_checks = keys(%{$current_permissions});
+        foreach my $key (keys(%{$current_permissions})) {
+            if ($key =~ /\0accesscontrol$/) {
+                if (defined($group)) {
+                    if ($key !~ m-^\Q$group\E/-) {
+                        next;
+                    }
+                }
+                my ($fullpath) = split(/\0/,$key);
+                if (ref($$current_permissions{$key}) eq 'HASH') {
+                    foreach my $control (keys(%{$$current_permissions{$key}})) {
+                        $access{$fullpath}{$control}=$$current_permissions{$fullpath."\0".$control};
+                    }
+                }
+            }
+        }
     }
-    foreach my $file_name (@access_checks) {
-        my $value = $$current_permissions{$file_name};
-        if (defined($group)) {
-            if ($file_name !~ m-^\Q$group\E/-) {
-                next;
+    return %access;
+}
+
+sub parse_access_controls {
+    my ($access_item) = @_;
+    my %content;
+    my $token;
+    my $parser=HTML::TokeParser->new(\$access_item);
+    while ($token=$parser->get_token) {
+        if ($token->[0] eq 'S')  {
+            my $entry=$token->[1];
+            if ($entry eq 'scope') {
+                my $type = $token->[2]{'type'};
+            } else {
+                my $value=$parser->get_text('/'.$entry);
+                $content{$entry}=$value;
             }
         }
-        if (ref($value) eq "ARRAY") {
-            foreach my $stored_what (@{$value}) {
-                if (ref($stored_what) eq 'HASH') {
-                    $access{$file_name} = $$stored_what{'access'};
+    }
+    return %content;
+}
+
+sub modify_access_controls {
+    my ($file_name,$changes,$domain,$user)=@_;
+    my ($outcome,$deloutcome);
+    my %store_permissions;
+    my %new_values;
+    my %new_control;
+    my %translation;
+    my @deletions = ();
+    my $now = time;
+    if (exists($$changes{'activate'})) {
+        if (ref($$changes{'activate'}) eq 'HASH') {
+            my @newitems = sort(keys(%{$$changes{'activate'}}));
+            my $numnew = scalar(@newitems);
+            for (my $i=0; $i<$numnew; $i++) {
+                my $newkey = $newitems[$i];
+                my $newid = &Apache::loncommon::get_cgi_id();
+                $newkey =~ s/^(\d+)/$newid/;
+                $translation{$1} = $newid;
+                $new_values{$file_name."\0".$newkey} = 
+                                          $$changes{'activate'}{$newitems[$i]};
+                $new_control{$newkey} = $now;
+            }
+        }
+    }
+    my %todelete;
+    my %changed_items;
+    foreach my $action ('delete','update') {
+        if (exists($$changes{$action})) {
+            if (ref($$changes{$action}) eq 'HASH') {
+                foreach my $key (keys(%{$$changes{$action}})) {
+                    my ($itemnum) = ($key =~ /^([^:]+):/);
+                    if ($action eq 'delete') { 
+                        $todelete{$itemnum} = 1;
+                    } else {
+                        $changed_items{$itemnum} = $key;
+                    }
                 }
             }
         }
     }
-    return %access;
+    # get lock on access controls for file.
+    my $lockhash = {
+                  $file_name."\0".'locked_access_records' => $env{'user.name'}.
+                                                       ':'.$env{'user.domain'},
+                   }; 
+    my $tries = 0;
+    my $gotlock = &newput('file_permissions',$lockhash,$domain,$user);
+   
+    while (($gotlock ne 'ok') && $tries <3) {
+        $tries ++;
+        sleep 1;
+        $gotlock = &newput('file_permissions',$lockhash,$domain,$user);
+    }
+    if ($gotlock eq 'ok') {
+        my %curr_permissions = &dump('file_permissions',$domain,$user,$file_name);
+        my ($tmp)=keys(%curr_permissions);
+        if ($tmp=~/^error:/) { undef(%curr_permissions); }
+        if (exists($curr_permissions{$file_name."\0".'accesscontrol'})) {
+            my $curr_controls = $curr_permissions{$file_name."\0".'accesscontrol'};
+            if (ref($curr_controls) eq 'HASH') {
+                foreach my $control_item (keys(%{$curr_controls})) {
+                    my ($itemnum) = ($control_item =~ /^([^:]+):/);
+                    if (defined($todelete{$itemnum})) {
+                        push(@deletions,$file_name."\0".$control_item);
+                    } else {
+                        if (defined($changed_items{$itemnum})) {
+                            $new_control{$changed_items{$itemnum}} = $now;
+                            push(@deletions,$file_name."\0".$control_item);
+                            $new_values{$file_name."\0".$changed_items{$itemnum}} = $$changes{'update'}{$changed_items{$itemnum}};
+                        } else {
+                            $new_control{$control_item} = $$curr_controls{$control_item};
+                        }
+                    }
+                }
+            }
+        }
+        $deloutcome = &del('file_permissions',\@deletions,$domain,$user);
+        $new_values{$file_name."\0".'accesscontrol'} = \%new_control;
+        $outcome = &put('file_permissions',\%new_values,$domain,$user);
+        #  remove lock
+        my @del_lock = ($file_name."\0".'locked_access_records');
+        my $dellockoutcome = &del('file_permissions',\@del_lock,$domain,$user);
+    } else {
+        $outcome = "error: could not obtain lockfile\n";  
+    }
+    return ($outcome,$deloutcome,\%new_values,\%translation);
 }
 
 #------------------------------------------------------Get Marked as Read Only
@@ -4708,9 +4817,7 @@
         if (ref($value) eq "ARRAY"){
             foreach my $stored_what (@{$value}) {
                 my $cmp2=$stored_what;
-                if (ref($stored_what eq 'HASH')) {
-                    next;
-                } elsif (ref($stored_what eq 'ARRAY')) {
+                if (ref($stored_what eq 'ARRAY')) {
                     $cmp2=join('',@{$stored_what});
                 }
                 if ($cmp1 eq $cmp2) {
@@ -4770,9 +4877,7 @@
         if (ref($current_locks) eq "ARRAY"){
             foreach my $locker (@{$current_locks}) {
                 my $compare=$locker;
-                if (!ref($locker) eq 'ARRAY') {
-                    push(@new_locks,$locker);
-                } else {   
+                if (ref($locker) eq 'ARRAY') {
                     $compare=join('',@{$locker});
                     if ($compare ne $symb_crs) {
                         push(@new_locks, $locker);
@@ -7844,24 +7949,82 @@
   file: (optional) the file you want access info on
 
 Returns:
-    a hash containing
-        keys of 'control type' (possiblities?)
-        values are XML contianing settings 
+    a hash (keys are file names) of hashes containing
+        keys are: path to file/file_name\0uniqueID:scope_end_start (see below)
+        values are XML containing access control settings (see below) 
 
 Internal notes:
 
- access controls are stored in file_permissions.db as  array of arrays and a hash.
-    array refs -> are locks
-    hash refs -> all other types of controls
-                 and will contain keys
-
-                'access' -> hash where keys are access controls and
-                            values are settings (in XML)
-
-                'accesscount' -> scalar - equal to the next number to
-                                 use as the first part of an access
-                                 control key when defining a new
-                                 control.
+ access controls are stored in file_permissions.db as key=value pairs.
+    key -> path to file/file_name\0uniqueID:scope_end_start
+        where scope -> public,guest,course,group,domains or users.
+              end -> UNIX time for end of access (0 -> no end date)
+              start -> UNIX time for start of access
+
+    value -> XML description of access control
+           <scope type=""> (type =1 of: public,guest,course,group,domains,users">
+            <start></start>
+            <end></end>
+
+            <password></password>  for scope type = guest
+
+            <domain></domain>     for scope type = course or group
+            <number></number>
+            <roles id="">
+             <role></role>
+             <access></access>
+             <section></section>
+             <group></group>
+            </roles>
+
+            <dom></dom>         for scope type = domains
+
+            <users>             for scope type = users
+             <user>
+              <uname></uname>
+              <udom></udom>
+             </user>
+            </users>
+           </scope> 
+              
+ Access data is also aggregated for each file in an additional key=value pair:
+ key -> path to file/file_name\0accesscontrol 
+ value -> reference to hash
+          hash contains key = value pairs
+          where key = uniqueID:scope_end_start
+                value = UNIX time record was last updated
+
+          Used to improve speed of look-ups of access controls for each file.  
+ 
+ Locks on files (resulting from submission of portfolio file to a homework problem stored in array of arrays.
+
+parse_access_controls():
+
+Parses XML of an access control record
+Args
+1. Text string (XML) of access comtrol record
+
+Returns:
+1. Hash of access control settings. 
+
+modify_access_controls():
+
+Modifies access controls for a portfolio file
+Args
+1. file name
+2. reference to hash of required changes,
+3. domain
+4. username
+  where domain,username are the domain of the portfolio owner 
+  (either a user or a course) 
+
+Returns:
+1. result of additions or updates ('ok' or 'error', with error message). 
+2. result of deletions ('ok' or 'error', with error message).
+3. reference to hash of any new or updated access controls.
+4. reference to hash used to map incoming IDs to uniqueIDs assigned to control.
+   key = integer (inbound ID)
+   value = uniqueID  
 
 =back
 

--raeburn1150497455--