Diff for /loncom/auth/lonroles.pm between versions 1.256.2.6.2.3 and 1.256.2.7

version 1.256.2.6.2.3, 2012/02/09 01:11:52 version 1.256.2.7, 2014/03/25 10:18:04
Line 57  course they should act on, etc. Both in Line 57  course they should act on, etc. Both in
 handler determines via C<lonnet>'s C<&allowed> function that a certain  handler determines via C<lonnet>'s C<&allowed> function that a certain
 action is not allowed, C<lonroles> is used as error handler. This  action is not allowed, C<lonroles> is used as error handler. This
 allows the user to select another role which may have permission to do  allows the user to select another role which may have permission to do
 what they were trying to do.  what they were trying to do. C<lonroles> can also be accessed via the
   B<CRS> button in the Remote Control. 
   
 =begin latex  =begin latex
   
Line 217  sub handler { Line 218  sub handler {
     my $now=time;      my $now=time;
     my $then=$env{'user.login.time'};      my $then=$env{'user.login.time'};
     my $refresh=$env{'user.refresh.time'};      my $refresh=$env{'user.refresh.time'};
     my $update=$env{'user.update.time'};  
     if (!$refresh) {      if (!$refresh) {
         $refresh = $then;          $refresh = $then;
     }      }
     if (!$update) {  
         $update = $then;  
     }  
   
 # -------------------------------------------------------- Check for new roles  
     my $updateresult;  
     if ($env{'form.doupdate'}) {  
         my $show_course=&Apache::loncommon::show_course();  
         my $checkingtxt;  
         if ($show_course) {  
             $checkingtxt = &mt('Checking for new courses ...');  
         } else {  
             $checkingtxt = &mt('Checking for new roles ...');  
         }  
         $updateresult = '<span class="LC_info">'.$checkingtxt.'</span>';  
         $updateresult .= &update_session_roles();  
         &Apache::lonnet::appenv({'user.update.time'  => $now});  
         $update = $now;  
     }  
   
     my $envkey;      my $envkey;
     my %dcroles = ();      my %dcroles = ();
     my $numdc = &check_fordc(\%dcroles,$update,$then);      my $numdc = &check_fordc(\%dcroles,$then);
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'});
     my $loncaparev = $Apache::lonnet::perlvar{'lonVersion'};      my $loncaparev = $Apache::lonnet::perlvar{'lonVersion'};
   
Line 263  sub handler { Line 243  sub handler {
                 if (defined($env{'user.role.'.$env{'form.switchrole'}})) {                  if (defined($env{'user.role.'.$env{'form.switchrole'}})) {
                     my ($start,$end) = split(/\./,$env{'user.role.'.$env{'form.switchrole'}});                      my ($start,$end) = split(/\./,$env{'user.role.'.$env{'form.switchrole'}});
                     if (!$end || $end > $now) {                      if (!$end || $end > $now) {
                         if (!$start || $start < $update) {                          if (!$start || $start < $refresh) {
                             $switch_is_active = 1;                              $switch_is_active = 1;
                         }                          }
                     }                      }
                 }                  }
                 unless ($switch_is_active) {                  unless ($switch_is_active) {
                     &adhoc_course_role($refresh,$update,$then);                      &adhoc_course_role($refresh,$then);
                 }                  }
             }              }
     my %temp=('logout_'.$env{'request.course.id'} => time);      my %temp=('logout_'.$env{'request.course.id'} => time);
Line 291  sub handler { Line 271  sub handler {
     ($envkey =~ m-^form\.(cc|co)\./($match_domain)/($match_courseid)$-)) {      ($envkey =~ m-^form\.(cc|co)\./($match_domain)/($match_courseid)$-)) {
                     if ($dcroles{$domain}) {                      if ($dcroles{$domain}) {
                         &Apache::lonnet::check_adhoc_privs($domain,$coursenum,                          &Apache::lonnet::check_adhoc_privs($domain,$coursenum,
                                                            $update,$refresh,$now,$ccrole);                                                             $then,$refresh,$now,$ccrole);
                     }                      }
                     last;                      last;
                 }                  }
Line 331  sub handler { Line 311  sub handler {
                     if ($dcroles{$domain}) {                      if ($dcroles{$domain}) {
                         my ($server_status,$home) = &check_author_homeserver($user,$domain);                          my ($server_status,$home) = &check_author_homeserver($user,$domain);
                         if (($server_status eq 'ok') || ($server_status eq 'switchserver')) {                          if (($server_status eq 'ok') || ($server_status eq 'switchserver')) {
                             &Apache::lonnet::check_adhoc_privs($domain,$user,$update,                              &Apache::lonnet::check_adhoc_privs($domain,$user,$then,
                                                                $refresh,$now,'ca');                                                                 $refresh,$now,'ca');
                             if ($server_status eq 'switchserver') {                              if ($server_status eq 'switchserver') {
                                 my $trolecode = 'ca./'.$domain.'/'.$user;                                   my $trolecode = 'ca./'.$domain.'/'.$user; 
Line 353  sub handler { Line 333  sub handler {
         foreach $envkey (keys %env) {          foreach $envkey (keys %env) {
             next if ($envkey!~/^user\.role\./);              next if ($envkey!~/^user\.role\./);
             my ($where,$trolecode,$role,$tstatus,$tend,$tstart);              my ($where,$trolecode,$role,$tstatus,$tend,$tstart);
             &Apache::lonnet::role_status($envkey,$update,$refresh,$now,\$role,\$where,              &Apache::lonnet::role_status($envkey,$then,$refresh,$now,\$role,\$where,
                                          \$trolecode,\$tstatus,\$tstart,\$tend);                                           \$trolecode,\$tstatus,\$tstart,\$tend);
             if ($env{'form.'.$trolecode}) {              if ($env{'form.'.$trolecode}) {
  if ($tstatus eq 'is') {   if ($tstatus eq 'is') {
Line 514  ENDENTERKEY Line 494  ENDENTERKEY
                         my $msg;                          my $msg;
  my ($furl,$ferr)=   my ($furl,$ferr)=
     &Apache::lonuserstate::readmap($cdom.'/'.$cnum);      &Apache::lonuserstate::readmap($cdom.'/'.$cnum);
                         unless (($ferr) || ($env{'form.switchrole'})) {  
                             &Apache::lonnet::put('nohist_crslastlogin',  
                                 {$env{'user.name'}.':'.$env{'user.domain'}.  
                                 ':'.$csec.':'.$role => $now},$cdom,$cnum);  
                         }  
  if (($env{'form.orgurl'}) &&    if (($env{'form.orgurl'}) && 
     ($env{'form.orgurl'}!~/^\/adm\/flip/)) {      ($env{'form.orgurl'}!~/^\/adm\/flip/)) {
     my $dest=$env{'form.orgurl'};      my $dest=$env{'form.orgurl'};
Line 700  function enterrole (thisform,rolecode,bu Line 675  function enterrole (thisform,rolecode,bu
        alert('$standby');         alert('$standby');
     }         }   
 }  }
   
 function setToUpdate(thisform) {  
     thisform.doupdate.value='1';  
     thisform.selectrole.value='';  
     thisform.submit();  
 }  
   
 // ]]>  // ]]>
 </script>  </script>
 ENDHEADER  ENDHEADER
Line 769  ENDHEADER Line 737  ENDHEADER
     }      }
 # -------------------------------------------------------- Choice or no choice?  # -------------------------------------------------------- Choice or no choice?
     if ($nochoose) {      if ($nochoose) {
         $r->print("<h2>".&mt('Sorry ...')."</h2>\n<span class='LC_error'>".   $r->print("<h2>".&mt('Sorry ...')."</h2>\n<span class='LC_error'>".
                   &mt('This action is currently not authorized.').'</span>'.    &mt('This action is currently not authorized.').'</span>'.
                   &Apache::loncommon::end_page());    &Apache::loncommon::end_page());
         return OK;   return OK;
     } else {      } else {
         $r->print($updateresult);  
         if (($ENV{'REDIRECT_QUERY_STRING'}) && ($fn)) {          if (($ENV{'REDIRECT_QUERY_STRING'}) && ($fn)) {
             $fn.='?'.$ENV{'REDIRECT_QUERY_STRING'};         $fn.='?'.$ENV{'REDIRECT_QUERY_STRING'};
         }          }
         $r->print('<form method="post" name="rolechoice" action="'.(($fn)?$fn:$r->uri).'">');          $r->print('<form method="post" name="rolechoice" action="'.(($fn)?$fn:$r->uri).'">');
         $r->print('<input type="hidden" name="orgurl" value="'.$fn.'" />');          $r->print('<input type="hidden" name="orgurl" value="'.$fn.'" />');
Line 786  ENDHEADER Line 753  ENDHEADER
     $r->rflush();      $r->rflush();
   
     my (%roletext,%sortrole,%roleclass,%futureroles,%timezones);      my (%roletext,%sortrole,%roleclass,%futureroles,%timezones);
     my ($countactive,$countfuture,$inrole,$possiblerole) =      my ($countactive,$countfuture,$inrole,$possiblerole) = 
         &gather_roles($update,$refresh,$now,$reinit,$nochoose,\%roletext,\%sortrole,\%roleclass,          &gather_roles($then,$refresh,$now,$reinit,$nochoose,\%roletext,\%sortrole,\%roleclass,
                       \%futureroles,\%timezones,$loncaparev);                        \%futureroles,\%timezones,$loncaparev);
   
     $refresh = $now;      $refresh = $now;
     &Apache::lonnet::appenv({'user.refresh.time'  => $refresh});      &Apache::lonnet::appenv({'user.refresh.time'  => $refresh});
     my $updatebutton = &mt('Check for role changes');  
     my $show_course=&Apache::loncommon::show_course();  
     if ($show_course) {  
         $updatebutton = &mt('Check for new courses');  
     }  
     my $do_update;  
     unless (($env{'form.source'} eq 'login') || ($env{'form.doupdate'})) {  
         $do_update = '<input type="hidden" name="doupdate" value="" />'.  
                      '<input type="button" name="update" value="'.  
                      $updatebutton.'" onclick="javascript:setToUpdate(this.form)" />';  
     }  
     if ($env{'user.adv'}) {      if ($env{'user.adv'}) {
         my $showall = '<label><input type="checkbox" name="showall"';          $r->print('<p><label><input type="checkbox" name="showall"');
         if ($env{'form.showall'}) {          if ($env{'form.showall'}) { $r->print(' checked="checked" '); }
             $showall .= ' checked="checked" ';          $r->print(' />'.&mt('Show all roles').'</label>'
         }                   .' <input type="submit" value="'.&mt('Update display').'" />'
         $showall .= ' />'.&mt('Show all roles').'</label>&nbsp;'.                   .'</p>');
                     '<input type="submit" value="'.&mt('Update display').'" />';  
         if ($do_update) {  
             $r->print('<div class="LC_left_float"><fieldset>'.  
                       '<legend>'. &mt('Display').'</legend>'.  
                       $showall.'</fieldset></div>'.  
                       '<div class="LC_left_float"><fieldset><legend>'.  
                       &mt('Changes?').'</legend>'.  
                       $do_update.'</fieldset></div><br clear="all" />');  
         } else {  
             $r->print($showall);  
         }  
     } else {      } else {
         $r->print('<p>'.$do_update.'</p>');  
         if ($countactive > 0) {          if ($countactive > 0) {
             $r->print(&Apache::loncoursequeueadmin::queued_selfenrollment());              $r->print(&Apache::loncoursequeueadmin::queued_selfenrollment());
             my $domdesc = &Apache::lonnet::domain($env{'user.domain'},'description');              my $domdesc = &Apache::lonnet::domain($env{'user.domain'},'description');
             my $esc_dom = &HTML::Entities::encode($env{'user.domain'},'"<>&');              my $esc_dom = &HTML::Entities::encode($env{'user.domain'},'"<>&'); 
             $r->print(              $r->print(
                 '<p>'                  '<p>'
                .&mt('[_1]Visit the [_2]Course/Community Catalog[_3]'                 .&mt('[_1]Visit the [_2]Course/Community Catalog[_3]'
Line 878  ENDHEADER Line 823  ENDHEADER
         }          }
         $r->print(&Apache::loncommon::end_page());          $r->print(&Apache::loncommon::end_page());
  return OK;   return OK;
     } elsif ($countactive==1 && $countfuture==0) { # Only one choice  
         my $needs_switchserver;  
         if ($env{'user.author'}) {  
             $needs_switchserver = &check_needs_switchserver($possiblerole);  
         }  
         if ((!$needs_switchserver) && ($env{'request.role'} eq 'cm')) {  
             $r->print('<h3>'.&mt('Please stand by.').'</h3>'.  
                 '<input type="hidden" name="'.$possiblerole.'" value="1" />'.  
             '<noscript><br /><input type="submit" name="submit" value="'.&mt('Continue').'" /></noscript>');  
             $r->print("</form>\n");  
             $r->rflush();  
             $r->print('<script type="text/javascript">document.forms.rolechoice.submit();</script>');  
             $r->print(&Apache::loncommon::end_page());  
             return OK;  
         }  
         if ($needs_switchserver) {  
             $r->print("<h2>".&mt('Server Switch Required')."</h2>\n".  
                       &mt('Construction Space access is only available from '.  
                           'the home server of the corresponding Author.').'<br />'.  
                       &mt("Click the 'Switch Server' link to go there.").'<br />');  
         }  
     }      }
 # ----------------------------------------------------------------------- Table  # ----------------------------------------------------------------------- Table
   
Line 1017  ENDHEADER Line 941  ENDHEADER
 }  }
   
 sub gather_roles {  sub gather_roles {
     my ($update,$refresh,$now,$reinit,$nochoose,$roletext,$sortrole,$roleclass,$futureroles,$timezones,$loncaparev) = @_;      my ($then,$refresh,$now,$reinit,$nochoose,$roletext,$sortrole,$roleclass,$futureroles,$timezones,$loncaparev) = @_;
     my ($countactive,$countfuture,$inrole,$possiblerole) = (0,0,0,'');      my ($countactive,$countfuture,$inrole,$possiblerole) = (0,0,0,'');
     my $advanced = $env{'user.adv'};      my $advanced = $env{'user.adv'};
     my $tryagain = $env{'form.tryagain'};      my $tryagain = $env{'form.tryagain'};
Line 1029  sub gather_roles { Line 953  sub gather_roles {
         my ($role_text,$role_text_end,$sortkey);          my ($role_text,$role_text_end,$sortkey);
         if ($envkey=~/^user\.role\./) {          if ($envkey=~/^user\.role\./) {
             my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend);              my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend);
             &Apache::lonnet::role_status($envkey,$update,$refresh,$now,\$role,\$where,              &Apache::lonnet::role_status($envkey,$then,$refresh,$now,\$role,\$where,
                                          \$trolecode,\$tstatus,\$tstart,\$tend);                                           \$trolecode,\$tstatus,\$tstart,\$tend);
             next if (!defined($role) || $role eq '' || $role =~ /^gr/);              next if (!defined($role) || $role eq '' || $role =~ /^gr/);
             $tremark='';              $tremark='';
Line 1573  sub check_author_homeserver { Line 1497  sub check_author_homeserver {
 }  }
   
 sub check_fordc {  sub check_fordc {
     my ($dcroles,$update,$then) = @_;      my ($dcroles,$then) = @_;
     my $numdc = 0;      my $numdc = 0;
     if ($env{'user.adv'}) {      if ($env{'user.adv'}) {
         foreach my $envkey (sort keys %env) {          foreach my $envkey (sort keys %env) {
Line 1581  sub check_fordc { Line 1505  sub check_fordc {
                 my $dcdom = $1;                  my $dcdom = $1;
                 my $livedc = 1;                  my $livedc = 1;
                 my ($tstart,$tend)=split(/\./,$env{$envkey});                  my ($tstart,$tend)=split(/\./,$env{$envkey});
                 my $limit = $update;                  if ($tstart && $tstart>$then) { $livedc = 0; }
                 if ($env{'request.role'} eq 'dc./'.$dcdom.'/') {                  if ($tend   && $tend  <$then) { $livedc = 0; }
                     $limit = $then;  
                 }  
                 if ($tstart && $tstart>$limit) { $livedc = 0; }  
                 if ($tend   && $tend  <$limit) { $livedc = 0; }  
                 if ($livedc) {                  if ($livedc) {
                     $$dcroles{$dcdom} = $envkey;                      $$dcroles{$dcdom} = $envkey;
                     $numdc++;                      $numdc++;
Line 1598  sub check_fordc { Line 1518  sub check_fordc {
 }  }
   
 sub adhoc_course_role {  sub adhoc_course_role {
     my ($refresh,$update,$then) = @_;      my ($refresh,$then) = @_;
     my ($cdom,$cnum,$crstype);      my ($cdom,$cnum,$crstype);
     $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};      $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
     $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};      $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
     $crstype = &Apache::loncommon::course_type();      $crstype = &Apache::loncommon::course_type();
     if (&check_forcc($cdom,$cnum,$refresh,$update,$then,$crstype)) {      if (&check_forcc($cdom,$cnum,$refresh,$then,$crstype)) {
         my $setprivs;          my $setprivs;
         if (!defined($env{'user.role.'.$env{'form.switchrole'}})) {          if (!defined($env{'user.role.'.$env{'form.switchrole'}})) {
             $setprivs = 1;              $setprivs = 1;
         } else {          } else {
             my ($start,$end) = split(/\./,$env{'user.role.'.$env{'form.switchrole'}});              my ($start,$end) = split(/\./,$env{'user.role.'.$env{'form.switchrole'}});
             if (($start && ($start>$refresh || $start == -1)) ||              if (($start && ($start>$refresh || $start == -1)) ||
                 ($end && $end<$update)) {                  ($end && $end<$then)) {
                 $setprivs = 1;                  $setprivs = 1;
             }              }
         }          }
Line 1653  sub adhoc_course_role { Line 1573  sub adhoc_course_role {
 }  }
   
 sub check_forcc {  sub check_forcc {
     my ($cdom,$cnum,$refresh,$update,$then,$crstype) = @_;      my ($cdom,$cnum,$refresh,$then,$crstype) = @_;
     my ($is_cc,$ccrole);      my ($is_cc,$ccrole);
     if ($crstype eq 'Community') {      if ($crstype eq 'Community') {
         $ccrole = 'co';          $ccrole = 'co';
Line 1666  sub check_forcc { Line 1586  sub check_forcc {
             if (defined($env{$envkey})) {              if (defined($env{$envkey})) {
                 $is_cc = 1;                  $is_cc = 1;
                 my ($tstart,$tend)=split(/\./,$env{$envkey});                  my ($tstart,$tend)=split(/\./,$env{$envkey});
                 my $limit = $update;  
                 if ($env{'request.role'} eq $ccrole.'./'.$cdom.'/'.$cnum) {  
                     $limit = $then;  
                 }  
                 if ($tstart && $tstart>$refresh) { $is_cc = 0; }                  if ($tstart && $tstart>$refresh) { $is_cc = 0; }
                 if ($tend   && $tend  <$limit) { $is_cc = 0; }                  if ($tend   && $tend  <$then) { $is_cc = 0; }
             }              }
         }          }
     }      }
Line 1688  sub check_release_required { Line 1604  sub check_release_required {
             my $otherserver;              my $otherserver;
             if (($major eq '' && $minor eq '') ||               if (($major eq '' && $minor eq '') || 
                 (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {                  (($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor)))) {
                 my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'});                  my ($userdomserver) = &Apache::lonnet::choose_server($env{'user.domain'},undef,$required);
                 my $switchlcrev =                   my $switchlcrev = 
                     &Apache::lonnet::get_server_loncaparev($env{'user.domain'},                      &Apache::lonnet::get_server_loncaparev($env{'user.domain'},
                                                            $userdomserver);                                                             $userdomserver);
Line 1902  sub courseloadpage { Line 1818  sub courseloadpage {
     return $startpage;      return $startpage;
 }  }
   
 sub update_session_roles {  
     my $then=$env{'user.login.time'};  
     my $refresh=$env{'user.refresh.time'};  
     if (!$refresh) {  
         $refresh = $then;  
     }  
     my $update = $env{'user.update.time'};  
     if (!$update) {  
         $update = $then;  
     }  
     my $now = time;  
     my %roleshash =  
         &Apache::lonnet::get_my_roles('','','userroles',  
                                       ['active','future','previous'],  
                                       undef,undef,1);  
     my ($msg,@newsec,$oldsec,$currrole_expired,@changed_roles,  
         %changed_groups,%dbroles,%deletedroles,%allroles,%allgroups,  
         %userroles,%checkedgroup,%crprivs,$hasgroups,%rolechange,  
         %groupchange,%newrole,%newgroup,%customprivchg,%groups_roles,  
         @rolecodes);  
     my @possroles = ('cr','st','ta','ad','ep','in','co','cc');  
     my %courseroles;  
     foreach my $item (keys(%roleshash)) {  
         my ($uname,$udom,$role,$remainder) = split(/:/,$item,4);  
         my ($tstart,$tend) = split(/:/,$roleshash{$item});  
         my ($section,$group,@group_privs);  
         if ($role =~ m{^gr/(\w*)$}) {  
             $role = 'gr';  
             my $priv = $1;  
             next if ($tstart eq '-1');  
             if (&curr_role_status($tstart,$tend,$refresh,$now) eq 'active') {  
                 if ($priv ne '') {  
                     push(@group_privs,$priv);  
                 }  
             }  
             if ($remainder =~ /:/) {  
                 (my $additional_privs,$group) =  
                     ($remainder =~ /^([\w:]+):([^:]+)$/);  
                 if ($additional_privs ne '') {  
                     if (&curr_role_status($tstart,$tend,$refresh,$now) eq 'active') {  
                         push(@group_privs,split(/:/,$additional_privs));  
                         @group_privs = sort(@group_privs);  
                     }  
                 }  
             } else {  
                 $group = $remainder;  
             }  
         } else {  
             $section = $remainder;  
         }  
         my $where = "/$udom/$uname";  
         if ($section ne '') {  
             $where .= "/$section";  
         } elsif ($group ne '') {  
             $where .= "/$group";  
         }  
         my $rolekey = "$role.$where";  
         my $envkey = "user.role.$rolekey";  
         $dbroles{$envkey} = 1;  
         if (($env{'request.role'} eq $rolekey) && ($role ne 'st')) {  
             if (&curr_role_status($tstart,$tend,$refresh,$now) ne 'active') {  
                 $currrole_expired = 1;  
             }  
         }  
         if ($env{$envkey} eq '') {  
             my $status_in_db =  
                 &curr_role_status($tstart,$tend,$refresh,$now);  
                 &gather_roleprivs(\%allroles,\%allgroups,\%userroles,$where,$role,$tstart,$tend,$status_in_db);  
             if (($role eq 'st') && ($env{'request.role'} =~ m{^\Q$role\E\.\Q/$udom/$uname\E})) {  
                 if ($status_in_db eq 'active') {  
                     if ($section eq '') {  
                         push(@newsec,'none');  
                     } else {  
                         push(@newsec,$section);  
                     }  
                 }  
             } else {  
                 unless (grep(/^\Q$role\E$/,@changed_roles)) {  
                     push(@changed_roles,$role);  
                 }  
                 if ($status_in_db ne 'previous') {  
                     if ($role eq 'gr') {  
                         $newgroup{$rolekey} = $status_in_db;  
                         if ($status_in_db eq 'active') {  
                             unless (ref($courseroles{$udom}) eq 'HASH') {  
                                 %{$courseroles{$udom}} =  
                                     &Apache::lonnet::get_my_roles('','','userroles',  
                                                                   ['active'],\@possroles,  
                                                                   [$udom],1);  
                             }  
                             &Apache::lonnet::get_groups_roles($udom,$uname,  
                                                               $courseroles{$udom},  
                                                               \@rolecodes,\%groups_roles);  
                         }  
                     } else {  
                         $newrole{$rolekey} = $status_in_db;  
                     }  
                 }  
             }  
         } else {  
             my ($currstart,$currend) = split(/\./,$env{$envkey});  
             if ($role eq 'gr') {  
                 if (&curr_role_status($currstart,$currend,$refresh,$update) ne 'previous') {  
                     $hasgroups = 1;  
                 }  
             }  
             if (($currstart ne $tstart) || ($currend ne $tend)) {  
                 my $status_in_env =  
                     &curr_role_status($currstart,$currend,$refresh,$update);  
                 my $status_in_db =  
                     &curr_role_status($tstart,$tend,$refresh,$now);  
                 if ($status_in_env ne $status_in_db) {  
                     if ($status_in_env eq 'active') {  
                         if ($role eq 'st') {  
                             if ($env{'request.role'} eq $rolekey) {  
                                 my $switchsection;  
                                 unless (ref($courseroles{$udom}) eq 'HASH') {  
                                     %{$courseroles{$udom}} =  
                                         &Apache::lonnet::get_my_roles('','','userroles',  
                                                                       ['active'],  
                                                                       \@possroles,[$udom],1);  
                                 }  
                                 foreach my $crsrole (keys(%{$courseroles{$udom}})) {  
                                     if ($crsrole =~ /^\Q$uname\E:\Q$udom\E:st/) {  
                                         $switchsection = 1;  
                                         last;  
                                     }  
                                 }  
                                 if ($switchsection) {  
                                     if ($section eq '') {  
                                         $oldsec = 'none';  
                                     } else {  
                                         $oldsec = $section;  
                                     }  
                                     &gather_roleprivs(\%allroles,\%allgroups,\%userroles,$where,$role,$tstart,$tend,$status_in_db);  
                                 } else {  
                                     $currrole_expired = 1;  
                                     next;  
                                 }  
                             }  
                         }  
                         unless ($rolekey eq $env{'request.role'}) {  
                             if ($role eq 'gr') {  
                                 &Apache::lonnet::delete_env_groupprivs($where,\%courseroles,\@possroles);  
                             } else {  
                                 &Apache::lonnet::delenv("user.priv.$rolekey",undef,[$role]);  
                                 &Apache::lonnet::delenv("user.priv.cm.$where",undef,['cm']);  
                             }  
                             &gather_roleprivs(\%allroles,\%allgroups,\%userroles,$where,$role,$tstart,$tend,$status_in_db);  
                         }  
                     } elsif ($status_in_db eq 'active') {  
                         if (($role eq 'st') &&  
                             ($env{'request.role'} =~ m{^\Q$role\E\.\Q/$udom/$uname\E})) {  
                             if ($section eq '') {  
                                 push(@newsec,'none');  
                             } else {  
                                 push(@newsec,$section);  
                             }  
                         } elsif ($role eq 'gr') {  
                             unless (ref($courseroles{$udom}) eq 'HASH') {  
                                 %{$courseroles{$udom}} =  
                                     &Apache::lonnet::get_my_roles('','','userroles',  
                                                                   ['active'],  
                                                                   \@possroles,[$udom],1);  
                             }  
                             &Apache::lonnet::get_groups_roles($udom,$uname,  
                                                               $courseroles{$udom},  
                                                               \@rolecodes,\%groups_roles);  
                         }  
                         &gather_roleprivs(\%allroles,\%allgroups,\%userroles,$where,$role,$tstart,$tend,$status_in_db);  
                     }  
                     unless (grep(/^\Q$role\E$/,@changed_roles)) {  
                         push(@changed_roles,$role);  
                     }  
                     if ($role eq 'gr') {  
                         $groupchange{"/$udom/$uname"}{$group} = $status_in_db;  
                     } else {  
                         $rolechange{$rolekey} = $status_in_db;  
                     }  
                 }  
             } else {  
                 if ($role eq 'gr') {  
                     unless ($checkedgroup{$where}) {  
                         my $status_in_db =  
                             &curr_role_status($tstart,$tend,$refresh,$now);  
                         if ($tstart eq '-1') {  
                             $status_in_db = 'deleted';  
                         }  
                         unless (ref($courseroles{$udom}) eq 'HASH') {  
                             %{$courseroles{$udom}} =  
                                 &Apache::lonnet::get_my_roles('','','userroles',  
                                                               ['active'],  
                                                               \@possroles,[$udom],1);  
                         }  
                         if (ref($courseroles{$udom}) eq 'HASH') {  
                             foreach my $item (keys(%{$courseroles{$udom}})) {  
                                 next unless ($item =~ /^\Q$uname\E/);  
                                 my ($cnum,$cdom,$crsrole,$crssec) = split(/:/,$item);  
                                 my $area = '/'.$cdom.'/'.$cnum;  
                                 if ($crssec ne '') {  
                                     $area .= '/'.$crssec;  
                                 }  
                                 my $crsrolekey = $crsrole.'.'.$area;  
                                 my $currprivs = $env{'user.priv.'.$crsrole.'.'.$area.'.'.$where};  
                                 $currprivs =~ s/^://;  
                                 $currprivs =~ s/\&F$//;  
                                 my @curr_grp_privs = split(/\&F:/,$currprivs);  
                                 @curr_grp_privs = sort(@curr_grp_privs);  
                                 my @diffs;  
                                 if (@group_privs > 0 || @curr_grp_privs > 0) {  
                                     @diffs = &Apache::loncommon::compare_arrays(\@group_privs,\@curr_grp_privs);  
                                 }  
                                 if (@diffs == 0) {  
                                     last;  
                                 } else {  
                                     unless(grep(/^\Qgr\E$/,@rolecodes)) {  
                                         push(@rolecodes,'gr');  
                                     }  
                                     &gather_roleprivs(\%allroles,\%allgroups,  
                                                       \%userroles,$where,$role,  
                                                       $tstart,$tend,$status_in_db);  
                                     if ($status_in_db eq 'active') {  
                                         &Apache::lonnet::get_groups_roles($udom,$uname,  
                                                                           $courseroles{$udom},  
                                                                           \@rolecodes,\%groups_roles);  
                                     }  
                                     $changed_groups{$udom.'_'.$uname}{$group} = $status_in_db;  
                                     last;  
                                 }  
                             }  
                         }  
                         $checkedgroup{$where} = 1;  
                     }  
                 } elsif ($role =~ /^cr/) {  
                     my $status_in_db =  
                         &curr_role_status($tstart,$tend,$refresh,$now);  
                     my ($rdummy,$rest) = split(/\//,$role,2);  
                     my ($rdummy,$rdomain,$rauthor,$rrole)=split(/\//,$role);  
                     my %currpriv;  
                     unless (exists($crprivs{$rest})) {  
                         my ($rdomain,$rauthor,$rrole)=split(/\//,$rest);  
                         my $homsvr=&Apache::lonnet::homeserver($rauthor,$rdomain);  
                         if (&Apache::lonnet::hostname($homsvr) ne '') {  
                             my ($rdummy,$roledef)=  
                             &Apache::lonnet::get('roles',["rolesdef_$rrole"],  
                                                  $rdomain,$rauthor);  
                             if (($rdummy ne 'con_lost') && ($roledef ne '')) {  
                                 my $i = 0;  
                                 my @scopes = ('sys','dom','crs');  
                                 my @privs = split(/\_/,$roledef);  
                                 foreach my $priv (@privs) {  
                                     my ($blank,@prv) = split(/:/,$priv);  
                                     @prv = map { $_ .= (/\&\w+$/ ? '':'&F') } @prv;  
                                     if (@prv) {  
                                         $priv = ':'.join(':',sort(@prv));  
                                     }  
                                     $crprivs{$rest}{$scopes[$i]} = $priv;  
                                     $i++;  
                                 }  
                             }  
                         }  
                     }  
                     $currpriv{sys} = $env{"user.priv.$rolekey./"};  
                     $currpriv{dom} = $env{"user.priv.$rolekey./$udom/"};  
                     $currpriv{crs} = $env{"user.priv.$rolekey.$where"};  
                     if (keys(%crprivs)) {  
                         if (($crprivs{$rest}{sys} ne $currpriv{sys}) ||  
                             ($crprivs{$rest}{dom} ne $currpriv{dom})  
  ||  
                             ($crprivs{$rest}{crs} ne $currpriv{crs})) {  
                             &gather_roleprivs(\%allroles,\%allgroups,\%userroles,$where,$role,$tstart,$tend,$status_in_db);  
                             unless (grep(/^\Q$role\E$/,@changed_roles)) {  
                                 push(@changed_roles,$role);  
                             }  
                             my $status_in_env =  
                                 &curr_role_status($currstart,$currend,$refresh,$update);  
                             if ($status_in_env eq 'active') {  
                                 $customprivchg{$rolekey} = $status_in_env;  
                             }  
                         }  
                     }  
                 }  
             }  
         }  
     }  
     foreach my $envkey (keys(%env)) {  
         next unless ($envkey =~ /^user\.role\./);  
         next if ($dbroles{$envkey});  
         next if ($envkey eq 'user.role.'.$env{'request.role'});  
         my ($currstart,$currend) = split(/\./,$env{$envkey});  
         my $status_in_env =  
             &curr_role_status($currstart,$currend,$refresh,$update);  
         my ($rolekey) = ($envkey =~ /^user\.role\.(.+)$/);  
         my ($role,$rest)=split(/\./,$rolekey,2);  
         if (&Apache::lonnet::delenv($envkey,undef,[$role])) {  
             if ($status_in_env eq 'active') {  
                 if ($role eq 'gr') {  
                     &Apache::lonnet::delete_env_groupprivs($rest,\%courseroles,  
                                                            \@possroles);  
                 } else {  
                     &Apache::lonnet::delenv("user.priv.$rolekey",undef,[$role]);  
                     &Apache::lonnet::delenv("user.priv.cm.$rest",undef,['cm']);  
                 }  
                 unless (grep(/^\Q$role\E$/,@changed_roles)) {  
                     push(@changed_roles,$role);  
                 }  
                 $deletedroles{$rolekey} = 1;  
             }  
         }  
     }  
     if (($oldsec) && (@newsec > 0)) {  
         if (@newsec > 1) {  
             $msg = '<div class="LC_warning">'.&mt('The section has changed for your current role. Log-out and log-in again to select a role for the new section.').'</div>';  
         } else {  
             my $newrole = $env{'request.role'};  
             if ($newsec[0] eq 'none') {  
                 $newrole =~ s{(/[^/])$}{};  
             } elsif ($oldsec eq 'none') {  
                 $newrole .= '/'.$newsec[0];  
             } else {  
                 $newrole =~ s{([^/]+)$}{$newsec[0]};  
             }  
             my $coursedesc = $env{'course.'.$env{'request.course.id'}.'.description'};  
             my ($curr_role) = ($env{'request.role'} =~ m{^(\w+)\./$match_domain/$match_courseid});  
             my %temp=('logout_'.$env{'request.course.id'} => time);  
             &Apache::lonnet::put('email_status',\%temp);  
             &Apache::lonnet::delenv('user.state.'.$env{'request.course.id'});  
             &Apache::lonnet::appenv({"request.course.id"   => '',  
                                      "request.course.fn"   => '',  
                                      "request.course.uri"  => '',  
                                      "request.course.sec"  => '',  
                                      "request.role"        => 'cm',  
                                      "request.role.adv"    => $env{'user.adv'},  
                                      "request.role.domain" => $env{'user.domain'}});  
             my $rolename = &Apache::loncommon::plainname($curr_role);  
             $msg = '<p><form name="reselectrole" action="/adm/roles" method="post" />'.  
                    '<input type="hidden" name="newrole" value="" />'.  
                    '<input type="hidden" name="selectrole" value="1" />'.  
                    '<span class="LC_info">'.  
                    &mt('Your section has changed for your current [_1] role in [_2].',$rolename,$coursedesc).'</span><br />';  
             my $button = '<input type="button" name="sectionchanged" value="'.  
                          &mt('Re-Select').'" onclick="javascript:enterrole(this.form,'."'$newrole','sectionchanged'".')" />';  
             if ($newsec[0] eq 'none') {  
                 $msg .= &mt('[_1] to continue with your new section-less role.',$button);  
             } else {  
                 $msg .= &mt('[_1] to continue with your new role in section ([_2]).',$button,$newsec[0]);  
             }  
             $msg .= '</form></p>';  
         }  
     } elsif ($currrole_expired) {  
         $msg .= '<div class="LC_warning">';  
         if (&Apache::loncommon::show_course()) {  
             $msg .= &mt('Your role in the current course has expired.');  
         } else {  
             $msg .= &mt('Your current role has expired.');  
         }  
         $msg .= '<br />'.&mt('However you can continue to use this role until you logout, click the "Re-Select" button, or your session has been idle for more than 24 hours.').'</div>';  
     }  
     if (!@changed_roles || !(keys(%changed_groups))) {  
         my ($rolesmsg,$groupsmsg);  
         if (!@changed_roles) {  
             if (&Apache::loncommon::show_course()) {  
                 $rolesmsg = &mt('No new courses or communities');  
             } else {  
                 $rolesmsg = &mt('No role changes');  
             }  
         }  
         if ($hasgroups && !(keys(%changed_groups)) && !(grep(/gr/,@changed_roles))) {  
             $groupsmsg = &mt('No changes in course/community groups');  
         }  
         if (!@changed_roles && !(keys(%changed_groups))) {  
             if (($msg ne '') || ($groupsmsg ne '')) {  
                 $msg .= '<ul>';  
                 if ($rolesmsg) {  
                     $msg .= '<li>'.$rolesmsg.'</li>';  
                 }  
                 if ($groupsmsg) {  
                     $msg .= '<li>'.$groupsmsg.'</li>';  
                 }  
                 $msg .= '</ul>';  
             } else {  
                 $msg = '&nbsp;<span class="LC_cusr_emph">'.$rolesmsg.'</span><br />';  
             }  
             return $msg;  
         }  
     }  
     my $changemsg;  
     if (@changed_roles > 0) {  
         if (keys(%newgroup) > 0) {  
             my $groupmsg;  
             foreach my $item (sort(keys(%newgroup))) {  
                 if (&is_active_course($item,$refresh,$update,\%roleshash)) {  
                     $groupmsg .= '<li>'.  
                                  &mt('[_1] with status: [_2].',  
                                  $item,$newgroup{$item}).'</li>';  
                 }  
             }  
             if ($groupmsg) {  
                 $changemsg .= '<li>'.  
                               &mt('Courses with new groups').'</li>'.  
                               '<ul>'.$groupmsg.'</ul></li>';  
             }  
         }  
         if (keys(%newrole) > 0) {  
             $changemsg .= '<li>'.&mt('New roles').  
                           '<ul>';  
             foreach my $item (sort(keys(%newrole))) {  
                 $changemsg .= '<li>'.  
                               &mt('[_1] with status: [_2].',  
                               $item,$newrole{$item}).'</li>';  
             }  
             $changemsg .= '</ul></li>';  
         }  
         if (keys(%customprivchg) > 0) {  
             $changemsg .= '<li>'.  
                           &mt('Custom roles with privilege changes').  
                           '<ul>';  
             foreach my $item (sort(keys(%customprivchg))) {  
                 $changemsg .= '<li>'.$item.'</li>';  
             }  
             $changemsg .= '</ul></li>';  
         }  
         if (keys(%rolechange) > 0) {  
             $changemsg .= '<li>'.  
                           &mt('Existing roles with status changes').'</li>'.  
                           '<ul>';  
             foreach my $item (sort(keys(%rolechange))) {  
                 $changemsg .= '<li>'.  
                               &mt('[_1] status now: [_2].',$item,  
                               $rolechange{$item}).'</li>';  
             }  
             $changemsg .= '</ul></li>';  
         }  
         if (keys(%deletedroles) > 0) {  
             $changemsg .= '<li>'.  
                           &mt('Existing roles deleted').'</li>'.  
                           '<ul>';  
             foreach my $item (sort(keys(%deletedroles))) {  
                 $changemsg .= '<li>'.$item.'</li>';  
             }  
             $changemsg .= '</ul></li>';  
         }  
     }  
     if ((keys(%changed_groups) > 0) || (keys(%groupchange) > 0)) {  
         my $groupchgmsg;  
         foreach my $key (sort(keys(%changed_groups))) {  
             my $crs = 'gr/'.$key;  
             $crs =~ s/_/\//;  
             if (&is_active_course($crs,$refresh,$update,\%roleshash)) {  
                 if (ref($changed_groups{$key}) eq 'HASH') {  
                     my @showgroups;  
                     foreach my $group (sort(keys(%{$changed_groups{$key}}))) {  
                         if ($changed_groups{$key}{$group} eq 'active') {  
                             push(@showgroups,$group);  
                         }  
                     }  
                     if (@showgroups > 0) {  
                         $groupchgmsg .= '<li>'.  
                                         &mt('Course: [_1], groups: [_2].',$key,  
                                         join(', ',@showgroups)).  
                                         '</li>';  
                     }  
                 }  
             }  
         }  
         if (keys(%groupchange) > 0) {  
             $groupchgmsg .= '<li>'.  
                           &mt('Existing course/community groups with status changes').'</li>'.  
                           '<ul>';  
             foreach my $crs (sort(keys(%groupchange))) {  
                 if (ref($groupchange{$crs}) eq 'HASH') {  
                     $groupchgmsg .= '<li>'.&mt('Course/Community: [_1]','<b>'.$crs.'</b><ul>');  
                     foreach my $group (sort(keys(%{$groupchange{$crs}}))) {  
                         $groupchgmsg .= '<li>'.&mt('Group: [_1] status now: [_2].','<b>'.$group.'</b>',$groupchange{$crs}{$group}).'</li>';  
                     }  
                     $groupchgmsg .= '</ul></li>';  
                 }  
             }  
             $groupchgmsg .= '</ul></li>';  
         }  
         if ($groupchgmsg) {  
             $changemsg .= '<li>'.  
                           &mt('Courses with changes in groups').'</li>'.  
                           '<ul>'.$groupchgmsg.'</ul></li>';  
         }  
     }  
     if ($changemsg) {  
         $msg .= '<ul>'.$changemsg.'</ul>';  
     }  
     &Apache::lonnet::set_userprivs(\%userroles,\%allroles,\%allgroups,\%groups_roles);  
     my ($curr_is_adv,$curr_role_adv,$curr_author,$curr_role_author);  
     $curr_author = $env{'user.author'};  
     if (($env{'request.role'} =~/^au/) || ($env{'request.role'} =~/^ca/) ||  
         ($env{'request.role'} =~/^aa/)) {  
         $curr_role_author=1;  
     }  
     $curr_is_adv = $env{'user.adv'};  
     $curr_role_adv = $env{'request.role.adv'};  
     if (keys(%userroles) > 0) {  
         foreach my $role (@changed_roles) {  
             unless(grep(/^\Q$role\E$/,@rolecodes)) {  
                 push(@rolecodes,$role);  
             }  
         }  
         unless(grep(/^\Qcm\E$/,@rolecodes)) {  
             push(@rolecodes,'cm');  
         }  
         &Apache::lonnet::appenv(\%userroles,\@rolecodes);  
     }  
     my %newenv;  
     if (&Apache::lonnet::is_advanced_user($env{'user.domain'},$env{'user.name'})) {  
         unless ($curr_is_adv) {  
             $newenv{'user.adv'} = 1;  
         }  
     } elsif ($curr_is_adv && !$curr_role_adv) {  
         &Apache::lonnet::delenv('user.adv');  
     }  
     my %authorroleshash =  
         &Apache::lonnet::get_my_roles('','','userroles',['active'],['au','ca','aa']);  
     if (keys(%authorroleshash)) {  
         unless ($curr_author) {  
             $newenv{'user.author'} = 1;  
         }  
     } elsif ($curr_author && !$curr_role_author) {  
         &Apache::lonnet::delenv('user.author');  
     }  
     if ($env{'request.course.id'}) {  
         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};  
         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};  
         my (@activecrsgroups,$crsgroupschanged);  
         if ($env{'request.course.groups'}) {  
             @activecrsgroups = split(/:/,$env{'request.course.groups'});  
             foreach my $item (keys(%deletedroles)) {  
                 if ($item =~ m{^gr\./\Q$cdom\E/\Q$cnum\E/(\w+)$}) {  
                     if (grep(/^\Q$1\E$/,@activecrsgroups)) {  
                         $crsgroupschanged = 1;  
                         last;  
                     }  
                 }  
             }  
         }  
         unless ($crsgroupschanged) {  
             foreach my $item (keys(%newgroup)) {  
                 if ($item =~ m{^gr\./\Q$cdom\E/\Q$cnum\E/(\w+)$}) {  
                     if ($newgroup{$item} eq 'active') {  
                         $crsgroupschanged = 1;  
                         last;  
                     }  
                 }  
             }  
         }  
         if ((ref($changed_groups{$env{'request.course.id'}}) eq 'HASH') ||  
             (ref($groupchange{"/$cdom/$cnum"}) eq 'HASH') ||  
             ($crsgroupschanged)) {  
             my %grouproles =  &Apache::lonnet::get_my_roles('','','userroles',  
                                                             ['active'],['gr'],[$cdom],1);  
             my @activegroups;  
             foreach my $item (keys(%grouproles)) {  
                 next unless($item =~ /^\Q$cnum\E:\Q$cdom\E/);  
                 my $group;  
                 my ($crsn,$crsd,$role,$remainder) = split(/:/,$item,4);  
                 if ($remainder =~ /:/) {  
                     (my $other,$group) = ($remainder =~ /^([\w:]+):([^:]+)$/);  
                 } else {  
                     $group = $remainder;  
                 }  
                 if ($group ne '') {  
                     push(@activegroups,$group);  
                 }  
             }  
             $newenv{'request.course.groups'} = join(':',@activegroups);  
         }  
     }  
     if (keys(%newenv)) {  
         &Apache::lonnet::appenv(\%newenv);  
     }  
     return $msg;  
 }  
   
 sub curr_role_status {  
     my ($start,$end,$refresh,$update) = @_;  
     if (($start) && ($start<0)) { return 'deleted' };  
     my $status = 'active';  
     if (($end) && ($end<=$update)) {  
         $status = 'previous';  
     }  
     if (($start) && ($refresh<$start)) {  
         $status = 'future';  
     }  
     return $status;  
 }  
   
 sub gather_roleprivs {  
     my ($allroles,$allgroups,$userroles,$area,$role,$tstart,$tend,$status) = @_;  
     return unless ((ref($allroles) eq 'HASH') && (ref($allgroups) eq 'HASH') && (ref($userroles) eq 'HASH'));  
     if (($area ne '') && ($role ne '')) {  
         &Apache::lonnet::userrolelog($role,$env{'user.name'},$env{'user.domain'},  
                                      $area,$tstart,$tend);  
         my $spec=$role.'.'.$area;  
         $userroles->{'user.role.'.$spec} = $tstart.'.'.$tend;  
         my ($tdummy,$tdomain,$trest)=split(/\//,$area);  
         if ($status eq 'active') {  
             if ($role =~ /^cr\//) {  
                 &Apache::lonnet::custom_roleprivs($allroles,$role,$tdomain,$trest,$spec,$area);  
             } elsif ($role eq 'gr') {  
                 my %rolehash = &Apache::lonnet::get('roles',[$area.'_'.$role],  
                                                     $env{'user.domain'},  
                                                     $env{'user.name'});  
                 my ($trole) = split(/_/,$rolehash{$area.'_'.$role},2);  
                 (undef,my $group_privs) = split(/\//,$trole);  
                 $group_privs = &unescape($group_privs);  
                 &Apache::lonnet::group_roleprivs($allgroups,$area,$group_privs,$tend,$tstart);  
             } else {  
                 &Apache::lonnet::standard_roleprivs($allroles,$role,$tdomain,$spec,$trest,$area);  
             }  
         }  
     }  
     return;  
 }  
   
 sub is_active_course {  
     my ($rolekey,$refresh,$update,$roleshashref) = @_;  
     return unless(ref($roleshashref) eq 'HASH');  
     my ($role,$cdom,$cnum) = split(/\//,$rolekey);  
     my $is_active;  
     foreach my $key (keys(%{$roleshashref})) {  
         if ($key =~ /^\Q$cnum\E:\Q$cdom\E:/) {  
             my ($tstart,$tend) = split(/:/,$roleshashref->{$key});  
             my $status = &curr_role_status($tstart,$tend,$refresh,$update);  
             if ($status eq 'active') {  
                 $is_active = 1;  
                 last;  
             }  
         }  
     }  
     return $is_active;  
 }  
   
 1;  1;
 __END__  __END__
   
Line 2571  course they should act on, etc. Both in Line 1849  course they should act on, etc. Both in
 handler determines via C<lonnet>'s C<&allowed> function that a certain  handler determines via C<lonnet>'s C<&allowed> function that a certain
 action is not allowed, C<lonroles> is used as error handler. This  action is not allowed, C<lonroles> is used as error handler. This
 allows the user to select another role which may have permission to do  allows the user to select another role which may have permission to do
 what they were trying to do.  what they were trying to do. C<lonroles> can also be accessed via the
   B<CRS> button in the Remote Control.
   
 =begin latex  =begin latex
   

Removed from v.1.256.2.6.2.3  
changed lines
  Added in v.1.256.2.7


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