File:  [LON-CAPA] / loncom / interface / lonviewclasslist.pm
Revision 1.19: download - view: text, annotated - select for diffs
Tue Sep 26 19:11:02 2023 UTC (7 months, 2 weeks ago) by raeburn
Branches: MAIN
CVS tags: HEAD
- Args expected by process_changes() and print_config_box() changed in
  courseprefs.pm (rev. 1.93 and rev. 1.99 respectively).

    1: # The LearningOnline Network with CAPA
    2: # Handler to display the classlist 
    3: #
    4: # $Id: lonviewclasslist.pm,v 1.19 2023/09/26 19:11:02 raeburn Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: ###############################################################
   29: ##############################################################
   30: 
   31: package Apache::lonviewclasslist;
   32: 
   33: use strict;
   34: use Apache::loncoursedata();
   35: use Apache::loncommon();
   36: use Apache::lonhtmlcommon();
   37: use Apache::courseprefs();
   38: use Apache::Constants qw(:common :http REDIRECT);
   39: use Apache::lonlocal;
   40: use Apache::lonnet;
   41: 
   42: 
   43: ###################################################################
   44: ###################################################################
   45: 
   46: =pod
   47: 
   48: =item &handler
   49: 
   50: The typical handler you see in all these modules.  Takes $r, the
   51: http request, as an argument.  
   52: 
   53: =cut
   54: 
   55: ###################################################################
   56: ###################################################################
   57: sub handler {
   58:     my $r=shift;
   59:     if ($r->header_only) {
   60:         &Apache::loncommon::content_type($r,'text/html');
   61:         $r->send_http_header;
   62:         return OK;
   63:     }
   64:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
   65:                                  ['register','forceedit','action','symb','todocs']);
   66:     if (! ($env{'request.course.fn'})) {
   67:         $env{'user.error.msg'}=
   68:             "/adm/viewclasslist:not in course role";
   69:         return HTTP_NOT_ACCEPTABLE; 
   70:     }
   71:     &Apache::loncommon::content_type($r,'text/html');
   72:     $r->send_http_header;
   73:     #
   74:     my $start_page;
   75:     if ($env{'form.register'}) {
   76:         $start_page = &Apache::loncommon::start_page('Classlist',undef,
   77:                              {'force_register' => $env{'form.register'}});
   78:     } else {
   79:         my $brcrum = [{'href' => 'adm/viewclasslist',
   80:                        'text' => 'View Classlist'},];
   81:         $start_page = &Apache::loncommon::start_page('Classlist',undef,
   82:                                                      {'bread_crumbs' => $brcrum});
   83:     }
   84:     $r->print(<<ENDHEADER);
   85: $start_page
   86: ENDHEADER
   87: 
   88:     # Get classlist view settings
   89:     my %viewsettings = &retrieve_view_settings();
   90:     my $crstype = &Apache::loncommon::course_type();
   91: 
   92:     if (($env{'form.forceedit'}) || ($env{'form.action'} eq 'setconfig'))  {
   93:         if (&Apache::lonnet::allowed('opa',$env{'request.course.id'})) {
   94:             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
   95:             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
   96:             my $rosterprefs = &roster_prefs($crstype);
   97:             my $allitems = {};
   98:             if ($env{'form.action'} eq 'setconfig') {
   99:                 my %values=&Apache::lonnet::dump('environment',$cdom,$cnum);
  100:                 if (keys(%values) > 0) {
  101:                     my ($numchanged,%changes,%disallowed);
  102:                     my $prefs = {
  103:                                     classlists => $rosterprefs,
  104:                                 };
  105:                     $changes{'classlists'} = {};
  106:                     &Apache::courseprefs::process_changes($cdom,$cnum,'classlists',\%values,
  107:                                                           $rosterprefs,
  108:                                                           $changes{'classlists'},
  109:                                                           $allitems,\%disallowed,$crstype);
  110:                     my $message;
  111:                     if (keys(%{$changes{'classlists'}}) > 0) {
  112:                         my $actions = ['classlists'];
  113:                         $message =
  114:                             &Apache::courseprefs::store_changes($cdom,$cnum,$actions,
  115:                                                                 $actions,$prefs,\%values,
  116:                                                                 \%changes,$crstype);
  117:                     } else {
  118:                         if ($crstype eq 'Community') {
  119:                             $message = &mt('No changes made to community settings.');
  120:                         } else {
  121:                             $message = &mt('No changes made to course settings.');
  122:                         }
  123:                     }
  124:                     $r->print(&Apache::loncommon::confirmwrapper($message));
  125:                } else {
  126:                     $r->print('<div class="LC_info">'.
  127:                               &mt('Unable to retrieve current settings.').'<br />'.
  128:                               &mt('No changes saved.').
  129:                               '</div>');
  130:                }
  131:            } else {
  132:                my $current = {};
  133:                my @settings = ('student_classlist_view','student_classlist_opt_in',
  134:                                'student_classlist_portfiles');
  135:                foreach my $setting (@settings) {
  136:                    $current->{$setting} = $env{"course.$env{'request.course.id'}.$setting"};
  137:                }
  138:                my ($output,$rowtotal) =
  139:                    &Apache::courseprefs::print_config_box($r,$cdom,$cnum,'display',
  140:                                                           'viewableroster',
  141:                                                           $rosterprefs,$current,
  142:                                                           $allitems,$crstype);
  143:                if ($output) {
  144:                    $r->print('<form method="post" name="display" action="/adm/viewclasslist">'."\n".
  145:                              '<input type="hidden" name="action" value="setconfig" />'."\n".
  146:                              '<input type="hidden" name="register" value="'.$env{'form.register'}.'" />'."\n".
  147:                              '<input type="hidden" name="forceedit" value="'.$env{'form.forceedit'}.'" />'."\n");
  148:                    if ($env{'form.symb'}) {
  149:                         $r->print('<input type="hidden" name="symb" value="'.$env{'form.symb'}.'" />'."\n");
  150:                    }
  151:                    if ($env{'form.symb'}) {
  152:                         $r->print('<input type="hidden" name="todocs" value="'.$env{'form.todocs'}.'" />'."\n");
  153:                    }
  154:                    $r->print('<div class="LC_left_float">'.
  155:                              $output.
  156:                              '</div><br clear="all" />'.
  157:                              '<input type="submit" value="'.&mt('Save').'" />'.
  158:                              '</form>');
  159:                } else {
  160:                    $r->print('<div class="LC_info">');
  161:                    if ($crstype eq 'Community') {
  162:                        $r->print(&mt('No member-viewable community roster settings available.'));
  163:                    } else {
  164:                        $r->print(&mt('No student-viewable course roster settings available.'));
  165:                    }
  166:                    $r->print('</div>');
  167:                }
  168:            }
  169:         } else {
  170:             $r->print('<div class="LC_info">');
  171:             if ($crstype eq 'Community') {
  172:                 $r->print(&mt('You do not have rights to modify member-viewable community roster settings.'));
  173:             } else {
  174:                 $r->print(&mt('You do not have rights to modify student-viewable course roster settings.'));
  175:             }
  176:             $r->print('</div>');
  177:         }
  178:     } else {
  179: 
  180:         # Print classlist
  181:         if (keys(%viewsettings) > 0) {
  182:             $r->print(&html_classlist($r,$crstype,\%viewsettings));
  183:         } else {
  184:             $r->print('<div class="LC_info">');
  185:             if ($crstype eq 'Community') {
  186:                 $r->print(&mt("Display of a member-viewable community roster is not currently enabled."));
  187:             } else {
  188:                 $r->print(&mt("Display of a student-viewable course roster is not currently enabled."));
  189:             }
  190:             $r->print('</div>');
  191:         }
  192:     }
  193:     #
  194:     # Finish up
  195:     $r->print(&Apache::loncommon::end_page());
  196:     return OK;
  197: }
  198: 
  199: sub retrieve_view_settings {
  200:     my %viewsettings;
  201:     if (exists($env{'request.course.id'})) {
  202:         my $cid = $env{'request.course.id'};
  203:         my $viewpermission = 'course.'.$cid.'.student_classlist_view';
  204:         my $student_opt_in = 'course.'.$cid.'.student_classlist_opt_in';
  205:         my $portfiles_link = 'course.'.$cid.'.student_classlist_portfiles';
  206:         if (exists($env{$viewpermission}) &&
  207:             $env{$viewpermission} =~ /^(all|section)$/) {
  208:             $viewsettings{'permission'} = $env{$viewpermission};
  209:             if ($viewsettings{'permission'} =~ /^section$/i) {
  210:                 $viewsettings{'limit_to_section'} = 1;
  211:             } else {
  212:                 $viewsettings{'limit_to_section'} = 0;
  213:             }
  214:             $viewsettings{'student_opt_in'} = $env{$student_opt_in};
  215:             $viewsettings{'portfiles_link'} = $env{$portfiles_link};
  216:         }
  217:     }
  218:     return %viewsettings;
  219: }
  220: 
  221: sub roster_prefs {
  222:     my ($crstype) = @_;
  223:     my %lt;
  224:     if ($crstype eq 'Community') {
  225:         %lt = &Apache::lonlocal::texthash (
  226:                   svrs => 'Member-viewable roster settings',
  227:                   stuv => 'Member-viewable membership list options',
  228:                   stul => 'Member agreement needed to be listed',
  229:         );
  230:     } else {
  231:         %lt = &Apache::lonlocal::texthash(
  232:                   svrs => 'Student-viewable roster settings',
  233:                   stuv => 'Student-viewable classlist options',
  234:                   stul => 'Student agreement needed to be listed',
  235:         );
  236:     }
  237:     $lt{'incl'} = &mt('Include link to accessible portfolio files');
  238: 
  239:     return 
  240:         { text => $lt{'svrs'},
  241:           header => [ {col1 => 'Setting',
  242:                        col2 => $lt{'stuv'}}],
  243:           ordered => ['student_classlist_view',
  244:                       'student_classlist_opt_in',
  245:                       'student_classlist_portfiles'],
  246:           itemtext => {
  247:                        student_classlist_view        => $lt{'stuv'},
  248:                        student_classlist_opt_in      => $lt{'stul'},
  249:                        student_classlist_portfiles   => $lt{'incl'},
  250:                       },
  251:         };
  252: }
  253: 
  254: sub html_classlist {
  255:     my ($r,$crstype,$viewsettings) = @_;
  256:     my ($Str,$title,$secdisplay,$cid,$cdom,$cnum,$listtype,%publicroster);
  257:     my $fullroster = &Apache::loncoursedata::get_classlist();
  258:     my $classlist;
  259: 
  260:     my $singular = 'student';
  261:     my $plural = 'students';
  262:     my $titleplural = 'Students';
  263:     my $heading = &mt('Student-viewable course roster');
  264: 
  265:     if ($crstype eq 'Community') {
  266:         $singular = 'member';
  267:         $plural = 'members';
  268:         $titleplural = 'Members';
  269:         $heading = &mt('Member-viewable community roster');
  270:     }
  271: 
  272:     if ($env{'form.action'} eq 'setenv') {
  273:         $Str .= &process_student_prefs($crstype);
  274:     }
  275: 
  276:     $Str .= '<h2>'.$heading.'</h2>';
  277: 
  278:     $cid = $env{'request.course.id'};
  279:     $cdom = $env{'course.'.$cid.'.domain'};
  280:     $cnum = $env{'course.'.$cid.'.num'};
  281: 
  282:     my $title;
  283:     if ($viewsettings->{'limit_to_section'}) {
  284:         if ($env{'request.course.sec'} eq '') {
  285:             $title = &mt($titleplural.' with no section');
  286:             $listtype = 'without a section';
  287:         } else {
  288:             $title = &mt($titleplural.' in section "[_1]"',
  289:                          $env{'request.course.sec'});
  290:             $listtype = 'in the section';
  291:             $secdisplay = " ($env{'request.course.sec'}) ";
  292:         }
  293:     } else {
  294:         $title = &mt($titleplural.' in any section');
  295:         $listtype = 'in the course';
  296:         if ($crstype eq 'Community') {
  297:             $listtype = 'in the community';
  298:         }
  299:     }
  300:     if ($viewsettings->{'student_opt_in'}) {
  301:         if ($env{'request.role'} =~ /^st/)  {
  302:             $Str .= &print_roster_form($crstype);
  303:         }
  304:         %publicroster = &Apache::lonnet::dump('publicroster',$cdom,$cnum);
  305:     }
  306: 
  307:     $Str .= '<h3>'.$title.'</h3>';
  308: 
  309:     my $fullcount = 0;
  310:     my $publiccount = 0;
  311:     my $displaycount = 0;
  312:     my $sectionidx  = &Apache::loncoursedata::CL_SECTION();
  313:     my $statusidx   = &Apache::loncoursedata::CL_STATUS();
  314: 
  315:     foreach my $student (keys(%{$fullroster})) {
  316:         my $section  = $fullroster->{$student}->[$sectionidx];
  317:         my $status   = $fullroster->{$student}->[$statusidx];
  318:         next if (lc($status) ne 'active');
  319:         if ($viewsettings->{'limit_to_section'}) {
  320:             next if ($section ne $env{'request.course.sec'});
  321:         }
  322:         $fullcount ++;
  323:         if ($viewsettings->{'student_opt_in'}) {
  324:             if ($publicroster{$student}) {
  325:                 $classlist->{$student} = $fullroster->{$student};
  326:                 $publiccount ++;
  327:             }
  328:         } else {
  329:             $classlist->{$student} = $fullroster->{$student};
  330:         }
  331:     }
  332:     if ($viewsettings->{'student_opt_in'}) {
  333:         $displaycount = $publiccount;
  334:         if ($fullcount > $publiccount) {
  335:             if ($publiccount) {
  336:                 $Str .= &mt('Only '.$plural.' who have opted to be listed in the roster ([_1] out of [_2] '.$plural.') are shown.',$publiccount,$fullcount).'<br />';
  337:             } else {
  338:                 if ($fullcount == 1) {
  339:                     $Str .= &mt('The single '.$singular.' '.$listtype.'[_1] has opted not to be listed in the roster.',$secdisplay);
  340:                 } else {
  341:                     $Str .= &mt('None of the [_1] '.$plural.' '.$listtype.'[_2] have opted to be listed in the roster.',$fullcount,$secdisplay);
  342:                 }
  343:                 return $Str;
  344:             }
  345:         } else {
  346:             if ($fullcount > 1) {
  347:                 $Str .= &mt('All [_1] '.$plural.' '.$listtype.'[_2] have opted to be listed in the roster.',$fullcount,$secdisplay); 
  348:             } elsif ($fullcount == 1) {
  349:                 $Str .= &mt('The single '.$singular.' '.$listtype.'[_1] has opted to be listed in the roster.',$secdisplay);
  350:             }
  351:         }
  352:     } else {
  353:         $displaycount = $fullcount;
  354:         if ($fullcount > 1) {
  355:             $Str .= &mt('All [_1] '.$plural.' '.$listtype.'[_2] are listed in the roster.',$fullcount,$secdisplay);
  356:         } elsif ($fullcount == 1) {
  357:             $Str .= &mt('There is only a single '.$singular.' '.$listtype.'[_1]',$secdisplay);
  358:         }
  359:     }
  360:     undef($fullroster);
  361: 
  362:     if (!$displaycount) {
  363:         $Str .= &mt('There are currently no '.$plural.' to display.');
  364:         return $Str;
  365:     }
  366: 
  367:     # Set up a couple variables.
  368:     my $usernameidx = &Apache::loncoursedata::CL_SNAME();
  369:     my $domainidx   = &Apache::loncoursedata::CL_SDOM();
  370:     my $fullnameidx = &Apache::loncoursedata::CL_FULLNAME();
  371: 
  372:     # Sort the students
  373:     my $sortby = $fullnameidx;
  374:     my @Sorted_Students = sort {
  375:         lc($classlist->{$a}->[$sortby])  cmp lc($classlist->{$b}->[$sortby])
  376:         } (keys(%$classlist));
  377:     $Str .= '<br />'.&Apache::loncommon::start_data_table()."\n".
  378:             &Apache::loncommon::start_data_table_header_row()."\n".
  379:         '<th></th>'. # for the count
  380:         '<th>'.&mt('Name').'</th>'.
  381:         '<th>'.&mt('Username').'</th>';
  382:     if (! $viewsettings->{'limit_to_section'}) {
  383:         $Str .= '<th>'.&mt('Section').'</th>';
  384:     }
  385:     if ($viewsettings->{'portfiles_link'}) {
  386:         $Str .= '<th>'.&mt('Available Portfolio files').'</th>';
  387:     }
  388:     $Str .= &Apache::loncommon::end_data_table_header_row();
  389:     my $count ++;
  390:     foreach my $student (@Sorted_Students) {
  391:         my $username = $classlist->{$student}->[$usernameidx];
  392:         my $domain   = $classlist->{$student}->[$domainidx];
  393:         my $fullname = $classlist->{$student}->[$fullnameidx];
  394:         if ($fullname =~ /^\s*$/) {
  395:             $fullname = &mt('Name not given');
  396:         }
  397:         my $section  = $classlist->{$student}->[$sectionidx];
  398:         if ($section eq '') {
  399:             $section = &mt('none');
  400:         }
  401:         $Str .= &Apache::loncommon::start_data_table_row()."\n".
  402:             '<td>'.$count++.'</td>'.
  403:             '<td>'.&Apache::loncommon::aboutmewrapper($fullname,
  404:                                                       $username,
  405:                                                       $domain).'</td>'.
  406:             '<td>'.('&nbsp;'x2).
  407:             &Apache::loncommon::messagewrapper
  408:             ('<img src="/adm/lonIcons/mailto.gif" border="0" />&nbsp;'.
  409:              $username.':'.$domain,$username,$domain).'</td>';
  410:         if (! $viewsettings->{'limit_to_section'}) {
  411:             $Str .= '<td>'.$section.'</td>';
  412:         }
  413:         if ($viewsettings->{'portfiles_link'}) {
  414:             my $filecounts = &Apache::lonaboutme::portfolio_files($r,'showlink',undef,undef,$domain,$username,$fullname);
  415:             my $link;
  416:             if (ref($filecounts) eq 'HASH') {
  417:                 $link = &mt('[quant,_1,file,files,No files]',$filecounts->{'both'});
  418:                 if ($filecounts->{'both'} > 0) {
  419:                     $link = '<a href="/adm/'.$domain.'/'.$username.'/aboutme/portfolio?classlist">'.$link.'</a>'; 
  420:                 }
  421:             } else {
  422:                 $link = '<span class="LC_error">'.&mt("Error retrieving file information.").'</span>';
  423:             }
  424:             $Str .= '<td>'.$link.'</td>';
  425:         }
  426:         $Str .= &Apache::loncommon::end_data_table_row()."\n";
  427:     }
  428:     $Str .= &Apache::loncommon::end_data_table();
  429:     return $Str;
  430: }
  431: 
  432: sub print_roster_form {
  433:     my ($crstype) = @_;
  434:     my $cid = $env{'request.course.id'};
  435:     my $showinroster = $env{'environment.internal.'.$cid.'.showinroster'};
  436:     my ($showoff,$showon);
  437:     if ($showinroster) {
  438:         $showon = ' checked="checked" ';
  439:         $showoff = ' ';
  440:     } else {
  441:         $showoff = ' checked="checked" ';
  442:         $showon = ' ';
  443:     }
  444:     my $singular = 'student';
  445:     if ($crstype eq 'Community') {
  446:         $singular = 'member';
  447:     }
  448:     my $output =
  449:         '<div class="LC_left_float">'
  450:        .'<fieldset><legend>'.&mt('Your roster setting').'</legend>';
  451:     if ($showinroster) {
  452:         $output .= &mt("You are currently listed in the $singular-viewable roster.");
  453:     } else {
  454:         $output .=  &mt("You are currently [_1]not[_2] listed in the $singular-viewable roster.",'<b>','</b>');
  455:     }
  456:     $output .= '<br />'.&mt('Include yourself in the roster?').'&nbsp;&nbsp;'.
  457:         '<form name="studentparm" method="post" action="">'.
  458:         '<span class="LC_nobreak"><label><input type="radio" name="showinroster" value="1"'.$showon.'/>'.&mt('Yes').'</label>&nbsp;&nbsp;<label>'.
  459:         '<input type="radio" name="showinroster" value="0"'.$showoff.'/>'.&mt('No').
  460:         '</label></span><br /><br />'.
  461:         '<input type="hidden" name="action" value="setenv" />'.
  462:         '<input type="submit" name="studentsubmit" value="'.&mt('Save').'" />'.
  463:         '</form></fieldset></div><br clear="all" />';
  464:     return $output;
  465: }
  466: 
  467: sub process_student_prefs {
  468:     my ($crstype) = @_;
  469:     my $cid = $env{'request.course.id'};
  470:     my $cdom = $env{'course.'.$cid.'.domain'};
  471:     my $cnum = $env{'course.'.$cid.'.num'};
  472:     my $uname = $env{'user.name'};
  473:     my $udom = $env{'user.domain'};
  474:     my $student = $uname.':'.$udom;
  475:     my %pubroster = &Apache::lonnet::get('publicroster',[$student],$cdom,$cnum);
  476:     my $visibility = &mt('off');
  477:     my $showinroster = $env{'form.showinroster'};
  478:     if ($showinroster) {
  479:         $visibility = &mt('on');
  480:     }
  481:     my $sturoster = 0;
  482:     if ($pubroster{$student}) {
  483:         $sturoster = 1;
  484:     }
  485: 
  486:     my $singular = 'student';
  487:     if ($crstype eq 'Community') {
  488:         $singular = 'member';
  489:     }
  490:     my $output;
  491:     if ($sturoster ne $showinroster) {
  492:         my %changeHash = (
  493:             'environment.internal.'.$cid.'.showinroster' => $showinroster,
  494:         );
  495:         my $putresult = &Apache::lonnet::put('environment',
  496:                                              \%changeHash,$udom,$uname);
  497:         if ($putresult eq 'ok') {
  498:             &Apache::lonnet::appenv(\%changeHash);
  499:             my $result = &Apache::lonnet::put('publicroster',{$student => $showinroster,},$cdom,$cnum);
  500:             if ($result eq 'ok') {
  501:                 $output .=
  502:                     &Apache::lonhtmlcommon::confirm_success(
  503:                         &mt("Display of your name in the $singular-viewable roster set to [_1].",'<b>'.$visibility.'</b>'));
  504:             } else {
  505:                 $output .=
  506:                     &Apache::lonhtmlcommon::confirm_success(
  507:                         &mt('Error occurred saving display setting.'),1);
  508:             }
  509:         } else {
  510:             $output .=
  511:                 &Apache::lonhtmlcommon::confirm_success(
  512:                     &mt('Error occurred saving display setting.'),1);
  513:         }
  514:     } else {
  515:         $output .=
  516:             &Apache::lonhtmlcommon::confirm_success(
  517:                 &mt("Display of your name in the $singular-viewable roster unchanged (set to [_1]).",'<b>'.$visibility.'</b>'));
  518:     }
  519:     $output = &Apache::loncommon::confirmwrapper($output);
  520:     return $output;
  521: }
  522: 
  523: 
  524: 
  525: 
  526: ###################################################################
  527: ###################################################################
  528: 
  529: 1;
  530: __END__
  531: 
  532: 

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