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