![]() ![]() | ![]() |
- fixes so that usenrames with _ work again
1: # The LearningOnline Network with CAPA 2: # Handler to drop and add students in courses 3: # 4: # $Id: londropadd.pm,v 1.123 2005/01/11 22:12:22 albertel 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: 32: package Apache::londropadd; 33: 34: use strict; 35: use Apache::lonnet(); 36: use Apache::loncommon(); 37: use Apache::lonhtmlcommon(); 38: use Apache::Constants qw(:common :http REDIRECT); 39: use Spreadsheet::WriteExcel; 40: use Apache::lonstathelpers(); 41: use Apache::lonlocal; 42: 43: ############################################################### 44: ############################################################### 45: sub header { 46: my $bodytag=&Apache::loncommon::bodytag('Enrollment Manager'); 47: my $title = &mt('LON-CAPA Enrollment Manager'); 48: return(<<ENDHEAD); 49: <html> 50: <head> 51: <title>$title</title> 52: </head> 53: $bodytag 54: <form method="post" enctype="multipart/form-data" 55: action="/adm/dropadd" name="studentform"> 56: ENDHEAD 57: } 58: 59: ############################################################### 60: ############################################################### 61: # Drop student from all sections of a course, except optional $csec 62: sub modifystudent { 63: my ($udom,$unam,$courseid,$csec,$desiredhost)=@_; 64: # if $csec is undefined, drop the student from all the courses matching 65: # this one. If $csec is defined, drop them from all other sections of 66: # this course and add them to section $csec 67: $courseid=~s/\_/\//g; 68: $courseid=~s/^(\w)/\/$1/; 69: my %roles = &Apache::lonnet::dump('roles',$udom,$unam); 70: my ($tmp) = keys(%roles); 71: # Bail out if we were unable to get the students roles 72: return "$1" if ($tmp =~ /^(con_lost|error|no_such_host)/i); 73: # Go through the roles looking for enrollment in this course 74: my $result = ''; 75: foreach my $course (keys(%roles)) { 76: if ($course=~/^$courseid(?:\/)*(?:\s+)*(\w+)*\_st$/) { 77: # We are in this course 78: my $section=$1; 79: $section='' if ($course eq $courseid.'_st'); 80: if (defined($csec) && $section eq $csec) { 81: $result .= 'ok:'; 82: } elsif ( ((!$section) && (!$csec)) || ($section ne $csec) ) { 83: my (undef,$end,$start)=split(/\_/,$roles{$course}); 84: my $now=time; 85: # if this is an active role 86: if (!($start && ($now<$start)) || !($end && ($now>$end))) { 87: my $reply=&Apache::lonnet::modifystudent 88: # dom name id mode pass f m l g 89: ($udom,$unam,'', '', '',undef,undef,undef,undef, 90: $section,time,undef,undef,$desiredhost); 91: $result .= $reply.':'; 92: } 93: } 94: } 95: } 96: if ($result eq '') { 97: $result = 'Unable to find section for this student'; 98: } else { 99: $result =~ s/(ok:)+/ok/g; 100: } 101: return $result; 102: } 103: 104: ############################################################### 105: ############################################################### 106: # build a domain and server selection form 107: sub domain_form { 108: my ($defdom) = @_; 109: # Set up domain and server selection forms 110: # 111: # Get the domains 112: my @domains = &Apache::loncommon::get_domains(); 113: # build up the menu information to be passed to 114: # &Apache::loncommon::linked_select_forms 115: my %select_menus; 116: foreach my $dom (@domains) { 117: # set up the text for this domain 118: $select_menus{$dom}->{'text'}= $dom; 119: # we want a choice of 'default' as the default in the second menu 120: $select_menus{$dom}->{'default'}= 'default'; 121: $select_menus{$dom}->{'select2'}->{'default'} = 'default'; 122: # Now build up the other items in the second menu 123: my %servers = &Apache::loncommon::get_library_servers($dom); 124: foreach my $server (keys(%servers)) { 125: $select_menus{$dom}->{'select2'}->{$server} 126: = "$server $servers{$server}"; 127: } 128: } 129: my $result = &Apache::loncommon::linked_select_forms 130: ('studentform',' with home server ',$defdom, 131: 'lcdomain','lcserver',\%select_menus); 132: return $result; 133: } 134: 135: ############################################################### 136: ############################################################### 137: # Menu Phase One 138: sub print_main_menu { 139: my ($r,$enrl_permission,$view_permission)=@_; 140: # 141: my ($cdom,$cnum) = split/_/,$ENV{'request.course.id'}; 142: my @menu = 143: ( 144: { text => 'Upload a class list', 145: help => 'Course_Create_Class_List', 146: action => 'upload', 147: permission => $enrl_permission, 148: }, 149: { text => 'Enroll a single student', 150: help => 'Course_Add_Student', 151: action => 'enrollstudent', 152: permission => $enrl_permission, 153: }, 154: { text => 'Modify student data', 155: help => 'Course_Modify_Student_Data', 156: action => 'modifystudent', 157: permission => $enrl_permission, 158: }, 159: { text => 'View Class List', 160: help => 'Course_View_Class_List', 161: action => 'classlist', 162: permission => $view_permission, 163: }, 164: { text => 'Drop Students', 165: help => 'Course_Drop_Student', 166: action => 'drop', 167: permission => $enrl_permission, 168: }, 169: { text => 'Automated Enrollment Manager', 170: permission => &Apache::lonnet::auto_run($cnum,$cdom), 171: url => '/adm/populate', 172: }, 173: ); 174: my $menu_html = ''; 175: foreach my $menu_item (@menu) { 176: next if (! $menu_item->{'permission'}); 177: $menu_html.='<p>'; 178: $menu_html.='<font size="+1">'; 179: if (exists($menu_item->{'url'})) { 180: $menu_html.=qq{<a href="$menu_item->{'url'}">}; 181: } else { 182: $menu_html.= 183: qq{<a href="/adm/dropadd?action=$menu_item->{'action'}">}; 184: } 185: $menu_html.= &mt($menu_item->{'text'}).'</a></font>'; 186: if (exists($menu_item->{'help'})) { 187: $menu_html.= 188: &Apache::loncommon::help_open_topic($menu_item->{'help'}); 189: } 190: $menu_html.='</p>'.$/; 191: } 192: $r->print($menu_html); 193: return; 194: } 195: 196: ############################################################### 197: ############################################################### 198: sub hidden_input { 199: my ($name,$value) = @_; 200: return '<input type="hidden" name="'.$name.'" value="'.$value.'" />'."\n"; 201: } 202: 203: sub print_upload_manager_header { 204: my ($r,$datatoken,$distotal,$krbdefdom)=@_; 205: my $javascript; 206: # 207: if (! exists($ENV{'form.upfile_associate'})) { 208: $ENV{'form.upfile_associate'} = 'forward'; 209: } 210: if ($ENV{'form.associate'} eq 'Reverse Association') { 211: if ( $ENV{'form.upfile_associate'} ne 'reverse' ) { 212: $ENV{'form.upfile_associate'} = 'reverse'; 213: } else { 214: $ENV{'form.upfile_associate'} = 'forward'; 215: } 216: } 217: if ($ENV{'form.upfile_associate'} eq 'reverse') { 218: $javascript=&upload_manager_javascript_reverse_associate(); 219: } else { 220: $javascript=&upload_manager_javascript_forward_associate(); 221: } 222: # 223: # Deal with restored settings 224: my $password_choice = ''; 225: if (exists($ENV{'form.ipwd_choice'}) && 226: $ENV{'form.ipwd_choice'} ne '') { 227: # If a column was specified for password, assume it is for an 228: # internal password. This is a bug waiting to be filed (could be 229: # local or krb auth instead of internal) but I do not have the 230: # time to mess around with this now. 231: $password_choice = 'int'; 232: } 233: # 234: my $javascript_validations=&javascript_validations('auth',$krbdefdom, 235: $password_choice); 236: my $checked=(($ENV{'form.noFirstLine'})?' checked="1"':''); 237: $r->print('<h3>'.&mt('Uploading Class List')."</h3>\n". 238: "<hr>\n". 239: '<h3>'.&mt('Identify fields')."</h3>\n"); 240: $r->print("<p>\n". 241: &mt('Total number of records found in file: [_1].',$distotal). 242: "\n". 243: "</p><hr>\n"); 244: $r->print(&mt('Enter as many fields as you can. The system will inform you and bring you back to this page if the data selected is insufficient to enroll students in your class.')."<hr>\n"); 245: $r->print(&hidden_input('action','upload'). 246: &hidden_input('state','got_file'). 247: &hidden_input('associate',''). 248: &hidden_input('datatoken',$datatoken). 249: &hidden_input('fileupload',$ENV{'form.fileupload'}). 250: &hidden_input('upfiletype',$ENV{'form.upfiletype'}). 251: &hidden_input('upfile_associate',$ENV{'form.upfile_associate'})); 252: $r->print('<input type="button" value="Reverse Association" '. 253: 'name="'.&mt('Reverse Association').'" '. 254: 'onClick="javascript:this.form.associate.value=\'Reverse Association\';submit(this.form);" />'); 255: $r->print('<input type="checkbox" name="noFirstLine" $checked />'. 256: &mt('Ignore First Line')); 257: $r->print("<hr />\n". 258: '<script type="text/javascript" language="Javascript">'."\n". 259: $javascript."\n".$javascript_validations.'</script>'); 260: } 261: 262: ############################################################### 263: ############################################################### 264: sub javascript_validations { 265: my ($mode,$krbdefdom,$curr_authtype,$curr_authfield)=@_; 266: my $authheader; 267: if ($mode eq 'auth') { 268: my %param = ( formname => 'studentform', 269: kerb_def_dom => $krbdefdom, 270: curr_authtype => $curr_authtype); 271: $authheader = &Apache::loncommon::authform_header(%param); 272: } elsif ($mode eq 'createcourse') { 273: my %param = ( formname => 'ccrs', 274: kerb_def_dom => $krbdefdom, 275: curr_authtype => $curr_authtype ); 276: $authheader = &Apache::loncommon::authform_header(%param); 277: } elsif ($mode eq 'modifycourse') { 278: my %param = ( formname => 'cmod', 279: kerb_def_dom => $krbdefdom, 280: mode => 'modifycourse', 281: curr_authtype => $curr_authtype, 282: curr_autharg => $curr_authfield ); 283: $authheader = &Apache::loncommon::authform_header(%param); 284: } 285: 286: 287: my %alert = &Apache::lonlocal::texthash 288: (username => 'You need to specify the username field.', 289: authen => 'You must choose an authentication type.', 290: krb => 'You need to specify the Kerberos domain.', 291: ipass => 'You need to specify the initial password.', 292: name => 'The optional name field was not specified.', 293: snum => 'The optional student number field was not specified.', 294: section => 'The optional section or group field was not specified.', 295: email => 'The optional email address field was not specified.', 296: continue => 'Continue enrollment?', 297: ); 298: 299: # my $pjump_def = &Apache::lonhtmlcommon::pjump_javascript_definition(); 300: my $function_name =(<<END); 301: function verify_message (vf,founduname,foundpwd,foundname,foundid,foundsec,foundemail) { 302: END 303: my $auth_checks; 304: if ($mode eq 'createcourse') { 305: $auth_checks .= (<<END); 306: if (vf.autoadds[0].checked == true) { 307: if (current.radiovalue == null || current.radiovalue == 'nochange') { 308: alert('$alert{'authen'}'); 309: return; 310: } 311: } 312: END 313: } else { 314: $auth_checks .= (<<END); 315: var foundatype=0; 316: if (founduname==0) { 317: alert('$alert{'username'}'); 318: return; 319: } 320: // alert('current.radiovalue = '+current.radiovalue); 321: if (current.radiovalue == null || current.radiovalue == '' || current.radiovalue == 'nochange') { 322: // They did not check any of the login radiobuttons. 323: alert('$alert{'authen'}'); 324: return; 325: } 326: END 327: } 328: if ($mode eq 'createcourse') { 329: $auth_checks .= " 330: if ( (vf.autoadds[0].checked == true) && 331: (vf.elements[current.argfield].value == null || vf.elements[current.argfield].value == '') ) { 332: "; 333: } elsif ($mode eq 'modifycourse') { 334: $auth_checks .= " 335: if (vf.elements[current.argfield].value == null || vf.elements[current.argfield].value == '') { 336: "; 337: } 338: if ( ($mode eq 'createcourse') || ($mode eq 'modifycourse') ) { 339: $auth_checks .= (<<END); 340: var alertmsg = ''; 341: switch (current.radiovalue) { 342: case 'krb': 343: alertmsg = '$alert{'krb'}'; 344: break; 345: default: 346: alertmsg = ''; 347: } 348: if (alertmsg != '') { 349: alert(alertmsg); 350: return; 351: } 352: } 353: END 354: } else { 355: $auth_checks .= (<<END); 356: foundatype=1; 357: if (current.argfield == null || current.argfield == '') { 358: var alertmsg = ''; 359: switch (current.value) { 360: case 'krb': 361: alertmsg = '$alert{'krb'}'; 362: break; 363: case 'loc': 364: case 'fsys': 365: alertmsg = '$alert{'ipass'}'; 366: break; 367: case 'fsys': 368: alertmsg = ''; 369: break; 370: default: 371: alertmsg = ''; 372: } 373: if (alertmsg != '') { 374: alert(alertmsg); 375: return; 376: } 377: } 378: END 379: } 380: my $optional_checks = ''; 381: if ( ($mode eq 'createcourse') || ($mode eq 'modifycourse') ) { 382: $optional_checks = (<<END); 383: vf.submit(); 384: } 385: END 386: } else { 387: $optional_checks = (<<END); 388: var message=''; 389: if (foundname==0) { 390: message='$alert{'name'}'; 391: } 392: if (foundid==0) { 393: if (message!='') { 394: message+='\\n'; 395: } 396: message+='$alert{'snum'}'; 397: } 398: if (foundsec==0) { 399: if (message!='') { 400: message+='\\n'; 401: } 402: message+='$alert{'section'}'; 403: } 404: if (foundemail==0) { 405: if (message!='') { 406: message+='\\n'; 407: } 408: message+='$alert{'email'}'; 409: } 410: if (message!='') { 411: message+= '\\n$alert{'continue'}'; 412: if (confirm(message)) { 413: vf.state.value='enrolling'; 414: vf.submit(); 415: } 416: } else { 417: vf.state.value='enrolling'; 418: vf.submit(); 419: } 420: } 421: END 422: } 423: my $result = $function_name; 424: if ( ($mode eq 'auth') || ($mode eq 'createcourse') || ($mode eq 'modifycourse') ) { 425: $result .= $auth_checks; 426: } 427: $result .= $optional_checks; 428: if ( ($mode eq 'auth') || ($mode eq 'createcourse') || ($mode eq 'modifycourse') ) { 429: $result .= $authheader; 430: } 431: return $result; 432: } 433: 434: ############################################################### 435: ############################################################### 436: sub upload_manager_javascript_forward_associate { 437: return(<<ENDPICK); 438: function verify(vf) { 439: var founduname=0; 440: var foundpwd=0; 441: var foundname=0; 442: var foundid=0; 443: var foundsec=0; 444: var foundemail=0; 445: var tw; 446: for (i=0;i<=vf.nfields.value;i++) { 447: tw=eval('vf.f'+i+'.selectedIndex'); 448: if (tw==1) { founduname=1; } 449: if ((tw>=2) && (tw<=6)) { foundname=1; } 450: if (tw==7) { foundid=1; } 451: if (tw==8) { foundsec=1; } 452: if (tw==9) { foundpwd=1; } 453: if (tw==10) { foundemail=1; } 454: } 455: verify_message(vf,founduname,foundpwd,foundname,foundid,foundsec,foundemail); 456: } 457: 458: // 459: // vf = this.form 460: // tf = column number 461: // 462: // values of nw 463: // 464: // 0 = none 465: // 1 = username 466: // 2 = names (lastname, firstnames) 467: // 3 = fname (firstname) 468: // 4 = mname (middlename) 469: // 5 = lname (lastname) 470: // 6 = gen (generation) 471: // 7 = id 472: // 8 = section 473: // 9 = ipwd (password) 474: // 10 = email address 475: 476: function flip(vf,tf) { 477: var nw=eval('vf.f'+tf+'.selectedIndex'); 478: var i; 479: // make sure no other columns are labeled the same as this one 480: for (i=0;i<=vf.nfields.value;i++) { 481: if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) { 482: eval('vf.f'+i+'.selectedIndex=0;') 483: } 484: } 485: // If we set this to 'lastname, firstnames', clear out all the ones 486: // set to 'fname','mname','lname','gen' (3,4,5,6) currently. 487: if (nw==2) { 488: for (i=0;i<=vf.nfields.value;i++) { 489: if ((eval('vf.f'+i+'.selectedIndex')>=3) && 490: (eval('vf.f'+i+'.selectedIndex')<=6)) { 491: eval('vf.f'+i+'.selectedIndex=0;') 492: } 493: } 494: } 495: // If we set this to one of 'fname','mname','lname','gen' (3,4,5,6), 496: // clear out any that are set to 'lastname, firstnames' (2) 497: if ((nw>=3) && (nw<=6)) { 498: for (i=0;i<=vf.nfields.value;i++) { 499: if (eval('vf.f'+i+'.selectedIndex')==2) { 500: eval('vf.f'+i+'.selectedIndex=0;') 501: } 502: } 503: } 504: // If we set the password, make the password form below correspond to 505: // the new value. 506: if (nw==9) { 507: changed_radio('int',document.studentform); 508: set_auth_radio_buttons('int',document.studentform); 509: vf.intarg.value=''; 510: vf.krbarg.value=''; 511: vf.locarg.value=''; 512: } 513: } 514: 515: function clearpwd(vf) { 516: var i; 517: for (i=0;i<=vf.nfields.value;i++) { 518: if (eval('vf.f'+i+'.selectedIndex')==9) { 519: eval('vf.f'+i+'.selectedIndex=0;') 520: } 521: } 522: } 523: 524: ENDPICK 525: } 526: 527: ############################################################### 528: ############################################################### 529: sub upload_manager_javascript_reverse_associate { 530: return(<<ENDPICK); 531: function verify(vf) { 532: var founduname=0; 533: var foundpwd=0; 534: var foundname=0; 535: var foundid=0; 536: var foundsec=0; 537: var tw; 538: for (i=0;i<=vf.nfields.value;i++) { 539: tw=eval('vf.f'+i+'.selectedIndex'); 540: if (i==0 && tw!=0) { founduname=1; } 541: if (((i>=1) && (i<=5)) && tw!=0 ) { foundname=1; } 542: if (i==6 && tw!=0) { foundid=1; } 543: if (i==7 && tw!=0) { foundsec=1; } 544: if (i==8 && tw!=0) { foundpwd=1; } 545: } 546: verify_message(vf,founduname,foundpwd,foundname,foundid,foundsec); 547: } 548: 549: function flip(vf,tf) { 550: var nw=eval('vf.f'+tf+'.selectedIndex'); 551: var i; 552: // picked the all one one name field, reset the other name ones to blank 553: if (tf==1 && nw!=0) { 554: for (i=2;i<=5;i++) { 555: eval('vf.f'+i+'.selectedIndex=0;') 556: } 557: } 558: //picked one of the piecewise name fields, reset the all in 559: //one field to blank 560: if ((tf>=2) && (tf<=5) && (nw!=0)) { 561: eval('vf.f1.selectedIndex=0;') 562: } 563: // intial password specified, pick internal authentication 564: if (tf==8 && nw!=0) { 565: changed_radio('int',document.studentform); 566: set_auth_radio_buttons('int',document.studentform); 567: vf.krbarg.value=''; 568: vf.intarg.value=''; 569: vf.locarg.value=''; 570: } 571: } 572: 573: function clearpwd(vf) { 574: var i; 575: if (eval('vf.f8.selectedIndex')!=0) { 576: eval('vf.f8.selectedIndex=0;') 577: } 578: } 579: ENDPICK 580: } 581: 582: ############################################################### 583: ############################################################### 584: sub print_upload_manager_footer { 585: my ($r,$i,$keyfields,$defdom,$today,$halfyear)=@_; 586: 587: my ($krbdef,$krbdefdom) = 588: &Apache::loncommon::get_kerberos_defaults($defdom); 589: my %param = ( formname => 'document.studentform', 590: kerb_def_dom => $krbdefdom, 591: kerb_def_auth => $krbdef 592: ); 593: if (exists($ENV{'form.ipwd_choice'}) && 594: defined($ENV{'form.ipwd_choice'}) && 595: $ENV{'form.ipwd_choice'} ne '') { 596: $param{'curr_authtype'} = 'int'; 597: } 598: my $krbform = &Apache::loncommon::authform_kerberos(%param); 599: my $intform = &Apache::loncommon::authform_internal(%param); 600: my $locform = &Apache::loncommon::authform_local(%param); 601: my $domform = &domain_form($defdom); 602: my $date_table = &date_setting_table(); 603: my $Str = "</table>\n"; 604: $Str .= &hidden_input('nfields',$i); 605: $Str .= &hidden_input('keyfields',$keyfields); 606: $Str .= '<h3>'.&mt('Login Type')."</h3>\n"; 607: $Str .= "<p>\n". 608: &mt('Note: this will not take effect if the user already exists'). 609: "</p><p>\n"; 610: $Str .= $krbform."\n</p><p>\n". 611: $intform."\n</p><p>\n". 612: $locform."\n</p>\n"; 613: $Str .= '<h3>'.&mt('LON-CAPA Domain for Students')."</h3>\n"; 614: $Str .= "<p>\n".&mt('LON-CAPA domain: [_1]',$domform)."\n</p>\n"; 615: $Str .= "<h3>".&mt('Starting and Ending Dates')."</h3>\n"; 616: $Str .= "<p>\n".$date_table."</p>\n"; 617: $Str .= "<h3>".&mt('Full Update')."</h3>\n"; 618: $Str .= '<input type="checkbox" name="fullup" value="yes">'. 619: ' '.&mt('Full update (also print list of users not enrolled anymore)'). 620: "</p>\n"; 621: $Str .= "<h3>".&mt('Student Number')."</h3>\n"; 622: $Str .= "<p>\n".'<input type="checkbox" name="forceid" value="yes">'; 623: $Str .= &mt('Disable ID/Student Number Safeguard and Force Change '. 624: 'of Conflicting IDs (only do if you know what you are doing)'). 625: "\n</p><p>\n"; 626: $Str .= '<input type="button" onClick="javascript:verify(this.form)" '. 627: 'value="Update Class List" />'."<br />\n"; 628: $Str .= &mt('Note: for large courses, this operation may be time '. 629: 'consuming'); 630: $r->print($Str); 631: return; 632: } 633: 634: ############################################################### 635: ############################################################### 636: sub print_upload_manager_form { 637: my $r=shift; 638: 639: my $firstLine; 640: my $datatoken; 641: if (!$ENV{'form.datatoken'}) { 642: $datatoken=&Apache::loncommon::upfile_store($r); 643: } else { 644: $datatoken=$ENV{'form.datatoken'}; 645: &Apache::loncommon::load_tmp_file($r); 646: } 647: my @records=&Apache::loncommon::upfile_record_sep(); 648: if($ENV{'form.noFirstLine'}){ 649: $firstLine=shift(@records); 650: } 651: my $total=$#records; 652: my $distotal=$total+1; 653: my $today=time; 654: my $halfyear=$today+15552000; 655: # 656: # Restore memorized settings 657: &Apache::loncommon::restore_course_settings 658: ('enrollment_upload',{ 'username_choice' => 'scalar', # column settings 659: 'names_choice' => 'scalar', 660: 'fname_choice' => 'scalar', 661: 'mname_choice' => 'scalar', 662: 'lname_choice' => 'scalar', 663: 'gen_choice' => 'scalar', 664: 'id_choice' => 'scalar', 665: 'sec_choice' => 'scalar', 666: 'ipwd_choice' => 'scalar', 667: 'email_choice' => 'scalar', 668: }); 669: # 670: # Determine kerberos parameters as appropriate 671: my $defdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 672: my ($krbdef,$krbdefdom) = 673: &Apache::loncommon::get_kerberos_defaults($defdom); 674: # 675: &print_upload_manager_header($r,$datatoken,$distotal,$krbdefdom); 676: my $i; 677: my $keyfields; 678: if ($total>=0) { 679: my @field= 680: (['username',&mt('Username'), $ENV{'form.username_choice'}], 681: ['names',&mt('Last Name, First Names'),$ENV{'form.names_choice'}], 682: ['fname',&mt('First Name'), $ENV{'form.fname_choice'}], 683: ['mname',&mt('Middle Names/Initials'),$ENV{'form.mname_choice'}], 684: ['lname',&mt('Last Name'), $ENV{'form.lname_choice'}], 685: ['gen', &mt('Generation'), $ENV{'form.gen_choice'}], 686: ['id', &mt('ID/Student Number'),$ENV{'form.id_choice'}], 687: ['sec', &mt('Group/Section'), $ENV{'form.sec_choice'}], 688: ['ipwd', &mt('Initial Password'),$ENV{'form.ipwd_choice'}], 689: ['email',&mt('EMail Address'), $ENV{'form.email_choice'}]); 690: if ($ENV{'form.upfile_associate'} eq 'reverse') { 691: &Apache::loncommon::csv_print_samples($r,\@records); 692: $i=&Apache::loncommon::csv_print_select_table($r,\@records, 693: \@field); 694: foreach (@field) { 695: $keyfields.=$_->[0].','; 696: } 697: chop($keyfields); 698: } else { 699: unshift(@field,['none','']); 700: $i=&Apache::loncommon::csv_samples_select_table($r,\@records, 701: \@field); 702: my %sone=&Apache::loncommon::record_sep($records[0]); 703: $keyfields=join(',',sort(keys(%sone))); 704: } 705: } 706: &print_upload_manager_footer($r,$i,$keyfields,$defdom,$today,$halfyear); 707: } 708: 709: ############################################################### 710: ############################################################### 711: sub enroll_single_student { 712: my $r=shift; 713: # Remove non alphanumeric values from section 714: $ENV{'form.csec'}=~s/\W//g; 715: # 716: # We do the dates first because the action of making them the defaul 717: # in the course is entirely separate from the action of enrolling the 718: # student. Also, a failure in setting the dates as default is not fatal 719: # to the process of enrolling / modifying a student. 720: my ($startdate,$enddate) = &get_dates_from_form(); 721: if ($ENV{'form.makedatesdefault'}) { 722: $r->print(&make_dates_default($startdate,$enddate)); 723: } 724: 725: $r->print('<h3>'.&mt('Enrolling Student').'</h3>'); 726: $r->print('<p>'.&mt('Enrolling').' '.$ENV{'form.cuname'}." \@ ". 727: $ENV{'form.lcdomain'}.'</p>'); 728: if (($ENV{'form.cuname'})&&($ENV{'form.cuname'}!~/\W/)&& 729: ($ENV{'form.lcdomain'})&&($ENV{'form.lcdomain'}!~/\W/)) { 730: # Deal with home server selection 731: my $domain=$ENV{'form.lcdomain'}; 732: my $desiredhost = $ENV{'form.lcserver'}; 733: if (lc($desiredhost) eq 'default') { 734: $desiredhost = undef; 735: } else { 736: my %home_servers =&Apache::loncommon::get_library_servers($domain); 737: if (! exists($home_servers{$desiredhost})) { 738: $r->print('<font color="#ff0000">'.&mt('Error').':</font>'. 739: &mt('Invalid home server specified')); 740: return; 741: } 742: } 743: $r->print(" ".&mt('with server')." $desiredhost :") if (defined($desiredhost)); 744: # End of home server selection logic 745: my $amode=''; 746: my $genpwd=''; 747: if ($ENV{'form.login'} eq 'krb') { 748: $amode='krb'; 749: $amode.=$ENV{'form.krbver'}; 750: $genpwd=$ENV{'form.krbarg'}; 751: } elsif ($ENV{'form.login'} eq 'int') { 752: $amode='internal'; 753: $genpwd=$ENV{'form.intarg'}; 754: } elsif ($ENV{'form.login'} eq 'loc') { 755: $amode='localauth'; 756: $genpwd=$ENV{'form.locarg'}; 757: if (!$genpwd) { $genpwd=" "; } 758: } 759: my $home = &Apache::lonnet::homeserver($ENV{'form.cuname'}, 760: $ENV{'form.lcdomain'}); 761: if ((($amode) && ($genpwd)) || ($home ne 'no_host')) { 762: # Clean out any old roles the student has in this class. 763: &modifystudent($ENV{'form.lcdomain'},$ENV{'form.cuname'}, 764: $ENV{'request.course.id'},$ENV{'form.csec'}, 765: $desiredhost); 766: my $login_result = &Apache::lonnet::modifystudent 767: ($ENV{'form.lcdomain'},$ENV{'form.cuname'}, 768: $ENV{'form.cstid'},$amode,$genpwd, 769: $ENV{'form.cfirst'},$ENV{'form.cmiddle'}, 770: $ENV{'form.clast'},$ENV{'form.cgen'}, 771: $ENV{'form.csec'},$enddate, 772: $startdate,$ENV{'form.forceid'}, 773: $desiredhost); 774: if ($login_result =~ /^ok/) { 775: $r->print($login_result); 776: $r->print("<p> ".&mt('If active, the new role will be available when the student next logs in to LON-CAPA.')."</p>"); 777: } else { 778: $r->print(&mt('unable to enroll').": ".$login_result); 779: } 780: } else { 781: $r->print('<p><font color="#ff0000">'.&mt('ERROR').'</font> '); 782: if ($amode =~ /^krb/) { 783: $r->print(&mt('Missing Kerberos domain information.').' '); 784: } else { 785: $r->print(&mt('Invalid login mode or password.').' '); 786: } 787: $r->print('<b>'.&mt('Unable to enroll').' '.$ENV{'form.cuname'}.'.</b></p>'); 788: } 789: } else { 790: $r->print(&mt('Invalid username or domain')); 791: } 792: } 793: 794: sub setup_date_selectors { 795: my ($starttime,$endtime,$mode) = @_; 796: if (! defined($starttime)) { 797: $starttime = time; 798: unless ($mode eq 'create_enrolldates' || $mode eq 'create_defaultdates') { 799: if (exists($ENV{'course.'.$ENV{'request.course.id'}. 800: '.default_enrollment_start_date'})) { 801: $starttime = $ENV{'course.'.$ENV{'request.course.id'}. 802: '.default_enrollment_start_date'}; 803: } 804: } 805: } 806: if (! defined($endtime)) { 807: $endtime = time+(6*30*24*60*60); # 6 months from now, approx 808: unless ($mode eq 'createcourse') { 809: if (exists($ENV{'course.'.$ENV{'request.course.id'}. 810: '.default_enrollment_end_date'})) { 811: $endtime = $ENV{'course.'.$ENV{'request.course.id'}. 812: '.default_enrollment_end_date'}; 813: } 814: } 815: } 816: my $startdateform = &Apache::lonhtmlcommon::date_setter('studentform', 817: 'startdate', 818: $starttime); 819: my $enddateform = &Apache::lonhtmlcommon::date_setter('studentform', 820: 'enddate', 821: $endtime); 822: if ($mode eq 'create_enrolldates') { 823: $startdateform = &Apache::lonhtmlcommon::date_setter('ccrs', 824: 'startenroll', 825: $starttime); 826: $enddateform = &Apache::lonhtmlcommon::date_setter('ccrs', 827: 'endenroll', 828: $endtime); 829: } 830: if ($mode eq 'create_defaultdates') { 831: $startdateform = &Apache::lonhtmlcommon::date_setter('ccrs', 832: 'startaccess', 833: $starttime); 834: $enddateform = &Apache::lonhtmlcommon::date_setter('ccrs', 835: 'endaccess', 836: $endtime); 837: } 838: return ($startdateform,$enddateform); 839: } 840: 841: sub get_dates_from_form { 842: my $startdate = &Apache::lonhtmlcommon::get_date_from_form('startdate'); 843: my $enddate = &Apache::lonhtmlcommon::get_date_from_form('enddate'); 844: if ($ENV{'form.no_end_date'}) { 845: $enddate = 0; 846: } 847: return ($startdate,$enddate); 848: } 849: 850: sub date_setting_table { 851: my ($starttime,$endtime,$mode) = @_; 852: my ($startform,$endform)=&setup_date_selectors($starttime,$endtime,$mode); 853: my $dateDefault = '<nobr>'. 854: '<input type="checkbox" name="makedatesdefault" /> '. 855: &mt('make these dates the default for future enrollment'); 856: if ($mode eq 'create_enrolldates' || $mode eq 'create_defaultdates') { 857: $dateDefault = ' '; 858: } 859: my $perpetual = '<nobr><input type="checkbox" name="no_end_date"'; 860: if (defined($endtime) && $endtime == 0) { 861: $perpetual .= ' checked'; 862: } 863: $perpetual.= ' /> '.&mt('no ending date').'</nobr>'; 864: if ($mode eq 'create_enrolldates') { 865: $perpetual = ' '; 866: } 867: my $result = ''; 868: $result .= "<table>\n"; 869: $result .= '<tr><td align="right">'.&mt('Starting Date').'</td>'. 870: '<td>'.$startform.'</td>'. 871: '<td>'.$dateDefault.'</td>'."</tr>\n"; 872: $result .= '<tr><td align="right">'.&mt('Ending Date').'</td>'. 873: '<td>'.$endform.'</td>'. 874: '<td>'.$perpetual.'</td>'."</tr>\n"; 875: $result .= "</table>\n"; 876: return $result; 877: } 878: 879: sub make_dates_default { 880: my ($startdate,$enddate) = @_; 881: my $result = ''; 882: my $dom = $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 883: my $crs = $ENV{'course.'.$ENV{'request.course.id'}.'.num'}; 884: my $put_result = &Apache::lonnet::put('environment', 885: {'default_enrollment_start_date'=>$startdate, 886: 'default_enrollment_end_date' =>$enddate},$dom,$crs); 887: if ($put_result eq 'ok') { 888: $result .= "Set default start and end dates for course<br />"; 889: # 890: # Refresh the course environment 891: &Apache::lonnet::coursedescription($ENV{'request.course.id'}); 892: } else { 893: $result .= &mt('Unable to set default dates for course').":".$put_result. 894: '<br />'; 895: } 896: return $result; 897: } 898: 899: ## 900: ## Single student enrollment routines (some of them) 901: ## 902: sub get_student_username_domain_form { 903: my $r = shift; 904: my $domform = &Apache::loncommon::select_dom_form 905: ($ENV{'course.'.$ENV{'request.course.id'}.'.domain'},'cudomain',0); 906: my %lt=&Apache::lonlocal::texthash( 907: 'eos' => "Enroll One Student", 908: 'usr' => "Username", 909: 'dom' => "Domain", 910: 'been' => "Begin Enrollment", 911: ); 912: $r->print(<<END); 913: <input type="hidden" name="action" value="enrollstudent" /> 914: <input type="hidden" name="state" value="gotusername" /> 915: <h3>$lt{'eos'}</h3> 916: <table> 917: <tr><th>$lt{'usr'}:</th> 918: <td><input type="text" name="cuname" size="15" /></td></tr> 919: <tr><th>$lt{'dom'}:</th> 920: <td>$domform</td></tr> 921: <tr><th> </th> 922: <td> 923: <input type="submit" name="Begin Enrollment" value="$lt{'been'}" /> 924: </td></tr> 925: </table> 926: <script type="text/javascript"> 927: // the if prevents the script error if the browser can not handle this 928: if ( document.studentform.cuname ) { document.studentform.cuname.focus(); } 929: </script> 930: END 931: return; 932: } 933: 934: sub print_enroll_single_student_form { 935: my $r=shift; 936: $r->print("<h3>".&mt('Enroll One Student')."</h3>"); 937: # 938: my $username = $ENV{'form.cuname'}; 939: my $domain = $ENV{'form.cudomain'}; 940: $username=~s/\W//gs; 941: $domain=~s/\W//gs; 942: my $home = &Apache::lonnet::homeserver($username,$domain); 943: # $new_user flags whether we are creating a new user or using an old one 944: my $new_user = 1; 945: if ($home ne 'no_host') { 946: $new_user = 0; 947: } 948: # 949: my $user_data_html = ''; 950: my $javascript_validations = ''; 951: if ($new_user) { 952: my $defdom=$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}; 953: # Set up authentication forms 954: my ($krbdef,$krbdefdom) = 955: &Apache::loncommon::get_kerberos_defaults($domain); 956: $javascript_validations=&javascript_validations('auth',$krbdefdom); 957: my %param = ( formname => 'document.studentform', 958: kerb_def_dom => $krbdefdom, 959: kerb_def_auth => $krbdef 960: ); 961: my $krbform = &Apache::loncommon::authform_kerberos(%param); 962: my $intform = &Apache::loncommon::authform_internal(%param); 963: my $locform = &Apache::loncommon::authform_local(%param); 964: # 965: # Set up domain selection form 966: my $homeserver_form = ''; 967: my %servers = &Apache::loncommon::get_library_servers($domain); 968: $homeserver_form = '<select name="lcserver" size="1">'."\n". 969: '<option value="default" selected>default</option>'."\n"; 970: while (my ($servername,$serverdescription) = each (%servers)) { 971: $homeserver_form .= '<option value="'.$servername.'">'. 972: $serverdescription."</option>\n"; 973: } 974: $homeserver_form .= "</select>\n"; 975: # 976: # 977: my %lt=&Apache::lonlocal::texthash( 978: 'udf' => "User Data for", 979: 'fn' => "First Name", 980: 'mn' => "Middle Name", 981: 'ln' => "Last Name", 982: 'gen' => "Generation", 983: 'hs' => "Home Server", 984: 'pswd' => "Password", 985: 'psam' => "Please select an authentication mechanism", 986: ); 987: $user_data_html = <<END; 988: <h3>$lt{'udf'} $username\@$domain</h3> 989: <table> 990: <tr><th>$lt{'fn'}:</th> 991: <td><input type="text" name="cfirst" size="15"></td></tr> 992: <tr><th>$lt{'mn'}:</th> 993: <td><input type="text" name="cmiddle" size="15"></td></tr> 994: <tr><th>$lt{'ln'}:</th> 995: <td><input type="text" name="clast" size="15"></td></tr> 996: <tr><th>$lt{'gen'}:</th> 997: <td><input type="text" name="cgen" size="5"> </td></tr> 998: <tr><th>$lt{'hs'}:</th> 999: <td>$homeserver_form</td></tr> 1000: </table> 1001: <h3>$lt{'pswd'}</h3> 1002: $lt{'psam'} 1003: <table> 1004: <p> 1005: $krbform 1006: <br /> 1007: $intform 1008: <br /> 1009: $locform 1010: </p> 1011: END 1012: } else { 1013: # User already exists. Do not worry about authentication 1014: my %uenv = &Apache::lonnet::dump('environment',$domain,$username); 1015: $javascript_validations = &javascript_validations('noauth'); 1016: my %lt=&Apache::lonlocal::texthash( 1017: 'udf' => "User Data for", 1018: 'fn' => "First Name", 1019: 'mn' => "Middle Name", 1020: 'ln' => "Last Name", 1021: 'gen' => "Generation", 1022: ); 1023: $user_data_html = <<END; 1024: <h3>$lt{'udf'} $username\@$domain</h3> 1025: <input type="hidden" name="lcserver" value="default" /> 1026: <table> 1027: <tr><th>$lt{'fn'}:</th> 1028: <td> 1029: <input type="text" name="cfirst" value="$uenv{'firstname'}" size="15" /> 1030: </td></tr> 1031: <tr><th>$lt{'mn'}:</th> 1032: <td> 1033: <input type="text" name="cmiddle" value="$uenv{'middlename'}" size="15" /> 1034: </td></tr> 1035: <tr><th>$lt{'ln'}:</th> 1036: <td> 1037: <input type="text" name="clast"value="$uenv{'lastname'}" size="15" /> 1038: </td></tr> 1039: <tr><th>$lt{'gen'}:</th> 1040: <td> 1041: <input type="text" name="cgen" value="$uenv{'generation'}" size="5" /> 1042: </td></tr> 1043: </table> 1044: END 1045: } 1046: my $date_table = &date_setting_table(); 1047: # Print it all out 1048: my %lt=&Apache::lonlocal::texthash( 1049: 'cd' => "Course Data", 1050: 'gs' => "Group/Section", 1051: 'idsn' => "ID/Student Number", 1052: 'disn' => "Disable ID/Student Number Safeguard and Force Change of Conflicting IDs (only do if you know what you are doing)", 1053: 'eas' => "Enroll as student", 1054: ); 1055: $r->print(<<END); 1056: <input type="hidden" name="action" value="enrollstudent" /> 1057: <input type="hidden" name="state" value="done" /> 1058: <input type="hidden" name="cuname" value="$username" /> 1059: <input type="hidden" name="lcdomain" value="$domain" /> 1060: <script type="text/javascript" language="Javascript"> 1061: function verify(vf) { 1062: var founduname=0; 1063: var foundpwd=0; 1064: var foundname=0; 1065: var foundid=0; 1066: var foundsec=0; 1067: var tw; 1068: if ((typeof(vf.cuname.value) !="undefined") && (vf.cuname.value!='') && 1069: (typeof(vf.lcdomain.value)!="undefined") && (vf.lcdomain.value!='')) { 1070: founduname=1; 1071: } 1072: if ((typeof(vf.cfirst.value)!="undefined") && (vf.cfirst.value!='') && 1073: (typeof(vf.clast.value) !="undefined") && (vf.clast.value!='')) { 1074: foundname=1; 1075: } 1076: if ((typeof(vf.csec.value)!="undefined") && (vf.csec.value!='')) { 1077: foundsec=1; 1078: } 1079: if ((typeof(vf.cstid.value)!="undefined") && (vf.cstid.value!='')) { 1080: foundid=1; 1081: } 1082: if (founduname==0) { 1083: alert('You need to specify at least the username and domain fields'); 1084: return; 1085: } 1086: verify_message(vf,founduname,foundpwd,foundname,foundid,foundsec); 1087: } 1088: 1089: $javascript_validations 1090: 1091: function clearpwd(vf) { 1092: //nothing else needs clearing 1093: } 1094: 1095: </script> 1096: 1097: $user_data_html 1098: 1099: <h3>$lt{'cd'}</h3> 1100: 1101: <p>$lt{'gs'}: <input type="text" name="csec" size="5" /> 1102: <p> 1103: $date_table 1104: </p> 1105: <h3>$lt{'idsn'}</h3> 1106: <p> 1107: $lt{'idsn'}: <input type="text" name="cstid" size="10"> 1108: </p><p> 1109: <input type="checkbox" name="forceid" value="yes"> 1110: $lt{'disn'} 1111: </p><p> 1112: <input type="button" onClick="verify(this.form)" value="$lt{'eas'}"> 1113: </p> 1114: END 1115: return; 1116: } 1117: 1118: # ========================================================= Menu Phase Two Drop 1119: sub print_drop_menu { 1120: my $r=shift; 1121: $r->print("<h3>".&mt('Drop Students')."</h3>"); 1122: my $cid=$ENV{'request.course.id'}; 1123: my ($classlist,$keylist) = &Apache::loncoursedata::get_classlist(); 1124: if (! defined($classlist)) { 1125: $r->print(&mt('There are no students currently enrolled.')."\n"); 1126: return; 1127: } 1128: # Print out the available choices 1129: &show_drop_list($r,$classlist,$keylist); 1130: return; 1131: } 1132: 1133: # ============================================== view classlist 1134: sub print_html_classlist { 1135: my ($r,$mode) = @_; 1136: if (! exists($ENV{'form.sortby'})) { 1137: $ENV{'form.sortby'} = 'username'; 1138: } 1139: if ($ENV{'form.Status'} !~ /^(Any|Expired|Active)$/) { 1140: $ENV{'form.Status'} = 'Active'; 1141: } 1142: my $status_select = &Apache::lonhtmlcommon::StatusOptions 1143: ($ENV{'form.Status'}); 1144: my $cid=$ENV{'request.course.id'}; 1145: my $cdom=$ENV{'course.'.$cid.'.domain'}; 1146: my $cnum=$ENV{'course.'.$cid.'.num'}; 1147: # 1148: # List course personnel 1149: my %coursepersonnel=&Apache::lonnet::get_course_adv_roles($cdom.'/'.$cnum); 1150: # 1151: if (! defined($ENV{'form.output'}) || 1152: $ENV{'form.output'} !~ /^(csv|excel|html)$/ ) { 1153: $ENV{'form.output'} = 'html'; 1154: } 1155: # 1156: $r->print('<br /><table border="2">'); 1157: foreach my $role (sort keys %coursepersonnel) { 1158: next if ($role =~ /^\s*$/); 1159: $r->print('<tr><td>'.$role.'</td><td>'); 1160: foreach my $user (split(',',$coursepersonnel{$role})) { 1161: my ($puname,$pudom)=split(':',$user); 1162: $r->print(' '.&Apache::loncommon::aboutmewrapper( 1163: &Apache::loncommon::plainname($puname, 1164: $pudom), 1165: $puname,$pudom)); 1166: } 1167: $r->print('</td></tr>'); 1168: } 1169: $r->print('</table>'); 1170: # 1171: # Interface output 1172: $r->print('<input type="hidden" name="action" value="'. 1173: $ENV{'form.action'}.'" />'); 1174: $r->print("<p>\n"); 1175: if ($ENV{'form.action'} ne 'modifystudent') { 1176: my %lt=&Apache::lonlocal::texthash('csv' => "CSV", 1177: 'excel' => "Excel", 1178: 'html' => 'HTML'); 1179: my $output_selector = '<select size="1" name="output" >'; 1180: foreach my $outputformat ('html','csv','excel') { 1181: my $option = '<option value="'.$outputformat.'" '; 1182: if ($outputformat eq $ENV{'form.output'}) { 1183: $option .= 'selected '; 1184: } 1185: $option .='>'.$lt{$outputformat}.'</option>'; 1186: $output_selector .= "\n".$option; 1187: } 1188: $output_selector .= '</select>'; 1189: $r->print(&mt('Output Format: [_1]',$output_selector).(' 'x3)); 1190: } 1191: $r->print(&mt('Student Status: [_1]',$status_select)."\n"); 1192: $r->print('<input type="submit" value="'.&mt('Update Display').'" />'. 1193: "\n</p>\n"); 1194: # 1195: # Print the classlist 1196: $r->print('<h2>'.&mt('Current Class List').'</h2>'); 1197: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist(); 1198: if (! defined($classlist)) { 1199: $r->print(&mt('There are no students currently enrolled.')."\n"); 1200: } else { 1201: # Print out the available choices 1202: if ($ENV{'form.action'} eq 'modifystudent') { 1203: &show_class_list($r,'view','modify', 1204: $ENV{'form.Status'},$classlist,$keylist); 1205: } else { 1206: &show_class_list($r,$ENV{'form.output'},'aboutme', 1207: $ENV{'form.Status'},$classlist,$keylist); 1208: } 1209: } 1210: } 1211: 1212: # =================================================== Show student list to drop 1213: sub show_class_list { 1214: my ($r,$mode,$linkto,$statusmode,$classlist,$keylist)=@_; 1215: my $cid=$ENV{'request.course.id'}; 1216: # 1217: # Variables for excel output 1218: my ($excel_workbook, $excel_sheet, $excel_filename,$row,$format); 1219: # 1220: # Variables for csv output 1221: my ($CSVfile,$CSVfilename); 1222: # 1223: my $sortby = $ENV{'form.sortby'}; 1224: if ($sortby !~ /^(username|domain|section|fullname|id|start|end|type)$/) { 1225: $sortby = 'username'; 1226: } 1227: # Print out header 1228: unless ($mode eq 'autoenroll') { 1229: $r->print(<<END); 1230: <input type="hidden" name="state" value="$ENV{'form.state'}" /> 1231: END 1232: } 1233: $r->print(<<END); 1234: <input type="hidden" name="sortby" value="$sortby" /> 1235: END 1236: if ($mode eq 'html' || $mode eq 'view' || $mode eq 'autoenroll') { 1237: if ($linkto eq 'aboutme') { 1238: $r->print(&mt('Select a user name to view the users personal page.')); 1239: } elsif ($linkto eq 'modify') { 1240: $r->print(&mt('Select a user name to modify the students information')); 1241: } 1242: my %lt=&Apache::lonlocal::texthash( 1243: 'usrn' => "username", 1244: 'dom' => "domain", 1245: 'sn' => "student name", 1246: 'sec' => "section", 1247: 'start' => "start date", 1248: 'end' => "end date", 1249: 'type' => "enroll type/action" 1250: ); 1251: unless ($mode eq 'autoenroll') { 1252: $r->print(<<END); 1253: <input type="hidden" name="sname" value="" /> 1254: <input type="hidden" name="sdom" value="" /> 1255: END 1256: } 1257: $r->print(" 1258: <p> 1259: <table border=2> 1260: <tr> 1261: "); 1262: if ($mode eq 'autoenroll') { 1263: $r->print(" 1264: <th><a href=\"javascript:document.studentform.sortby.value='type';document.studentform.submit();\">$lt{'type'}</a></th> 1265: "); 1266: } else { 1267: $r->print(" 1268: <th>Count</th> 1269: "); 1270: } 1271: $r->print(<<END); 1272: <th> 1273: <a href="javascript:document.studentform.sortby.value='username';document.studentform.submit();">$lt{'usrn'}</a> 1274: </th><th> 1275: <a href="javascript:document.studentform.sortby.value='domain';document.studentform.submit();">$lt{'dom'}</a> 1276: </th><th> 1277: <a href="javascript:document.studentform.sortby.value='id';document.studentform.submit();">ID</a> 1278: </th><th> 1279: <a href="javascript:document.studentform.sortby.value='fullname';document.studentform.submit();">$lt{'sn'}</a> 1280: </th><th> 1281: <a href="javascript:document.studentform.sortby.value='section';document.studentform.submit();">$lt{'sec'}</a> 1282: </th><th> 1283: <a href="javascript:document.studentform.sortby.value='start';document.studentform.submit();">$lt{'start'}</a> 1284: </th><th> 1285: <a href="javascript:document.studentform.sortby.value='end';document.studentform.submit();">$lt{'end'}</a> 1286: </th> 1287: </tr> 1288: END 1289: } elsif ($mode eq 'csv') { 1290: # 1291: # Open a file 1292: $CSVfilename = '/prtspool/'. 1293: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. 1294: time.'_'.rand(1000000000).'.csv'; 1295: unless ($CSVfile = Apache::File->new('>/home/httpd'.$CSVfilename)) { 1296: $r->log_error("Couldn't open $CSVfilename for output $!"); 1297: $r->print("Problems occured in writing the csv file. ". 1298: "This error has been logged. ". 1299: "Please alert your LON-CAPA administrator."); 1300: $CSVfile = undef; 1301: } 1302: # 1303: # Write headers and data to file 1304: if($statusmode eq 'Expired') { 1305: print $CSVfile '"'.&mt('Students with expired roles').'"'."\n"; 1306: } 1307: if ($statusmode eq 'Any') { 1308: print $CSVfile '"'.join('","',map { 1309: &Apache::loncommon::csv_translate(&mt($_)) 1310: } ("username","domain","ID","student name", 1311: "section","start date","end date","status")).'"'."\n"; 1312: } else { 1313: print $CSVfile '"'.join('","',map { 1314: &Apache::loncommon::csv_translate(&mt($_)) 1315: } ("username","domain","ID","student name", 1316: "section","start date","end date")).'"'."\n"; 1317: } 1318: } elsif ($mode eq 'excel') { 1319: # Create the excel spreadsheet 1320: $excel_filename = '/prtspool/'. 1321: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'. 1322: time.'_'.rand(1000000000).'.xls'; 1323: $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'. 1324: $excel_filename); 1325: $excel_workbook->set_tempdir('/home/httpd/perl/tmp'); 1326: # 1327: $format = &Apache::loncommon::define_excel_formats($excel_workbook); 1328: $excel_sheet = $excel_workbook->addworksheet('classlist'); 1329: # 1330: my $description = 'Class List for '. 1331: $ENV{'course.'.$ENV{'request.course.id'}.'.description'}; 1332: $excel_sheet->write($row++,0,$description,$format->{'h1'}); 1333: # 1334: $excel_sheet->write($row++,0,["username","domain","ID", 1335: "student name","section", 1336: "start date","end date","status"], 1337: $format->{'bold'}); 1338: } 1339: # 1340: # Sort the students 1341: my %index; 1342: my $i; 1343: foreach (@$keylist) { 1344: $index{$_} = $i++; 1345: } 1346: my $index = $index{$sortby}; 1347: my $second = $index{'username'}; 1348: my $third = $index{'domain'}; 1349: my @Sorted_Students = sort { 1350: lc($classlist->{$a}->[$index]) cmp lc($classlist->{$b}->[$index]) 1351: || 1352: lc($classlist->{$a}->[$second]) cmp lc($classlist->{$b}->[$second]) 1353: || 1354: lc($classlist->{$a}->[$third]) cmp lc($classlist->{$b}->[$third]) 1355: } (keys(%$classlist)); 1356: my $studentcount = 0; 1357: my $autocount = 0; 1358: my $manualcount = 0; 1359: my $unlockcount = 0; 1360: my $lockcount = 0; 1361: foreach my $student (@Sorted_Students) { 1362: my $sdata = $classlist->{$student}; 1363: my $username = $sdata->[$index{'username'}]; 1364: my $domain = $sdata->[$index{'domain'}]; 1365: my $section = $sdata->[$index{'section'}]; 1366: my $name = $sdata->[$index{'fullname'}]; 1367: my $id = $sdata->[$index{'id'}]; 1368: my $status = $sdata->[$index{'status'}]; 1369: my $start = $sdata->[$index{'start'}]; 1370: my $end = $sdata->[$index{'end'}]; 1371: my $type = $sdata->[$index{'type'}]; 1372: next if (($statusmode ne 'Any') && ($status ne $statusmode)); 1373: if ($mode eq 'view' || $mode eq 'html' || $mode eq 'autoenroll') { 1374: if (! defined($start) || $start == 0) { 1375: $start = &mt('none'); 1376: } else { 1377: $start = &Apache::lonlocal::locallocaltime($start); 1378: } 1379: if (! defined($end) || $end == 0) { 1380: $end = &mt('none'); 1381: } else { 1382: $end = &Apache::lonlocal::locallocaltime($end); 1383: } 1384: $r->print("<tr>\n "); 1385: if ($mode eq 'autoenroll') { 1386: my $lockedtype = $sdata->[$index{'lockedtype'}]; 1387: $studentcount++; 1388: my $cellentry; 1389: if ($type eq 'auto') { 1390: $cellentry = '<b>'.&mt('auto').'</b> <input type="checkbox" name="chgauto" value="'.$username.':'.$domain.'" /> Change'; 1391: $autocount ++; 1392: } else { 1393: $cellentry = '<table border="0" cellspacing="0"><tr><td rowspan="2"><b>'.&mt('manual').'</b></td><td><nobr><input type="checkbox" name="chgmanual" value="'.$username.':'.$domain.'" /> Change</nobr></td></tr><tr><td><nobr>'; 1394: $manualcount ++; 1395: if ($lockedtype) { 1396: $cellentry .= '<input type="checkbox" name="unlockchg" value="'.$username.':'.$domain.'" /> '.&mt('Unlock'); 1397: $unlockcount ++; 1398: } else { 1399: $cellentry .= '<input type="checkbox" name="lockchg" value="'.$username.':'.$domain.'" /> '.&mt('Lock'); 1400: $lockcount ++; 1401: } 1402: $cellentry .= '</nobr></td></tr></table>'; 1403: } 1404: $r->print("<td>$cellentry<td>\n "); 1405: } else { 1406: $r->print("<td>".(++$studentcount)."</td><td>\n "); 1407: } 1408: if ($linkto eq 'nothing') { 1409: $r->print($username); 1410: } elsif ($linkto eq 'aboutme') { 1411: $r->print(&Apache::loncommon::aboutmewrapper($username, 1412: $username, 1413: $domain)); 1414: } elsif ($linkto eq 'modify') { 1415: $r->print('<a href="'. 1416: "javascript:document.studentform.sname.value='". 1417: $username. 1418: "';document.studentform.sdom.value='".$domain. 1419: "';document.studentform.state.value='selected". 1420: "';document.studentform.submit();".'">'. 1421: $username."</a>\n"); 1422: } 1423: $r->print(<<"END"); 1424: </td> 1425: <td>$domain</td> 1426: <td>$id</td> 1427: <td>$name</td> 1428: <td>$section</td> 1429: <td>$start</td> 1430: <td>$end</td> 1431: </tr> 1432: END 1433: } elsif ($mode eq 'csv') { 1434: next if (! defined($CSVfile)); 1435: # no need to bother with $linkto 1436: if (! defined($start) || $start == 0) { 1437: $start = &mt('none'); 1438: } else { 1439: $start = &Apache::lonlocal::locallocaltime($start); 1440: } 1441: if (! defined($end) || $end == 0) { 1442: $end = &mt('none'); 1443: } else { 1444: $end = &Apache::lonlocal::locallocaltime($end); 1445: } 1446: my @line = (); 1447: foreach ($username,$domain,$id,$name,$section,$start,$end) { 1448: push @line,&Apache::loncommon::csv_translate($_); 1449: } 1450: if ($statusmode eq 'Any') { 1451: push @line,&Apache::loncommon::csv_translate($status); 1452: } 1453: print $CSVfile '"'.join('","',@line).'"'."\n"; 1454: } elsif ($mode eq 'excel') { 1455: $excel_sheet->write($row,0,[$username,$domain,$id, 1456: $name,$section]); 1457: my $col = 5; 1458: foreach my $time ($start,$end) { 1459: $excel_sheet->write($row,$col++, 1460: &Apache::lonstathelpers::calc_serial($time), 1461: $format->{'date'}); 1462: } 1463: $excel_sheet->write($row,$col++,$status); 1464: $row++; 1465: } 1466: } 1467: if ($mode eq 'view' || $mode eq 'html' || $mode eq 'autoenroll') { 1468: $r->print('</table><br>'); 1469: } elsif ($mode eq 'excel') { 1470: $excel_workbook->close(); 1471: $r->print('<p><a href="'.$excel_filename.'">'. 1472: &mt('Your Excel spreadsheet').'</a> '.&mt('is ready for download').'.</p>'."\n"); 1473: } elsif ($mode eq 'csv') { 1474: close($CSVfile); 1475: $r->print('<a href="'.$CSVfilename.'">'. 1476: &mt('Your CSV file').'</a> is ready for download.'. 1477: "\n"); 1478: $r->rflush(); 1479: } 1480: if ($mode eq 'autoenroll') { 1481: return ($studentcount,$autocount,$manualcount,$lockcount,$unlockcount); 1482: } 1483: return; 1484: } 1485: 1486: 1487: # 1488: # print out form for modification of a single students data 1489: # 1490: sub print_modify_student_form { 1491: my $r = shift(); 1492: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 1493: ['sdom','sname']); 1494: my $sname = $ENV{'form.sname'}; 1495: my $sdom = $ENV{'form.sdom'}; 1496: my $sortby = $ENV{'form.sortby'}; 1497: # determine the students name information 1498: my %info=&Apache::lonnet::get('environment', 1499: ['firstname','middlename', 1500: 'lastname','generation','id'], 1501: $sdom, $sname); 1502: my ($tmp) = keys(%info); 1503: if ($tmp =~ /^(con_lost|error|no_such_host)/i) { 1504: $r->print('<font color="#ff0000" size="+2">'.&mt('Error').'</font>'. 1505: '<p>'. 1506: &mt('Unable to retrieve environment data for').' '.$sname. 1507: &mt('in domain').' '.$sdom.'</p><p>'. 1508: &mt('Please contact your LON-CAPA administrator regarding this situation.').'</p></body></html>'); 1509: return; 1510: } 1511: # determine the students starting and ending times and section 1512: my ($starttime,$endtime,$section) = &get_enrollment_data($sname,$sdom); 1513: if ($starttime =~ /^error/) { 1514: $r->print('<h2>'&mt('Error').'</h2>'); 1515: $r->print('<p>'.$starttime.'</p>'); 1516: return; 1517: } 1518: # 1519: # Deal with date forms 1520: my $current_date_description = ''; 1521: my $textdate = ''; 1522: 1523: if (! defined($starttime) || $starttime == 0) { 1524: $current_date_description = &mt('Current Starting Date: not set'). 1525: '<br />'; 1526: } else { 1527: $current_date_description = 1528: &mt('Current Starting Date: [_1]', 1529: &Apache::lonlocal::locallocaltime($starttime)).'<br />'; 1530: } 1531: if (! defined($endtime) || $endtime == 0) { 1532: $current_date_description.= &mt('Current Ending Date: not set'). 1533: '<br />'; 1534: } else { 1535: $current_date_description.= 1536: &mt('Current Ending Date: [_1]', 1537: &Apache::lonlocal::locallocaltime($endtime)).'<br />'; 1538: 1539: } 1540: my $date_table = &date_setting_table($starttime,$endtime); 1541: # 1542: if (! exists($ENV{'form.Status'}) || 1543: $ENV{'form.Status'} !~ /^(Any|Expired|Active)$/) { 1544: $ENV{'form.Status'} = 'crap'; 1545: } 1546: # Make sure student is enrolled in course 1547: my %lt=&Apache::lonlocal::texthash( 1548: 'mef' => "Modify Enrollment for", 1549: 'odcc' => "Only domain coordinators can change a users password.", 1550: 'sn' => "Student Name", 1551: 'fn' => "First", 1552: 'mn' => "Middle", 1553: 'ln' => "Last", 1554: 'gen' => "Generation", 1555: 'sid' => "Student ID", 1556: 'disn' => "Disable ID/Student Number Safeguard and Force Change of Conflicting IDs (only do if you know what you are doing)", 1557: 'sec' => "Section", 1558: 'sm' => "Submit Modifications", 1559: ); 1560: $r->print(<<END); 1561: <p> 1562: <font size="+1"> 1563: $lt{'odcc'} 1564: </font> 1565: </p> 1566: <input type="hidden" name="slogin" value="$sname" /> 1567: <input type="hidden" name="sdomain" value="$sdom" /> 1568: <input type="hidden" name="action" value="modifystudent" /> 1569: <input type="hidden" name="state" value="done" /> 1570: <input type="hidden" name="sortby" value="$sortby" /> 1571: <input type="hidden" name="Status" value="$ENV{'form.Status'}" /> 1572: <h2>$lt{'mef'} $info{'firstname'} $info{'middlename'} 1573: $info{'lastname'} $info{'generation'}, $sname\@$sdom</h2> 1574: <p> 1575: <b>$lt{'sn'}</b> 1576: <table> 1577: <tr><th>$lt{'fn'}</th><th>$lt{'mn'}</th><th>$lt{'ln'}</th><th>$lt{'gen'}</th></tr> 1578: <tr><td> 1579: <input type="text" name="firstname" value="$info{'firstname'}" /></td><td> 1580: <input type="text" name="middlename" value="$info{'middlename'}" /></td><td> 1581: <input type="text" name="lastname" value="$info{'lastname'}" /></td><td> 1582: <input type="text" name="generation" value="$info{'generation'}" /></td></tr> 1583: </table> 1584: </p><p> 1585: <b>$lt{'sid'}</b>: <input type="text" name="id" value="$info{'id'}" size="12"/> 1586: </p><p> 1587: <input type="checkbox" name="forceid" > 1588: $lt{'disn'} 1589: </p><p> 1590: <b>$lt{'sec'}</b>: <input type="text" name="section" value="$section" size="14"/> 1591: </p> 1592: <p>$current_date_description</p> 1593: <p>$date_table</p> 1594: <input type="submit" value="$lt{'sm'}" /> 1595: </body></html> 1596: END 1597: return; 1598: } 1599: 1600: # 1601: # modify a single students section 1602: # 1603: sub modify_single_student { 1604: my $r = shift; 1605: # 1606: # Remove non alphanumeric values from the section 1607: $ENV{'form.section'} =~ s/\W//g; 1608: # 1609: # Do the date defaults first 1610: my ($starttime,$endtime) = &get_dates_from_form(); 1611: if ($ENV{'form.makedatesdefault'}) { 1612: $r->print(&make_dates_default($starttime,$endtime)); 1613: } 1614: # Get the 'sortby' and 'Status' variables so the user goes back to their 1615: # previous screen 1616: my $sortby = $ENV{'form.sortby'}; 1617: my $status = $ENV{'form.Status'}; 1618: # 1619: # We always need this information 1620: my $slogin = $ENV{'form.slogin'}; 1621: my $sdom = $ENV{'form.sdomain'}; 1622: # 1623: # Get the old data 1624: my %old=&Apache::lonnet::get('environment', 1625: ['firstname','middlename', 1626: 'lastname','generation','id'], 1627: $sdom, $slogin); 1628: $old{'section'} = &Apache::lonnet::getsection($sdom,$slogin, 1629: $ENV{'request.course.id'}); 1630: my ($tmp) = keys(%old); 1631: if ($tmp =~ /^(con_lost|error|no_such_host)/i) { 1632: $r->print(&mt('There was an error determining the environment values for')." $slogin \@ $sdom."); 1633: return; 1634: } 1635: undef $tmp; 1636: # 1637: # Get the new data 1638: my $firstname = $ENV{'form.firstname'}; 1639: my $middlename = $ENV{'form.middlename'}; 1640: my $lastname = $ENV{'form.lastname'}; 1641: my $generation = $ENV{'form.generation'}; 1642: my $section = $ENV{'form.section'}; 1643: my $courseid = $ENV{'request.course.id'}; 1644: my $sid = $ENV{'form.id'}; 1645: my $displayable_starttime = localtime($starttime); 1646: my $displayable_endtime = localtime($endtime); 1647: # 1648: # check for forceid override 1649: if ((defined($old{'id'})) && ($old{'id'} ne '') && 1650: ($sid ne $old{'id'}) && (! exists($ENV{'form.forceid'}))) { 1651: $r->print("<font color=\"ff0000\">".&mt('You changed the students id but did not disable the ID change safeguard. The students id will not be changed.')."</font>"); 1652: $sid = $old{'id'}; 1653: } 1654: # 1655: # talk to the user about what we are going to do 1656: my %lt=&Apache::lonlocal::texthash( 1657: 'mdu' => "Modifying data for user", 1658: 'si' => "Student Information", 1659: 'fd' => "Field", 1660: 'ov' => "Old Value", 1661: 'nv' => "New Value", 1662: 'fn' => "First name", 1663: 'mn' => "Middle name", 1664: 'ln' => "Last name", 1665: 'gen' => "Generation", 1666: 'sec' => "Section", 1667: 'ri' => "Role Information", 1668: 'st' => "Start Time", 1669: 'et' => "End Time", 1670: ); 1671: $r->print(<<END); 1672: <h2>$lt{'mdu'} $slogin \@ $sdom </h2> 1673: <h3>$lt{'si'}</h3> 1674: <table rules="rows" border="1" cellpadding="3" > 1675: <tr> 1676: <th> $lt{'fd'} </th> 1677: <th> $lt{'ov'} </th> 1678: <th> $lt{'nv'} </th> 1679: </tr> 1680: <tr> 1681: <td> <b>$lt{'fn'}</b> </td> 1682: <td> $old{'firstname'} </td> 1683: <td> $firstname </td> 1684: </tr><tr> 1685: <td> <b>$lt{'mn'}</b> </td> 1686: <td> $old{'middlename'} </td> 1687: <td> $middlename </td> 1688: </tr><tr> 1689: <td> <b>$lt{'ln'}</b> </td> 1690: <td> $old{'lastname'} </td> 1691: <td> $lastname </td> 1692: </tr><tr> 1693: <td> <b>$lt{'gen'}</b> </td> 1694: <td> $old{'generation'} </td> 1695: <td> $generation </td> 1696: </tr><tr> 1697: <td> <b>ID</b> </td> 1698: <td> $old{'id'} </td> 1699: <td> $sid </td> 1700: </tr><tr> 1701: <td> <b>$lt{'sec'}</b> </td> 1702: <td> $old{'section'} </td> 1703: <td> $section</td> 1704: </tr> 1705: </table> 1706: <h3>$lt{'ri'}</h3> 1707: <table> 1708: <tr><td align="right"><b>$lt{'st'}:</b></td><td> $displayable_starttime </td></tr> 1709: <tr><td align="right"><b>$lt{'et'}:</b></td><td> $displayable_endtime </td></tr> 1710: </table> 1711: <p> 1712: END 1713: # 1714: # Send request(s) to modify data (final undef is for 'desiredhost', 1715: # which is a moot point because the student already has an account. 1716: my $modify_section_results = &modifystudent($sdom,$slogin, 1717: $ENV{'request.course.id'}, 1718: $section,undef); 1719: if ($modify_section_results !~ /^ok/) { 1720: $r->print(&mt('An error occured during the attempt to change the section for this student.')."<br />"); 1721: } 1722: my $roleresults = &Apache::lonnet::modifystudent 1723: ($sdom,$slogin,$sid,undef,undef,$firstname,$middlename,$lastname, 1724: $generation,$section,$endtime,$starttime,$ENV{'form.forceid'}); 1725: if ($roleresults eq 'refused' ) { 1726: $r->print(&mt('Your request to change the role information for this student was refused. You do not appear to have sufficient authority to change student information.')); 1727: } elsif ($roleresults !~ /ok/) { 1728: $r->print(&mt('An error occurred during the attempt to change the role information for this student.')." <br />". 1729: &mt('The error reported was')." ". 1730: $roleresults); 1731: &Apache::lonnet::logthis("londropadd:failed attempt to modify student". 1732: " data for ".$slogin." \@ ".$sdom." by ". 1733: $ENV{'user.name'}." \@ ".$ENV{'user.domain'}. 1734: ":".$roleresults); 1735: } else { # everything is okay! 1736: $r->print(&mt('Student information updated successfully.')." <br />". 1737: &mt('The student must log out and log in again to see these changes.')); 1738: } 1739: my $Masd=&mt('Modify another students data'); 1740: $r->print(<<END); 1741: </p><p> 1742: <input type="hidden" name="action" value="modifystudent" /> 1743: <input type="hidden" name="sortby" value="$sortby" /> 1744: <input type="hidden" name="Status" value="$status" /> 1745: <a href="javascript:document.studentform.submit();">$Masd</a> 1746: </body></html> 1747: END 1748: return; 1749: } 1750: 1751: sub get_enrollment_data { 1752: my ($sname,$sdomain) = @_; 1753: my $courseid = $ENV{'request.course.id'}; 1754: $courseid =~ s:_:/:g; 1755: my %roles = &Apache::lonnet::dump('roles',$sdomain,$sname); 1756: my ($tmp) = keys(%roles); 1757: # Bail out if we were unable to get the students roles 1758: return ('error'.$tmp) if ($tmp =~ /^(con_lost|error|no_such_host)/i); 1759: # Go through the roles looking for enrollment in this course 1760: my ($end,$start) = (undef,undef); 1761: my $section = ''; 1762: my $count = scalar(keys(%roles)); 1763: while (my ($course,$role) = each(%roles)) { 1764: if ($course=~ /^\/$courseid\/*\s*(\w+)*_st$/ ) { 1765: # 1766: # Get active role 1767: $section=$1; 1768: (undef,$end,$start)=split(/\_/,$role); 1769: my $now=time; 1770: my $notactive=0; 1771: if ($start) { 1772: if ($now<$start) { $notactive=1; } 1773: } 1774: if ($end) { 1775: if ($now>$end) { $notactive=1; } 1776: } 1777: unless ($notactive) { return ($start,$end,$section); } 1778: } 1779: } 1780: return ($start,$end,$section); 1781: } 1782: 1783: ################################################# 1784: ################################################# 1785: 1786: =pod 1787: 1788: =item show_drop_list 1789: 1790: Display a list of students to drop 1791: Inputs: 1792: 1793: =over 4 1794: 1795: =item $r, Apache request 1796: 1797: =item $classlist, hash pointer returned from loncoursedata::get_classlist(); 1798: 1799: =item $keylist, array pointer returned from loncoursedata::get_classlist() 1800: which describes the order elements are stored in the %$classlist values. 1801: 1802: =item $nosort, if true, sorting links are omitted. 1803: 1804: =back 1805: 1806: =cut 1807: 1808: ################################################# 1809: ################################################# 1810: sub show_drop_list { 1811: my ($r,$classlist,$keylist,$nosort)=@_; 1812: my $cid=$ENV{'request.course.id'}; 1813: if (! exists($ENV{'form.sortby'})) { 1814: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 1815: ['sortby']); 1816: } 1817: my $sortby = $ENV{'form.sortby'}; 1818: if ($sortby !~ /^(username|domain|section|fullname|id|start|end)$/) { 1819: $sortby = 'username'; 1820: } 1821: # 1822: my $action = "drop"; 1823: $r->print(<<END); 1824: <input type="hidden" name="sortby" value="$sortby" /> 1825: <input type="hidden" name="action" value="$action" /> 1826: <input type="hidden" name="state" value="done" /> 1827: <script> 1828: function checkAll(field) { 1829: for (i = 0; i < field.length; i++) 1830: field[i].checked = true ; 1831: } 1832: 1833: function uncheckAll(field) { 1834: for (i = 0; i < field.length; i++) 1835: field[i].checked = false ; 1836: } 1837: </script> 1838: <p> 1839: <input type="hidden" name="phase" value="four"> 1840: END 1841: 1842: my %lt=&Apache::lonlocal::texthash('usrn' => "username", 1843: 'dom' => "domain", 1844: 'sn' => "student name", 1845: 'sec' => "section", 1846: 'start' => "start date", 1847: 'end' => "end date", 1848: ); 1849: if ($nosort) { 1850: $r->print(<<END); 1851: <table border=2> 1852: <tr> 1853: <th> </th> 1854: <th>$lt{'usrn'}</th> 1855: <th>$lt{'dom'}</th> 1856: <th>ID</th> 1857: <th>$lt{'sn'}</th> 1858: <th>$lt{'sec'}</th> 1859: <th>$lt{'start'}</th> 1860: <th>$lt{'end'}</th> 1861: </tr> 1862: END 1863: 1864: } else { 1865: $r->print(<<END); 1866: <table border=2> 1867: <tr><th> </th> 1868: <th> 1869: <a href="/adm/dropadd?action=$action&sortby=username">$lt{'usrn'}</a> 1870: </th><th> 1871: <a href="/adm/dropadd?action=$action&sortby=domain">$lt{'dom'}</a> 1872: </th><th> 1873: <a href="/adm/dropadd?action=$action&sortby=id">ID</a> 1874: </th><th> 1875: <a href="/adm/dropadd?action=$action&sortby=fullname">$lt{'sn'}</a> 1876: </th><th> 1877: <a href="/adm/dropadd?action=$action&sortby=section">$lt{'sec'}</a> 1878: </th><th> 1879: <a href="/adm/dropadd?action=$action&sortby=start">$lt{'start'}</a> 1880: </th><th> 1881: <a href="/adm/dropadd?action=$action&sortby=end">$lt{'end'}</a> 1882: </th> 1883: </tr> 1884: END 1885: } 1886: # 1887: # Sort the students 1888: my %index; 1889: my $i; 1890: foreach (@$keylist) { 1891: $index{$_} = $i++; 1892: } 1893: my $index = $index{$sortby}; 1894: my $second = $index{'username'}; 1895: my $third = $index{'domain'}; 1896: my @Sorted_Students = sort { 1897: lc($classlist->{$a}->[$index]) cmp lc($classlist->{$b}->[$index]) 1898: || 1899: lc($classlist->{$a}->[$second]) cmp lc($classlist->{$b}->[$second]) 1900: || 1901: lc($classlist->{$a}->[$third]) cmp lc($classlist->{$b}->[$third]) 1902: } (keys(%$classlist)); 1903: foreach my $student (@Sorted_Students) { 1904: my $error; 1905: my $sdata = $classlist->{$student}; 1906: my $username = $sdata->[$index{'username'}]; 1907: my $domain = $sdata->[$index{'domain'}]; 1908: my $section = $sdata->[$index{'section'}]; 1909: my $name = $sdata->[$index{'fullname'}]; 1910: my $id = $sdata->[$index{'id'}]; 1911: my $start = $sdata->[$index{'start'}]; 1912: my $end = $sdata->[$index{'end'}]; 1913: if (! defined($start) || $start == 0) { 1914: $start = &mt('none'); 1915: } else { 1916: $start = &Apache::lonlocal::locallocaltime($start); 1917: } 1918: if (! defined($end) || $end == 0) { 1919: $end = &mt('none'); 1920: } else { 1921: $end = &Apache::lonlocal::locallocaltime($end); 1922: } 1923: my $status = $sdata->[$index{'status'}]; 1924: next if ($status ne 'Active'); 1925: # 1926: $r->print(<<"END"); 1927: <tr> 1928: <td><input type="checkbox" name="droplist" value="$student"></td> 1929: <td>$username</td> 1930: <td>$domain</td> 1931: <td>$id</td> 1932: <td>$name</td> 1933: <td>$section</td> 1934: <td>$start</td> 1935: <td>$end</td> 1936: </tr> 1937: END 1938: } 1939: $r->print('</table><br>'); 1940: %lt=&Apache::lonlocal::texthash( 1941: 'dp' => "Drop Students", 1942: 'ca' => "check all", 1943: 'ua' => "uncheck all", 1944: ); 1945: $r->print(<<"END"); 1946: </p><p> 1947: <input type="button" value="$lt{'ca'}" onclick="javascript:checkAll(document.studentform.droplist)"> 1948: <input type="button" value="$lt{'ua'}" onclick="javascript:uncheckAll(document.studentform.droplist)"> 1949: <p><input type=submit value="$lt{'dp'}"></p> 1950: END 1951: return; 1952: } 1953: 1954: # 1955: # Print out the initial form to get the courselist file 1956: # 1957: sub print_first_courselist_upload_form { 1958: my $r=shift; 1959: my $str; 1960: $str = '<input type="hidden" name="phase" value="two">'; 1961: $str .= '<input type="hidden" name="action" value="upload" />'; 1962: $str .= '<input type="hidden" name="state" value="got_file" />'; 1963: $str .= "<h3>".&mt('Upload a class list')."</h3>\n"; 1964: $str .= &Apache::loncommon::upfile_select_html(); 1965: $str .= "<p>\n"; 1966: $str .= '<input type="submit" name="fileupload" value="'. 1967: &mt('Upload class list').'">'."\n"; 1968: $str .= '<input type="checkbox" name="noFirstLine" /> '. 1969: &mt('Ignore First Line')."</p>\n"; 1970: $str .= &Apache::loncommon::help_open_topic("Course_Create_Class_List", 1971: &mt("How do I create a class list from a spreadsheet")). 1972: "<br />\n"; 1973: $str .= &Apache::loncommon::help_open_topic("Course_Convert_To_CSV", 1974: &mt("How do I create a CSV file from a spreadsheet")). 1975: "<br />\n"; 1976: $str .= "</body>\n</html>\n"; 1977: $r->print($str); 1978: return; 1979: } 1980: 1981: # ================================================= Drop/Add from uploaded file 1982: sub upfile_drop_add { 1983: my $r=shift; 1984: &Apache::loncommon::load_tmp_file($r); 1985: my @studentdata=&Apache::loncommon::upfile_record_sep(); 1986: if($ENV{'form.noFirstLine'}){shift(@studentdata);} 1987: my @keyfields = split(/\,/,$ENV{'form.keyfields'}); 1988: my $cid = $ENV{'request.course.id'}; 1989: my %fields=(); 1990: for (my $i=0; $i<=$ENV{'form.nfields'}; $i++) { 1991: if ($ENV{'form.upfile_associate'} eq 'reverse') { 1992: if ($ENV{'form.f'.$i} ne 'none') { 1993: $fields{$keyfields[$i]}=$ENV{'form.f'.$i}; 1994: } 1995: } else { 1996: $fields{$ENV{'form.f'.$i}}=$keyfields[$i]; 1997: } 1998: } 1999: # 2000: # Store the field choices away 2001: foreach my $field (qw/username names 2002: fname mname lname gen id sec ipwd email/) { 2003: $ENV{'form.'.$field.'_choice'}=$fields{$field}; 2004: } 2005: &Apache::loncommon::store_course_settings('enrollment_upload', 2006: { 'username_choice' => 'scalar', 2007: 'names_choice' => 'scalar', 2008: 'fname_choice' => 'scalar', 2009: 'mname_choice' => 'scalar', 2010: 'lname_choice' => 'scalar', 2011: 'gen_choice' => 'scalar', 2012: 'id_choice' => 'scalar', 2013: 'sec_choice' => 'scalar', 2014: 'ipwd_choice' => 'scalar', 2015: 'email_choice' => 'scalar' }); 2016: 2017: # 2018: my ($startdate,$enddate) = &get_dates_from_form(); 2019: if ($ENV{'form.makedatesdefault'}) { 2020: $r->print(&make_dates_default($startdate,$enddate)); 2021: } 2022: # Determine domain and desired host (home server) 2023: my $domain=$ENV{'form.lcdomain'}; 2024: my $desiredhost = $ENV{'form.lcserver'}; 2025: if (lc($desiredhost) eq 'default') { 2026: $desiredhost = undef; 2027: } else { 2028: my %home_servers = &Apache::loncommon::get_library_servers($domain); 2029: if (! exists($home_servers{$desiredhost})) { 2030: $r->print('<font color="#ff0000">'.&mt('Error').'</font>'. 2031: &mt('Invalid home server specified')); 2032: $r->print("</body>\n</html>\n"); 2033: return; 2034: } 2035: } 2036: # Determine authentication mechanism 2037: my $amode = ''; 2038: my $genpwd = ''; 2039: if ($ENV{'form.login'} eq 'krb') { 2040: $amode='krb'; 2041: $amode.=$ENV{'form.krbver'}; 2042: $genpwd=$ENV{'form.krbarg'}; 2043: } elsif ($ENV{'form.login'} eq 'int') { 2044: $amode='internal'; 2045: if ((defined($ENV{'form.intarg'})) && ($ENV{'form.intarg'})) { 2046: $genpwd=$ENV{'form.intarg'}; 2047: } 2048: } elsif ($ENV{'form.login'} eq 'loc') { 2049: $amode='localauth'; 2050: if ((defined($ENV{'form.locarg'})) && ($ENV{'form.locarg'})) { 2051: $genpwd=$ENV{'form.locarg'}; 2052: } 2053: } 2054: if ($amode =~ /^krb/) { 2055: if (! defined($genpwd) || $genpwd eq '') { 2056: $r->print('<font color="red" size="+1">'. 2057: &mt('Unable to enroll students').'</font> '. 2058: &mt('No Kerberos domain was specified.').'</p>'); 2059: $amode = ''; # This causes the loop below to be skipped 2060: } 2061: } 2062: unless (($domain=~/\W/) || ($amode eq '')) { 2063: ####################################### 2064: ## Enroll Students ## 2065: ####################################### 2066: $r->print('<h3>'.&mt('Enrolling Students')."</h3>\n<p>\n"); 2067: my $count=0; 2068: my $flushc=0; 2069: my %student=(); 2070: # Get new classlist 2071: foreach (@studentdata) { 2072: my %entries=&Apache::loncommon::record_sep($_); 2073: # Determine student name 2074: unless (($entries{$fields{'username'}} eq '') || 2075: (!defined($entries{$fields{'username'}}))) { 2076: my ($fname, $mname, $lname,$gen) = ('','','',''); 2077: if (defined($fields{'names'})) { 2078: ($lname,$fname,$mname)=($entries{$fields{'names'}}=~ 2079: /([^\,]+)\,\s*(\w+)\s*(.*)$/); 2080: } else { 2081: if (defined($fields{'fname'})) { 2082: $fname=$entries{$fields{'fname'}}; 2083: } 2084: if (defined($fields{'mname'})) { 2085: $mname=$entries{$fields{'mname'}}; 2086: } 2087: if (defined($fields{'lname'})) { 2088: $lname=$entries{$fields{'lname'}}; 2089: } 2090: if (defined($fields{'gen'})) { 2091: $gen=$entries{$fields{'gen'}}; 2092: } 2093: } 2094: if ($entries{$fields{'username'}}=~/\W/) { 2095: $r->print('<br />'. 2096: &mt('<b>[_1]</b>: Unacceptable username for user [_2] [_3] [_4] [_5]', 2097: $entries{$fields{'username'}},$fname,$mname,$lname,$gen). 2098: '</b>'); 2099: } else { 2100: # determine section number 2101: my $sec=''; 2102: my $username=$entries{$fields{'username'}}; 2103: if (defined($fields{'sec'})) { 2104: if (defined($entries{$fields{'sec'}})) { 2105: $sec=$entries{$fields{'sec'}}; 2106: } 2107: } 2108: # remove non alphanumeric values from section 2109: $sec =~ s/\W//g; 2110: # determine student id number 2111: my $id=''; 2112: if (defined($fields{'id'})) { 2113: if (defined($entries{$fields{'id'}})) { 2114: $id=$entries{$fields{'id'}}; 2115: } 2116: $id=~tr/A-Z/a-z/; 2117: } 2118: # determine email address 2119: my $email=''; 2120: if (defined($fields{'email'})) { 2121: if (defined($entries{$fields{'email'}})) { 2122: $email=$entries{$fields{'email'}}; 2123: unless ($email=~/^[^\@]+\@[^\@]+$/) { $email=''; } 2124: } 2125: } 2126: # determine student password 2127: my $password=''; 2128: if ($genpwd) { 2129: $password=$genpwd; 2130: } else { 2131: if (defined($fields{'ipwd'})) { 2132: if ($entries{$fields{'ipwd'}}) { 2133: $password=$entries{$fields{'ipwd'}}; 2134: } 2135: } 2136: } 2137: # Clean up whitespace 2138: foreach (\$domain,\$username,\$id,\$fname,\$mname, 2139: \$lname,\$gen,\$sec) { 2140: $$_ =~ s/(\s+$|^\s+)//g; 2141: } 2142: if ($password || $ENV{'form.login'} eq 'loc') { 2143: &modifystudent($domain,$username,$cid,$sec, 2144: $desiredhost); 2145: my $reply=&Apache::lonnet::modifystudent 2146: ($domain,$username,$id,$amode,$password, 2147: $fname,$mname,$lname,$gen,$sec,$enddate, 2148: $startdate,$ENV{'form.forceid'},$desiredhost, 2149: $email); 2150: if ($reply ne 'ok') { 2151: $reply =~ s/^error://; 2152: $r->print('<br />'. 2153: &mt('<b>[_1]</b>: Unable to enroll: [_2]',$username,$reply)); 2154: } else { 2155: $count++; $flushc++; 2156: $student{$username}=1; 2157: $r->print('. '); 2158: if ($flushc>15) { 2159: $r->rflush; 2160: $flushc=0; 2161: } 2162: } 2163: } else { 2164: $r->print('<br />'. 2165: &mt('<b>[_1]</b>: Unable to enroll. No password specified.',$username) 2166: ); 2167: } 2168: } 2169: } 2170: } # end of foreach (@studentdata) 2171: $r->print("</p>\n<p>\n".&mt('Processed [_1] student(s).',$count). 2172: "</p>\n"); 2173: $r->print("<p>\n". 2174: &mt('If active, the new role will be available when the '. 2175: 'students next log in to LON-CAPA.')."</p>\n"); 2176: ##################################### 2177: # Drop students # 2178: ##################################### 2179: if ($ENV{'form.fullup'} eq 'yes') { 2180: $r->print('<h3>'.&mt('Dropping Students')."</h3>\n"); 2181: # Get current classlist 2182: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist(); 2183: if (! defined($classlist)) { 2184: $r->print(&mt('There are no students currently enrolled.'). 2185: "\n"); 2186: } else { 2187: # Remove the students we just added from the list of students. 2188: foreach (@studentdata) { 2189: my %entries=&Apache::loncommon::record_sep($_); 2190: unless (($entries{$fields{'username'}} eq '') || 2191: (!defined($entries{$fields{'username'}}))) { 2192: delete($classlist->{$entries{$fields{'username'}}. 2193: ':'.$domain}); 2194: } 2195: } 2196: # Print out list of dropped students. 2197: &show_drop_list($r,$classlist,$keylist,'nosort'); 2198: } 2199: } 2200: } # end of unless 2201: } 2202: 2203: # ================================================================== Phase four 2204: sub drop_student_list { 2205: my $r=shift; 2206: my $count=0; 2207: my @droplist; 2208: if (ref($ENV{'form.droplist'})) { 2209: @droplist = @{$ENV{'form.droplist'}}; 2210: } else { 2211: @droplist = ($ENV{'form.droplist'}); 2212: } 2213: foreach (@droplist) { 2214: my ($uname,$udom)=split(/\:/,$_); 2215: # drop student 2216: my $result = &modifystudent($udom,$uname,$ENV{'request.course.id'}); 2217: if ($result eq 'ok' || $result eq 'ok:') { 2218: $r->print(&mt('Dropped [_1]',$uname.'@'.$udom).'<br>'); 2219: $count++; 2220: } else { 2221: $r->print( 2222: &mt('Error dropping [_1]:[_2]',$uname.'@'.$udom,$result). 2223: '<br />'); 2224: } 2225: } 2226: $r->print('<p><b>'.&mt('Dropped [_1] student(s).',$count).'</b></p>'); 2227: $r->print('<p>'.&mt('Re-enrollment will re-activate data.')) if ($count); 2228: } 2229: 2230: ################################################################### 2231: ################################################################### 2232: 2233: =pod 2234: 2235: =item &handler 2236: 2237: The typical handler you see in all these modules. Takes $r, the 2238: http request, as an argument. 2239: 2240: The response to the request is governed by two form variables 2241: 2242: form.action form.state response 2243: --------------------------------------------------- 2244: undefined undefined print main menu 2245: upload undefined print courselist upload menu 2246: upload got_file deal with uploaded file, 2247: print the upload managing menu 2248: upload enrolling enroll students based on upload 2249: drop undefined print the classlist ready to drop 2250: drop done drop the selected students 2251: enrollstudent undefined print student username domain form 2252: enrollstudent gotusername print single student enroll menu 2253: enrollstudent enrolling enroll student 2254: classlist undefined print html classlist 2255: classlist csv print csv classlist 2256: modifystudent undefined print classlist to select student to modify 2257: modifystudent selected print modify student menu 2258: modifystudent done make modifications to student record 2259: 2260: =cut 2261: 2262: ################################################################### 2263: ################################################################### 2264: sub handler { 2265: my $r=shift; 2266: if ($r->header_only) { 2267: &Apache::loncommon::content_type($r,'text/html'); 2268: $r->send_http_header; 2269: return OK; 2270: } 2271: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 2272: ['action','state']); 2273: 2274: &Apache::lonhtmlcommon::clear_breadcrumbs(); 2275: &Apache::lonhtmlcommon::add_breadcrumb 2276: ({href=>"/adm/dropadd", 2277: text=>"Enrollment Manager", 2278: faq=>9,bug=>'Instructor Interface',}); 2279: # Needs to be in a course 2280: if (! ($ENV{'request.course.fn'})) { 2281: # Not in a course 2282: $ENV{'user.error.msg'}= 2283: "/adm/dropadd:cst:0:0:Cannot drop or add students"; 2284: return HTTP_NOT_ACCEPTABLE; 2285: } 2286: # 2287: my $view_permission = 2288: &Apache::lonnet::allowed('vcl',$ENV{'request.course.id'}); 2289: my $enrl_permission = 2290: &Apache::lonnet::allowed('cst',$ENV{'request.course.id'}); 2291: if (! $view_permission && ! $enrl_permission) { 2292: $ENV{'user.error.msg'}= 2293: "/adm/dropadd:cst:0:0:Cannot drop or add students"; 2294: return HTTP_NOT_ACCEPTABLE; 2295: } 2296: # 2297: # Only output the header information if they did not request csv format 2298: # 2299: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, 2300: ['state','action']); 2301: # Start page 2302: &Apache::loncommon::content_type($r,'text/html'); 2303: $r->send_http_header; 2304: $r->print(&header()); 2305: # 2306: # Main switch on form.action and form.state, as appropriate 2307: if (! exists($ENV{'form.action'})) { 2308: $r->print(&Apache::lonhtmlcommon::breadcrumbs 2309: (undef,'Enrollment Manager')); 2310: &print_main_menu($r,$enrl_permission,$view_permission); 2311: } elsif ($ENV{'form.action'} eq 'upload' && $enrl_permission) { 2312: &Apache::lonhtmlcommon::add_breadcrumb 2313: ({href=>'/adm/dropadd?action=upload&state=', 2314: text=>"Upload Classlist"}); 2315: $r->print(&Apache::lonhtmlcommon::breadcrumbs 2316: (undef,'Upload Classlist','Course_Create_Class_List')); 2317: if (! exists($ENV{'form.state'})) { 2318: &print_first_courselist_upload_form($r); 2319: } elsif ($ENV{'form.state'} eq 'got_file') { 2320: &print_upload_manager_form($r); 2321: } elsif ($ENV{'form.state'} eq 'enrolling') { 2322: if ($ENV{'form.datatoken'}) { 2323: &upfile_drop_add($r); 2324: } else { 2325: # Hmmm, this is an error 2326: } 2327: } else { 2328: &print_first_courselist_upload_form($r); 2329: } 2330: } elsif ($ENV{'form.action'} eq 'drop' && $enrl_permission) { 2331: &Apache::lonhtmlcommon::add_breadcrumb 2332: ({href=>'/adm/dropadd?action=drop', 2333: text=>"Drop Students"}); 2334: $r->print(&Apache::lonhtmlcommon::breadcrumbs 2335: (undef,'Drop Students','Course_Drop_Student')); 2336: if (! exists($ENV{'form.state'})) { 2337: &print_drop_menu($r); 2338: } elsif ($ENV{'form.state'} eq 'done') { 2339: &drop_student_list($r); 2340: } else { 2341: &print_drop_menu($r); 2342: } 2343: } elsif ($ENV{'form.action'} eq 'enrollstudent' && $enrl_permission) { 2344: &Apache::lonhtmlcommon::add_breadcrumb 2345: ({href=>'/adm/dropadd?action=enrollstudent', 2346: text=>"Enroll Student"}); 2347: $r->print(&Apache::lonhtmlcommon::breadcrumbs 2348: (undef,'Enroll Student','Course_Add_Student')); 2349: if (! exists($ENV{'form.state'})) { 2350: &get_student_username_domain_form($r); 2351: } elsif ($ENV{'form.state'} eq 'gotusername') { 2352: &print_enroll_single_student_form($r); 2353: } elsif ($ENV{'form.state'} eq 'enrolling') { 2354: &enroll_single_student($r); 2355: } else { 2356: &get_student_username_domain_form($r); 2357: } 2358: } elsif ($ENV{'form.action'} eq 'classlist' && $view_permission) { 2359: &Apache::lonhtmlcommon::add_breadcrumb 2360: ({href=>'/adm/dropadd?action=classlist', 2361: text=>"View Classlist"}); 2362: $r->print(&Apache::lonhtmlcommon::breadcrumbs 2363: (undef,'View Classlist','Course_View_Class_List')); 2364: if (! exists($ENV{'form.state'})) { 2365: &print_html_classlist($r,undef); 2366: } elsif ($ENV{'form.state'} eq 'csv') { 2367: &print_html_classlist($r,'csv'); 2368: } elsif ($ENV{'form.state'} eq 'excel') { 2369: &print_html_classlist($r,'excel'); 2370: } else { 2371: &print_html_classlist($r,undef); 2372: } 2373: } elsif ($ENV{'form.action'} eq 'modifystudent' && $enrl_permission) { 2374: &Apache::lonhtmlcommon::add_breadcrumb 2375: ({href=>'/adm/dropadd?action=modifystudent', 2376: text=>"Modify Student Data"}); 2377: $r->print(&Apache::lonhtmlcommon::breadcrumbs 2378: (undef,'Modify Student Data','Course_Modify_Student_Data')); 2379: if (! exists($ENV{'form.state'})) { 2380: &print_html_classlist($r); 2381: } elsif ($ENV{'form.state'} eq 'selected') { 2382: &print_modify_student_form($r); 2383: } elsif ($ENV{'form.state'} eq 'done') { 2384: &modify_single_student($r); 2385: } else { 2386: &print_html_classlist($r); 2387: } 2388: } else { 2389: # We should not end up here, but I guess it is possible 2390: &Apache::lonnet::logthis("Undetermined state in londropadd.pm. ". 2391: "form.action = ".$ENV{'form.action'}. 2392: "Someone should fix this."); 2393: $r->print(&Apache::lonhtmlcommon::breadcrumbs 2394: (undef,'Enrollment Manager')); 2395: &print_main_menu($r,$enrl_permission,$view_permission); 2396: } 2397: # 2398: # Finish up 2399: $r->print('</form></body></html>'); 2400: return OK; 2401: } 2402: 2403: ################################################################### 2404: ################################################################### 2405: 2406: 1; 2407: __END__ 2408: 2409: