# The LearningOnline Network with CAPA # Handler to drop and add students in courses # # (Handler to set parameters for assessments # # (Handler to resolve ambiguous file locations # # (TeX Content Handler # # 05/29/00,05/30,10/11 Gerd Kortemeyer) # # 10/11,10/12,10/16 Gerd Kortemeyer) # # 11/20,11/21,11/22,11/23,11/24,11/25,11/27,11/28, # 12/08,12/12 Gerd Kortemeyer) # # 12/26,12/27,12/28, # 01/01/01,01/15,02/10,02/13,02/14 Gerd Kortemeyer package Apache::londropadd; use strict; use Apache::lonnet; use Apache::Constants qw(:common :http REDIRECT); # ================================================================ Print header sub header { my $r=shift; $r->print(< LON-CAPA Student Drop/Add

Drop/Add Students

Course: $ENV{'course.'.$ENV{'request.course.id'}.'.description'}

ENDHEAD } # ========================================================= Store uploaded file # needs $ENV{'form.upfile'} # return $datatoken to be put into hidden field sub upfile_store { my $r=shift; $ENV{'form.upfile'}=~s/\r/\n/gs; $ENV{'form.upfile'}=~s/\f/\n/gs; $ENV{'form.upfile'}=~s/\n+/\n/gs; $ENV{'form.upfile'}=~s/\n+$//gs; my $datatoken=$ENV{'user.name'}.'_'.$ENV{'user.domain'}. '_enroll_'.$ENV{'request.course.id'}.'_'.time.'_'.$$; { my $fh=Apache::File->new('>'.$r->dir_config('lonDaemons'). '/tmp/'.$datatoken.'.tmp'); print $fh $ENV{'form.upfile'}; } return $datatoken; } # ================================================= Load uploaded file from tmp # needs $ENV{'form.datatoken'} # sets $ENV{'form.upfile'} sub load_tmp_file { my $r=shift; my @studentdata=(); { my $fh; if ($fh=Apache::File->new($r->dir_config('lonDaemons'). '/tmp/'.$ENV{'form.datatoken'}.'.tmp')) { @studentdata=<$fh>; } } $ENV{'form.upfile'}=join('',@studentdata); } # ========================================= Separate uploaded file into records # returns array of records sub upfile_record_sep { if ($ENV{'form.upfiletype'} eq 'xml') { } else { return split(/\n/,$ENV{'form.upfile'}); } } # =============================================== Separate a record into fields sub record_sep { my $record=shift; my %components=(); if ($ENV{'form.upfiletype'} eq 'xml') { } elsif ($ENV{'form.upfiletype'} eq 'space') { my $i=0; map { my $field=$_; $field=~s/^(\"|\')//; $field=~s/(\"|\')$//; $components{$i}=$field; $i++; } split(/\s+/,$record); } elsif ($ENV{'form.upfiletype'} eq 'tab') { my $i=0; map { my $field=$_; $field=~s/^(\"|\')//; $field=~s/(\"|\')$//; $components{$i}=$field; $i++; } split(/\t+/,$record); } else { my @allfields=split(/\,/,$record); my $i=0; my $j; for ($j=0;$j<=$#allfields;$j++) { my $field=$allfields[$j]; if ($field=~/^\s*(\"|\')/) { my $delimiter=$1; while (($field!~/$delimiter$/) && ($j<$#allfields)) { $j++; $field.=','.$allfields[$j]; } $field=~s/^\s*$delimiter//; $field=~s/$delimiter\s*$//; } $components{$i}=$field; $i++; } } return %components; } # =========== Drop student from all sections of a course, except optional $csec sub dropstudent { my ($udom,$unam,$courseid,$csec)=@_; $courseid=~s/\_/\//g; $courseid=~s/^(\w)/\/$1/; map { my ($key,$value)=split(/\=/,$_); $key=&Apache::lonnet::unescape($key); if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) { my $section=$1; if ($key eq $courseid.'_st') { $section=''; } if ($section ne $csec) { my ($dummy,$end,$start)=split(/\_/, &Apache::lonnet::unescape($value)); my $now=time; my $notactive=0; if ($start) { if ($now<$start) { $notactive=1; } } if ($end) { if ($now>$end) { $notactive=1; } } unless ($notactive) { my $reply=&Apache::lonnet::modifystudent( $udom,$unam,'','','', '','','','',$section,time); } } } } split(/\&/,&Apache::lonnet::reply('dump:'.$udom.':'.$unam.':roles', &Apache::lonnet::homeserver($unam,$udom))); } # ============================================================== Menu Phase One sub menu_phase_one { my $r=shift; $r->print(<

Upload a courselist


Type:


Enroll a single student


Drop students

ENDUPFORM } # ======================================================= Menu Phase Two Upload sub menu_phase_two_upload { my $r=shift; my $datatoken=&upfile_store($r); my @records=&upfile_record_sep(); my $total=$#records; my $distotal=$total+1; $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/; my $krbdefdom=$1; $krbdefdom=~tr/a-z/A-Z/; my $today=time; my $halfyear=$today+15552000; my $defdom=$r->dir_config('lonDefDomain'); $r->print(<


Identify fields

Total number of records found in file: $distotal ENDPICK my %sone; my %stwo; my %sthree; my $i=0; if ($total>=0) { %sone=&record_sep($records[0]); if ($total>=1) { %stwo=&record_sep($records[1]); } if ($total>=2) { %sthree=&record_sep($records[2]); } map { $r->print(''); $i++; } sort keys %sone; $i--; } my $keyfields=join(',',sort keys %sone); $r->print(<

Login Type

Note: this will not take effect if the user already exists

Kerberos authenticated with domain

Internally authenticated (with initial password )

LON-CAPA Domain for Students

LON-CAPA domain:

Starting and Ending Dates

Set Starting Date

Set Ending Date

Full Update

Full update (also print list of users not enrolled anymore)


Note: for large courses, this operation might be time consuming. ENDPICK } # ======================================================= Enroll single student sub enroll_single_student { my $r=shift; $r->print('

Enrolling Student

'); if (($ENV{'form.cuname'})&&($ENV{'form.cuname'}!~/\W/)&& ($ENV{'form.cdomain'})&&($ENV{'form.cdomain'}!~/\W/)) { my $amode=''; my $genpwd=''; if ($ENV{'form.login'} eq 'krb') { $amode='krb4'; $genpwd=$ENV{'form.krbdom'}; } elsif ($ENV{'form.login'} eq 'int') { $amode='internal'; $genpwd=$ENV{'form.intpwd'}; } if (($amode) && ($genpwd)) { &dropstudent($ENV{'form.cdomain'},$ENV{'form.cuname'}, $ENV{'request.course.id'},$ENV{'form.csec'}); $r->print(&Apache::lonnet::modifystudent( $ENV{'form.cdomain'},$ENV{'form.cuname'}, $ENV{'form.cstid'},$amode,$genpwd, $ENV{'form.cfirst'},$ENV{'form.cmiddle'}, $ENV{'form.clast'},$ENV{'form.cgen'}, $ENV{'form.csec'},$ENV{'form.enddate'}, $ENV{'form.startdate'})); } else { $r->print('Invalid login mode or password'); } } else { $r->print('Invalid username or domain'); } } # ======================================================= Menu Phase Two Enroll sub menu_phase_two_enroll { my $r=shift; $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/; my $krbdefdom=$1; $krbdefdom=~tr/a-z/A-Z/; my $today=time; my $halfyear=$today+15552000; my $defdom=$r->dir_config('lonDefDomain'); $r->print(< function verify(vf) { var founduname=0; var foundpwd=0; var foundname=0; var foundid=0; var foundsec=0; var foundatype=0; var tw; var message=''; if ((vf.cuname.value!=undefined) && (vf.cuname.value!='') && (vf.cdomain.value!=undefined) && (vf.cdomain.value!='')) { founduname=1; } if ((vf.cfirst.value!=undefined) && (vf.cfirst.value!='') && (vf.clast.value!=undefined) && (vf.clast.value!='')) { foundname=1; } if ((vf.csec.value!=undefined) && (vf.csec.value!='')) { foundsec=1; } if ((vf.cstid.value!=undefined) && (vf.cstid.value!='')) { foundid=1; } if (founduname==0) { alert('You need to specify at least the username and domain fields'); return; } if (vf.login[0].checked) { foundatype=1; if (vf.krbdom.value=='') { alert('You need to specify the Kerberos domain'); return; } } if (vf.login[1].checked) { foundatype=1; if ((vf.intpwd.value=='') && (foundpwd==0)) { alert('You need to specify the initial password'); return; } } if (foundatype==0) { alert('You need to set the login type'); return; } if (foundname==0) { message='No first and last name specified. '; } if (foundid==0) { message+='No ID or student number field specified. '; } if (foundsec==0) { message+='No section or group field specified. '; } if (vf.startdate.value=='') { message+='No starting date set. '; } if (vf.enddate.value=='') { message+='No ending date set. '; } if ((vf.enddate.value!='') && (vf.startdate.value!='')) { if (Math.round(vf.enddate.value)

Personal Data

First Name:
Middle Name:
Last Name:
Generation:

ID/Student Number:

Group/Section:

Login Data

Username:

Domain:

Note: login settings below will not take effect if the user already exists

Kerberos authenticated with domain

Internally authenticated (with initial password )

Starting and Ending Dates

Set Starting Date

Set Ending Date


ENDSENROLL } # ========================================================= Menu Phase Two Drop sub menu_phase_two_drop { my $r=shift; my $cid=$ENV{'request.course.id'}; my $classlst=&Apache::lonnet::reply ('dump:'.$ENV{'course.'.$cid.'.domain'}.':'. $ENV{'course.'.$cid.'.num'}.':classlist', $ENV{'course.'.$cid.'.home'}); my %currentlist=(); my $now=time; unless ($classlst=~/^error\:/) { map { my ($name,$value)=split(/\=/,$_); my ($end,$start)=split(/\:/, &Apache::lonnet::unescape($value)); my $active=1; if (($end) && ($now>$end)) { $active=0; } if ($active) { $currentlist{&Apache::lonnet::unescape($name)}=1; } } split(/\&/,$classlst); # ----------------------------------------------------------- Print out choices &show_drop_list($r,%currentlist); } else { $r->print( '

Could not access classlist: '.$classlst. '

'); } } # =================================================== Show student list to drop sub show_drop_list { my ($r,%currentlist)=@_; my $cid=$ENV{'request.course.id'}; $r->print(''); $r->print('
FieldSamples
'); if (defined($sone{$i})) { $r->print($sone{$i}."
\n"); } if (defined($stwo{$i})) { $r->print($stwo{$i}."
\n"); } if (defined($sthree{$i})) { $r->print($sthree{$i}."
\n"); } $r->print('
'); map { my ($sname,$sdom)=split(/\:/,$_); my %reply=&Apache::lonnet::idrget($sdom,$sname); my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid); my @reply=split(/[\&\=]/,&Apache::lonnet::reply( 'get:'.$sdom.':'.$sname. ':environment:firstname&middlename&lastname&generation', &Apache::lonnet::homeserver($sname,$sdom))); $r->print( '\n"); } sort keys %currentlist; $r->print('
'. $sname.''.$sdom.''. $reply{$sname}.''. &Apache::lonnet::unescape($reply[2]).' '. &Apache::lonnet::unescape($reply[3]).', '. &Apache::lonnet::unescape($reply[0]).' '. &Apache::lonnet::unescape($reply[1]). ''. $ssec."

'); $r->print(''); } # ================================================= Drop/Add from uploaded file sub upfile_drop_add { my $r=shift; &load_tmp_file($r); my @studentdata=&upfile_record_sep(); my @keyfields=split(/\,/,$ENV{'form.keyfields'}); my $cid=$ENV{'request.course.id'}; my %fields=(); for (my $i=0;$i<=$ENV{'form.nfields'};$i++) { $fields{$ENV{'form.f'.$i}}=$keyfields[$i]; } my $startdate=$ENV{'form.startdate'}; my $enddate=$ENV{'form.enddate'}; if ($startdate=~/\D/) { $startdate=''; } if ($enddate=~/\D/) { $enddate=''; } my $domain=$ENV{'form.lcdomain'}; my $amode=''; my $genpwd=''; if ($ENV{'form.login'} eq 'krb') { $amode='krb4'; $genpwd=$ENV{'form.krbdom'}; } elsif ($ENV{'form.login'} eq 'int') { $amode='internal'; if ((defined($ENV{'form.intpwd'})) && ($ENV{'form.intpwd'})) { $genpwd=$ENV{'form.intpwd'}; } } unless (($domain=~/\W/) || ($amode eq '')) { $r->print('

Enrolling Students

'); my $count=0; my $flushc=0; my %student=(); # ----------------------------------------------------------- Get new classlist # --------------------------------------------------------- Enroll new students map { my %entries=&record_sep($_); unless (($entries{$fields{'username'}} eq '') || (!defined($entries{$fields{'username'}}))) { my $fname=''; my $mname=''; my $lname=''; my $gen=''; if (defined($fields{'names'})) { ($lname,$fname,$mname)= ($entries{$fields{'names'}}=~/([^\,]+)\,\s*(\w+)\s*(.*)$/); } else { if (defined($fields{'fname'})) { $fname=$entries{$fields{'fname'}}; } if (defined($fields{'mname'})) { $mname=$entries{$fields{'mname'}}; } if (defined($fields{'lname'})) { $lname=$entries{$fields{'lname'}}; } if (defined($fields{'gen'})) { $gen=$entries{$fields{'gen'}}; } } if ($entries{$fields{'username'}}=~/\W/) { $r->print('

Unacceptable username: '. $entries{$fields{'username'}}.' for user '. $fname.' '.$mname.' '.$lname.' '.$gen.'

'); } else { my $sec=''; my $username=$entries{$fields{'username'}}; if (defined($fields{'sec'})) { if (defined($entries{$fields{'sec'}})) { $sec=$entries{$fields{'sec'}}; } } my $id=''; if (defined($fields{'id'})) { if (defined($entries{$fields{'id'}})) { $id=$entries{$fields{'id'}}; } $id=~tr/A-Z/a-z/; } my $password=''; if ($genpwd) { $password=$genpwd; } else { if (defined($fields{'ipwd'})) { if ($entries{$fields{'ipwd'}}) { $password=$entries{$fields{'ipwd'}}; } } } if ($password) { &dropstudent($domain,$username,$cid,$sec); my $reply=&Apache::lonnet::modifystudent( $domain,$username,$id,$amode,$password, $fname,$mname,$lname,$gen,$sec,$enddate,$startdate); unless ($reply eq 'ok') { $r->print( "

Error enrolling $username: $reply

"); } else { $count++; $flushc++; $student{$username}=1; $r->print('. '); if ($flushc>15) { $r->rflush; $flushc=0; } } } else { $r->print( "

No password for $username

"); } } } } @studentdata; $r->print('

Processed Students: '.$count); # --------------------------------------------------------------- Drop students if ($ENV{'form.fullup'} eq 'yes') { $r->print('

Dropping Students

'); # ------------------------------------------------------- Get current classlist my $classlst=&Apache::lonnet::reply ('dump:'.$ENV{'course.'.$cid.'.domain'}.':'. $ENV{'course.'.$cid.'.num'}.':classlist', $ENV{'course.'.$cid.'.home'}); my %currentlist=(); my $now=time; unless ($classlst=~/^error\:/) { map { my ($name,$value)=split(/\=/,$_); my ($end,$start)=split(/\:/, &Apache::lonnet::unescape($value)); my $active=1; if (($end) && ($now>$end)) { $active=0; } if ($active) { $currentlist{&Apache::lonnet::unescape($name)}=1; } } split(/\&/,$classlst); # ------------------------------------------------ Now got up-to-date classlist map { my %entries=&record_sep($_); unless (($entries{$fields{'username'}} eq '') || (!defined($entries{$fields{'username'}}))) { delete($currentlist{ $entries{$fields{'username'}}.':'. $domain}); } } @studentdata; # ----------------------------------------------------------- Print out choices &show_drop_list($r,%currentlist); } else { $r->print( '

Could not access classlist: '.$classlst. '

'); } } # ------------------------------------------------------------------------ Done } } # ================================================================== Phase four sub drop_student_list { my $r=shift; my $count=0; map { if ($_=~/^form\.drop\:/) { my ($dummy,$uname,$udom)=split(/\:/,$_); &dropstudent($udom,$uname,$ENV{'request.course.id'}); $r->print('Dropped '.$uname.' at '.$udom.'
'); $count++; } } keys %ENV; $r->print('

Dropped '.$count.' student(s).'); $r->print('

Re-enrollment will re-activate data.'); } # ================================================================ Main Handler sub handler { my $r=shift; if ($r->header_only) { $r->content_type('text/html'); $r->send_http_header; return OK; } # ----------------------------------------------------- Needs to be in a course if (($ENV{'request.course.fn'}) && (&Apache::lonnet::allowed('cst',$ENV{'request.course.id'}))) { # ------------------------------------------------------------------ Start page $r->content_type('text/html'); $r->send_http_header; &header($r); # --------------------------------------------------- Phase one, initial screen unless ($ENV{'form.phase'}) { &menu_phase_one($r); } # ------------------------------------------------------------------- Phase two if ($ENV{'form.phase'} eq 'two') { if ($ENV{'form.fileupload'}) { &menu_phase_two_upload($r); } elsif ($ENV{'form.enroll'}) { &menu_phase_two_enroll($r); } elsif ($ENV{'form.drop'}) { &menu_phase_two_drop($r); } } # ----------------------------------------------------------------- Phase three if ($ENV{'form.phase'} eq 'three') { if ($ENV{'form.datatoken'}) { &upfile_drop_add($r); } } # ------------------------------------------------------------------ Phase four if ($ENV{'form.phase'} eq 'four') { &drop_student_list($r); } # ------------------------------------------------------------------ Phase four if ($ENV{'form.phase'} eq 'five') { &enroll_single_student($r); } # ------------------------------------------------------------------------- End $r->print('

'); } else { # ----------------------------- Not in a course, or not allowed to modify parms $ENV{'user.error.msg'}= "/adm/dropadd:cst:0:0:Cannot drop or add students"; return HTTP_NOT_ACCEPTABLE; } return OK; } 1; __END__