# The LearningOnline Network with CAPA
# Utility functions for managing LON-CAPA user accounts
#
# $Id: lonuserutils.pm,v 1.12 2007/12/06 04:03:36 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
#
###############################################################
###############################################################
package Apache::lonuserutils;
use strict;
use Apache::lonnet;
use Apache::loncommon();
use Apache::lonhtmlcommon;
use Apache::lonlocal;
use Apache::longroup;
use LONCAPA qw(:DEFAULT :match);
###############################################################
###############################################################
# Drop student from all sections of a course, except optional $csec
sub modifystudent {
my ($udom,$unam,$courseid,$csec,$desiredhost)=@_;
# if $csec is undefined, drop the student from all the courses matching
# this one. If $csec is defined, drop them from all other sections of
# this course and add them to section $csec
my $cdom = $env{'course.'.$courseid.'.domain'};
my $cnum = $env{'course.'.$courseid.'.num'};
my %roles = &Apache::lonnet::dump('roles',$udom,$unam);
my ($tmp) = keys(%roles);
# Bail out if we were unable to get the students roles
return "$1" if ($tmp =~ /^(con_lost|error|no_such_host)/i);
# Go through the roles looking for enrollment in this course
my $result = '';
foreach my $course (keys(%roles)) {
if ($course=~m{^/\Q$cdom\E/\Q$cnum\E(?:\/)*(?:\s+)*(\w+)*\_st$}) {
# We are in this course
my $section=$1;
$section='' if ($course eq "/$cdom/$cnum".'_st');
if (defined($csec) && $section eq $csec) {
$result .= 'ok:';
} elsif ( ((!$section) && (!$csec)) || ($section ne $csec) ) {
my (undef,$end,$start)=split(/\_/,$roles{$course});
my $now=time;
# if this is an active role
if (!($start && ($now<$start)) || !($end && ($now>$end))) {
my $reply=&Apache::lonnet::modifystudent
# dom name id mode pass f m l g
($udom,$unam,'', '', '',undef,undef,undef,undef,
$section,time,undef,undef,$desiredhost);
$result .= $reply.':';
}
}
}
}
if ($result eq '') {
$result = 'Unable to find section for this student';
} else {
$result =~ s/(ok:)+/ok/g;
}
return $result;
}
sub modifyuserrole {
my ($context,$setting,$changeauth,$cid,$udom,$uname,$uid,$umode,$upass,
$first,$middle,$last,$gene,$sec,$forceid,$desiredhome,$email,$role,
$end,$start,$checkid) = @_;
my ($scope,$userresult,$authresult,$roleresult,$idresult);
if ($setting eq 'course' || $context eq 'course') {
$scope = '/'.$cid;
$scope =~ s/\_/\//g;
if ($role ne 'cc' && $sec ne '') {
$scope .='/'.$sec;
}
} elsif ($context eq 'domain') {
$scope = '/'.$env{'request.role.domain'}.'/';
} elsif ($context eq 'construction_space') {
$scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'};
}
if ($context eq 'domain') {
my $uhome = &Apache::lonnet::homeserver($uname,$udom);
if ($uhome ne 'no_host') {
if (($changeauth eq 'Yes') && (&Apache::lonnet::allowed('mau',$udom))) {
if ((($umode =~ /^krb4|krb5|internal$/) && $upass ne '') ||
($umode eq 'localauth')) {
$authresult = &Apache::lonnet::modifyuserauth($udom,$uname,$umode,$upass);
}
}
if (($forceid) && (&Apache::lonnet::allowed('mau',$udom)) &&
($env{'form.recurseid'}) && ($checkid)) {
my %userupdate = (
lastname => $last,
middlename => $middle,
firstname => $first,
generation => $gene,
id => $uid,
);
$idresult = &propagate_id_change($uname,$udom,\%userupdate);
}
}
}
$userresult =
&Apache::lonnet::modifyuser($udom,$uname,$uid,$umode,$upass,$first,
$middle,$last,$gene,$forceid,$desiredhome,
$email,$role,$start,$end);
if ($userresult eq 'ok') {
if ($role ne '') {
$roleresult = &Apache::lonnet::assignrole($udom,$uname,$scope,
$role,$end,$start);
}
}
return ($userresult,$authresult,$roleresult,$idresult);
}
sub propagate_id_change {
my ($uname,$udom,$user) = @_;
my (@types,@roles);
@types = ('active','future');
@roles = ('st');
my $idresult;
my %roleshash = &Apache::lonnet::get_my_roles($uname,
$udom,'userroles',\@types,\@roles);
my %args = (
one_time => 1,
);
foreach my $item (keys(%roleshash)) {
my ($cnum,$cdom,$role) = split(/:/,$item);
my ($start,$end) = split(/:/,$roleshash{$item});
if (&Apache::lonnet::is_course($cdom,$cnum)) {
my $result = &update_classlist($cdom,$cnum,$udom,$uname,$user);
my %coursehash =
&Apache::lonnet::coursedescription($cdom.'_'.$cnum,\%args);
my $cdesc = $coursehash{'description'};
if ($cdesc eq '') {
$cdesc = $cdom.'_'.$cnum;
}
if ($result eq 'ok') {
$idresult .= &mt('Classlist update for "[_1]" in "[_2]".',$uname.':'.$udom,$cdesc).' '."\n";
} else {
$idresult .= &mt('Error: "[_1]" during classlist update for "[_2]" in "[_3]".',$result,$uname.':'.$udom,$cdesc).' '."\n";
}
}
}
return $idresult;
}
sub update_classlist {
my ($cdom,$cnum,$udom,$uname,$user) = @_;
my ($uid,$classlistentry);
my $fullname =
&Apache::lonnet::format_name($user->{'firstname'},$user->{'middlename'},
$user->{'lastname'},$user->{'generation'},
'lastname');
my %classhash = &Apache::lonnet::get('classlist',[$uname.':'.$udom],
$cdom,$cnum);
my @classinfo = split(/:/,$classhash{$uname.':'.$udom});
my $ididx=&Apache::loncoursedata::CL_ID() - 2;
my $nameidx=&Apache::loncoursedata::CL_FULLNAME() - 2;
for (my $i=0; $i<@classinfo; $i++) {
if ($i == $ididx) {
if (defined($user->{'id'})) {
$classlistentry .= $user->{'id'}.':';
} else {
$classlistentry .= $classinfo[$i].':';
}
} elsif ($i == $nameidx) {
$classlistentry .= $fullname.':';
} else {
$classlistentry .= $classinfo[$i].':';
}
}
$classlistentry =~ s/:$//;
my $reply=&Apache::lonnet::cput('classlist',
{"$uname:$udom" => $classlistentry},
$cdom,$cnum);
if (($reply eq 'ok') || ($reply eq 'delayed')) {
return 'ok';
} else {
return 'error: '.$reply;
}
}
###############################################################
###############################################################
# build a role type and role selection form
sub domain_roles_select {
# Set up the role type and role selection boxes when in
# domain context
#
# Role types
my @roletypes = ('domain','construction_space','course');
my %lt = &role_type_names();
#
# build up the menu information to be passed to
# &Apache::loncommon::linked_select_forms
my %select_menus;
if ($env{'form.roletype'} eq '') {
$env{'form.roletype'} = 'domain';
}
foreach my $roletype (@roletypes) {
# set up the text for this domain
$select_menus{$roletype}->{'text'}= $lt{$roletype};
# we want a choice of 'default' as the default in the second menu
if ($env{'form.roletype'} ne '') {
$select_menus{$roletype}->{'default'} = $env{'form.showrole'};
} else {
$select_menus{$roletype}->{'default'} = 'Any';
}
# Now build up the other items in the second menu
my @roles;
if ($roletype eq 'domain') {
@roles = &domain_roles();
} elsif ($roletype eq 'construction_space') {
@roles = &construction_space_roles();
} else {
@roles = &course_roles('domain');
unshift(@roles,'cr');
}
my $order = ['Any',@roles];
$select_menus{$roletype}->{'order'} = $order;
foreach my $role (@roles) {
if ($role eq 'cr') {
$select_menus{$roletype}->{'select2'}->{$role} =
&mt('Custom role');
} else {
$select_menus{$roletype}->{'select2'}->{$role} =
&Apache::lonnet::plaintext($role);
}
}
$select_menus{$roletype}->{'select2'}->{'Any'} = &mt('Any');
}
my $result = &Apache::loncommon::linked_select_forms
('studentform',(' 'x3).&mt('Role: '),$env{'form.roletype'},
'roletype','showrole',\%select_menus,['domain','construction_space','course']);
return $result;
}
###############################################################
###############################################################
sub hidden_input {
my ($name,$value) = @_;
return ''."\n";
}
sub print_upload_manager_header {
my ($r,$datatoken,$distotal,$krbdefdom,$context)=@_;
my $javascript;
#
if (! exists($env{'form.upfile_associate'})) {
$env{'form.upfile_associate'} = 'forward';
}
if ($env{'form.associate'} eq 'Reverse Association') {
if ( $env{'form.upfile_associate'} ne 'reverse' ) {
$env{'form.upfile_associate'} = 'reverse';
} else {
$env{'form.upfile_associate'} = 'forward';
}
}
if ($env{'form.upfile_associate'} eq 'reverse') {
$javascript=&upload_manager_javascript_reverse_associate();
} else {
$javascript=&upload_manager_javascript_forward_associate();
}
#
# Deal with restored settings
my $password_choice = '';
if (exists($env{'form.ipwd_choice'}) &&
$env{'form.ipwd_choice'} ne '') {
# If a column was specified for password, assume it is for an
# internal password. This is a bug waiting to be filed (could be
# local or krb auth instead of internal) but I do not have the
# time to mess around with this now.
$password_choice = 'int';
}
#
my $javascript_validations =
&javascript_validations('auth',$krbdefdom,$password_choice,undef,
$env{'request.role.domain'});
my $checked=(($env{'form.noFirstLine'})?' checked="checked" ':'');
$r->print(&mt('Total number of records found in file: [_1].',$distotal).
" \n");
$r->print('
'.
&mt('Identify fields in uploaded list')."
\n");
$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 are insufficient to add users.')." \n");
$r->print(&hidden_input('action','upload').
&hidden_input('state','got_file').
&hidden_input('associate','').
&hidden_input('datatoken',$datatoken).
&hidden_input('fileupload',$env{'form.fileupload'}).
&hidden_input('upfiletype',$env{'form.upfiletype'}).
&hidden_input('upfile_associate',$env{'form.upfile_associate'}));
$r->print(' ');
$r->print('');
$r->print("
\n".
'');
}
###############################################################
###############################################################
sub javascript_validations {
my ($mode,$krbdefdom,$curr_authtype,$curr_authfield,$domain)=@_;
my $authheader;
if ($mode eq 'auth') {
my %param = ( formname => 'studentform',
kerb_def_dom => $krbdefdom,
curr_authtype => $curr_authtype);
$authheader = &Apache::loncommon::authform_header(%param);
} elsif ($mode eq 'createcourse') {
my %param = ( formname => 'ccrs',
kerb_def_dom => $krbdefdom,
curr_authtype => $curr_authtype );
$authheader = &Apache::loncommon::authform_header(%param);
} elsif ($mode eq 'modifycourse') {
my %param = ( formname => 'cmod',
kerb_def_dom => $krbdefdom,
mode => 'modifycourse',
curr_authtype => $curr_authtype,
curr_autharg => $curr_authfield );
$authheader = &Apache::loncommon::authform_header(%param);
}
my %alert = &Apache::lonlocal::texthash
(username => 'You need to specify the username field.',
authen => 'You must choose an authentication type.',
krb => 'You need to specify the Kerberos domain.',
ipass => 'You need to specify the initial password.',
name => 'The optional name field was not specified.',
snum => 'The optional ID number field was not specified.',
section => 'The optional section field was not specified.',
email => 'The optional email address field was not specified.',
role => 'The optional role field was not specified.',
continue => 'Continue adding users?',
);
# my $pjump_def = &Apache::lonhtmlcommon::pjump_javascript_definition();
my $function_name =(< 1) {
$auth_checks .= (<=2) && (tw<=6)) { foundname=1; }
if (tw==7) { foundid=1; }
if (tw==8) { foundsec=1; }
if (tw==9) { foundpwd=1; }
if (tw==10) { foundemail=1; }
if (tw==11) { foundrole=1; }
}
verify_message(vf,founduname,foundpwd,foundname,foundid,foundsec,foundemail,foundrole);
}
//
// vf = this.form
// tf = column number
//
// values of nw
//
// 0 = none
// 1 = username
// 2 = names (lastname, firstnames)
// 3 = fname (firstname)
// 4 = mname (middlename)
// 5 = lname (lastname)
// 6 = gen (generation)
// 7 = id
// 8 = section
// 9 = ipwd (password)
// 10 = email address
// 11 = role
function flip(vf,tf) {
var nw=eval('vf.f'+tf+'.selectedIndex');
var i;
// make sure no other columns are labeled the same as this one
for (i=0;i<=vf.nfields.value;i++) {
if ((i!=tf) && (eval('vf.f'+i+'.selectedIndex')==nw)) {
eval('vf.f'+i+'.selectedIndex=0;')
}
}
// If we set this to 'lastname, firstnames', clear out all the ones
// set to 'fname','mname','lname','gen' (3,4,5,6) currently.
if (nw==2) {
for (i=0;i<=vf.nfields.value;i++) {
if ((eval('vf.f'+i+'.selectedIndex')>=3) &&
(eval('vf.f'+i+'.selectedIndex')<=6)) {
eval('vf.f'+i+'.selectedIndex=0;')
}
}
}
// If we set this to one of 'fname','mname','lname','gen' (3,4,5,6),
// clear out any that are set to 'lastname, firstnames' (2)
if ((nw>=3) && (nw<=6)) {
for (i=0;i<=vf.nfields.value;i++) {
if (eval('vf.f'+i+'.selectedIndex')==2) {
eval('vf.f'+i+'.selectedIndex=0;')
}
}
}
// If we set the password, make the password form below correspond to
// the new value.
if (nw==9) {
changed_radio('int',document.studentform);
set_auth_radio_buttons('int',document.studentform);
vf.intarg.value='';
vf.krbarg.value='';
vf.locarg.value='';
}
}
function clearpwd(vf) {
var i;
for (i=0;i<=vf.nfields.value;i++) {
if (eval('vf.f'+i+'.selectedIndex')==9) {
eval('vf.f'+i+'.selectedIndex=0;')
}
}
}
ENDPICK
}
###############################################################
###############################################################
sub upload_manager_javascript_reverse_associate {
return(<=1) && (i<=5)) && tw!=0 ) { foundname=1; }
if (i==6 && tw!=0) { foundid=1; }
if (i==7 && tw!=0) { foundsec=1; }
if (i==8 && tw!=0) { foundpwd=1; }
if (i==9 && tw!=0) { foundrole=1; }
}
verify_message(vf,founduname,foundpwd,foundname,foundid,foundsec,foundrole);
}
function flip(vf,tf) {
var nw=eval('vf.f'+tf+'.selectedIndex');
var i;
// picked the all one name field, reset the other name ones to blank
if (tf==1 && nw!=0) {
for (i=2;i<=5;i++) {
eval('vf.f'+i+'.selectedIndex=0;')
}
}
//picked one of the piecewise name fields, reset the all in
//one field to blank
if ((tf>=2) && (tf<=5) && (nw!=0)) {
eval('vf.f1.selectedIndex=0;')
}
// intial password specified, pick internal authentication
if (tf==8 && nw!=0) {
changed_radio('int',document.studentform);
set_auth_radio_buttons('int',document.studentform);
vf.krbarg.value='';
vf.intarg.value='';
vf.locarg.value='';
}
}
function clearpwd(vf) {
var i;
if (eval('vf.f8.selectedIndex')!=0) {
eval('vf.f8.selectedIndex=0;')
}
}
ENDPICK
}
###############################################################
###############################################################
sub print_upload_manager_footer {
my ($r,$i,$keyfields,$defdom,$today,$halfyear,$context)=@_;
my $formname;
if ($context eq 'course') {
$formname = 'document.studentform';
} elsif ($context eq 'construction_space') {
$formname = 'document.studentform';
} elsif ($context eq 'domain') {
$formname = 'document.studentform';
}
my ($krbdef,$krbdefdom) =
&Apache::loncommon::get_kerberos_defaults($defdom);
my %param = ( formname => $formname,
kerb_def_dom => $krbdefdom,
kerb_def_auth => $krbdef
);
if (exists($env{'form.ipwd_choice'}) &&
defined($env{'form.ipwd_choice'}) &&
$env{'form.ipwd_choice'} ne '') {
$param{'curr_authtype'} = 'int';
}
my $krbform = &Apache::loncommon::authform_kerberos(%param);
my $intform = &Apache::loncommon::authform_internal(%param);
my $locform = &Apache::loncommon::authform_local(%param);
my $date_table = &date_setting_table(undef,undef,$context);
my $Str = "\n".'
END
return;
}
#
# Print out the initial form to get the file containing a list of users
#
sub print_first_users_upload_form {
my ($r,$context) = @_;
my $str;
$str = '';
$str .= '';
$str .= '';
$str .= "
".&mt('Upload a file containing information about users')."
\n");
}
my %counts = (
user => 0,
auth => 0,
role => 0,
);
my $flushc=0;
my %student=();
my %curr_groups;
my %userchg;
if ($context eq 'course') {
# Get information about course groups
%curr_groups = &Apache::longroup::coursegroups();
}
my (%curr_rules,%got_rules,%alerts);
# Get new users list
foreach (@userdata) {
my %entries=&Apache::loncommon::record_sep($_);
# Determine user name
unless (($entries{$fields{'username'}} eq '') ||
(!defined($entries{$fields{'username'}}))) {
my ($fname, $mname, $lname,$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'}}
ne &LONCAPA::clean_username($entries{$fields{'username'}})) {
$r->print(' '.
&mt('[_1]: Unacceptable username for user [_2] [_3] [_4] [_5]',
$entries{$fields{'username'}},$fname,$mname,$lname,$gen).
'');
} else {
my $username = $entries{$fields{'username'}};
my $sec;
if ($context eq 'course' || $setting eq 'course') {
# determine section number
if (defined($fields{'sec'})) {
if (defined($entries{$fields{'sec'}})) {
$sec=$entries{$fields{'sec'}};
}
} else {
$sec = $defaultsec;
}
# remove non alphanumeric values from section
$sec =~ s/\W//g;
if ($sec eq "none" || $sec eq 'all') {
$r->print(' '.
&mt('[_1]: Unable to enroll: section name "[_2]" for user [_3] [_4] [_5] [_6] is a reserved word.',
$username,$sec,$fname,$mname,$lname,$gen));
next;
} elsif (($sec ne '') && (exists($curr_groups{$sec}))) {
$r->print(' '.
&mt('[_1]: Unable to enroll: section name "[_2]" for user [_3] [_4] [_5] [_6] is a course group. Section names and group names must be distinct.',
$username,$sec,$fname,$mname,$lname,$gen));
next;
}
}
# determine id number
my $id='';
if (defined($fields{'id'})) {
if (defined($entries{$fields{'id'}})) {
$id=$entries{$fields{'id'}};
}
$id=~tr/A-Z/a-z/;
}
# determine email address
my $email='';
if (defined($fields{'email'})) {
if (defined($entries{$fields{'email'}})) {
$email=$entries{$fields{'email'}};
unless ($email=~/^[^\@]+\@[^\@]+$/) { $email=''; } }
}
# determine user password
my $password = $genpwd;
if (defined($fields{'ipwd'})) {
if ($entries{$fields{'ipwd'}}) {
$password=$entries{$fields{'ipwd'}};
}
}
# determine user role
my $role = '';
if (defined($fields{'role'})) {
if ($entries{$fields{'role'}}) {
my @poss_roles =
&curr_role_permissions($context,$setting);
if (grep(/^\Q$entries{$fields{'role'}}\E/,@poss_roles)) {
$role=$entries{$fields{'role'}};
} else {
my $rolestr = join(', ',@poss_roles);
$r->print(' '.
&mt('[_1]: You do not have permission to add the requested role [_2] for the user.',$entries{$fields{'username'}},$entries{$fields{'role'}}).' '.&mt('Allowable role(s) is/are: [_1].',$rolestr)."\n");
next;
}
}
}
if ($role eq '') {
$role = $defaultrole;
}
# Clean up whitespace
foreach (\$domain,\$username,\$id,\$fname,\$mname,
\$lname,\$gen,\$sec,\$role) {
$$_ =~ s/(\s+$|^\s+)//g;
}
# check against rules
my $checkid = 0;
my $newuser = 0;
my (%rulematch,%inst_results,%idinst_results);
my $uhome=&Apache::lonnet::homeserver($username,$domain);
if ($uhome eq 'no_host') {
$checkid = 1;
$newuser = 1;
my $checkhash;
my $checks = { 'username' => 1 };
$checkhash->{$username.':'.$domain} = { 'newuser' => 1, };
&Apache::loncommon::user_rule_check($checkhash,$checks,
\%alerts,\%rulematch,\%inst_results,\%curr_rules,
\%got_rules);
if (ref($alerts{'username'}) eq 'HASH') {
if (ref($alerts{'username'}{$domain}) eq 'HASH') {
next if ($alerts{'username'}{$domain}{$username});
}
}
}
if ($id ne '') {
if (!$newuser) {
my %idhash = &Apache::lonnet::idrget($domain,($username));
if ($idhash{$username} ne $id) {
$checkid = 1;
}
}
if ($checkid) {
my $checkhash;
my $checks = { 'id' => 1 };
$checkhash->{$username.':'.$domain} = { 'newuser' => $newuser,
'id' => $id };
&Apache::loncommon::user_rule_check($checkhash,$checks,
\%alerts,\%rulematch,\%idinst_results,\%curr_rules,
\%got_rules);
if (ref($alerts{'id'}) eq 'HASH') {
if (ref($alerts{'id'}{$domain}) eq 'HASH') {
next if ($alerts{'id'}{$domain}{$id});
}
}
}
}
if ($password || $env{'form.login'} eq 'loc') {
my ($userresult,$authresult,$roleresult);
if ($role eq 'st') {
&modifystudent($domain,$username,$cid,$sec,
$desiredhost);
$roleresult =
&Apache::lonnet::modifystudent
($domain,$username,$id,$amode,$password,
$fname,$mname,$lname,$gen,$sec,$enddate,
$startdate,$env{'form.forceid'},
$desiredhost,$email);
} else {
($userresult,$authresult,$roleresult) =
&modifyuserrole($context,$setting,
$changeauth,$cid,$domain,$username,
$id,$amode,$password,$fname,
$mname,$lname,$gen,$sec,
$env{'form.forceid'},$desiredhost,
$email,$role,$enddate,$startdate,$checkid);
}
$flushc =
&user_change_result($r,$userresult,$authresult,
$roleresult,\%counts,$flushc,
$username,%userchg);
} else {
if ($context eq 'course') {
$r->print(' '.
&mt('[_1]: Unable to enroll. No password specified.',$username)
);
} elsif ($context eq 'construction_space') {
$r->print(' '.
&mt('[_1]: Unable to add co-author. No password specified.',$username)
);
} else {
$r->print(' '.
&mt('[_1]: Unable to add user. No password specified.',$username)
);
}
}
}
}
} # end of foreach (@userdata)
# Flush the course logs so reverse user roles immediately updated
&Apache::lonnet::flushcourselogs();
$r->print("
\n".
&mt('Roles added for [_1] users. If user is active, the new role will be available when the user next logs in to LON-CAPA.',$counts{'role'})."
\n");
}
if ($counts{'auth'} > 0) {
$r->print("
\n".
&mt('Authentication changed for [_1] existing users.',
$counts{'auth'})."
\n");
}
if (keys(%alerts) > 0) {
if (ref($alerts{'username'}) eq 'HASH') {
foreach my $dom (sort(keys(%{$alerts{'username'}}))) {
my $count;
if (ref($alerts{'username'}{$dom}) eq 'HASH') {
$count = keys(%{$alerts{'username'}{$dom}});
}
my $domdesc = &Apache::lonnet::domain($domain,'description');
if (ref($curr_rules{$dom}) eq 'HASH') {
$r->print(&Apache::loncommon::instrule_disallow_msg(
'username',$domdesc,$count,'upload'));
}
$r->print(&Apache::loncommon::user_rule_formats($dom,
$domdesc,$curr_rules{$dom}{'username'},
'username'));
}
}
if (ref($alerts{'id'}) eq 'HASH') {
foreach my $dom (sort(keys(%{$alerts{'id'}}))) {
my $count;
if (ref($alerts{'id'}{$dom}) eq 'HASH') {
$count = keys(%{$alerts{'id'}{$dom}});
}
my $domdesc = &Apache::lonnet::domain($domain,'description');
if (ref($curr_rules{$dom}) eq 'HASH') {
$r->print(&Apache::loncommon::instrule_disallow_msg(
'id',$domdesc,$count,'upload'));
}
$r->print(&Apache::loncommon::user_rule_formats($dom,
$domdesc,$curr_rules{$dom}{'id'},'id'));
}
}
}
$r->print('');
#####################################
# Drop students #
#####################################
if ($env{'form.fullup'} eq 'yes') {
$r->print('
'.&mt('Dropping Students')."
\n");
# Get current classlist
my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist();
if (! defined($classlist)) {
$r->print(&mt('There are no students currently enrolled.').
"\n");
} else {
# Remove the students we just added from the list of students.
foreach (@userdata) {
my %entries=&Apache::loncommon::record_sep($_);
unless (($entries{$fields{'username'}} eq '') ||
(!defined($entries{$fields{'username'}}))) {
delete($classlist->{$entries{$fields{'username'}}.
':'.$domain});
}
}
# Print out list of dropped students.
&show_drop_list($r,$classlist,$keylist,'nosort');
}
}
} # end of unless
}
sub user_change_result {
my ($r,$userresult,$authresult,$roleresult,$counts,$flushc,$username,
$userchg) = @_;
my $okresult = 0;
if ($userresult ne 'ok') {
if ($userresult =~ /^error:(.+)$/) {
my $error = $1;
$r->print(' '.
&mt('[_1]: Unable to add/modify: [_2]',$username,$error));
}
} else {
$counts->{'user'} ++;
$okresult = 1;
}
if ($authresult ne 'ok') {
if ($authresult =~ /^error:(.+)$/) {
my $error = $1;
$r->print(' '.
&mt('[_1]: Unable to modify authentication: [_2]',$username,$error));
}
} else {
$counts->{'auth'} ++;
$okresult = 1;
}
if ($roleresult ne 'ok') {
if ($roleresult =~ /^error:(.+)$/) {
my $error = $1;
$r->print(' '.
&mt('[_1]: Unable to add role: [_2]',$username,$error));
}
} else {
$counts->{'role'} ++;
$okresult = 1;
}
if ($okresult) {
$flushc++;
$userchg->{$username}=1;
$r->print('. ');
if ($flushc>15) {
$r->rflush;
$flushc=0;
}
}
return $flushc;
}
# ========================================================= Menu Phase Two Drop
sub print_expire_menu {
my ($r,$context) = @_;
$r->print("
".&mt("Expire Users' Roles")."
");
my $cid=$env{'request.course.id'};
my ($classlist,$keylist) = &Apache::loncoursedata::get_classlist();
if (! defined($classlist)) {
$r->print(&mt('There are no students currently enrolled.')."\n");
return;
}
# Print out the available choices
&show_drop_list($r,$classlist,$keylist);
return;
}
# ================================================================== Phase four
sub update_user_list {
my ($r,$context,$setting,$choice) = @_;
my $now = time;
my $count=0;
my @changelist;
if ($choice ne '') {
@changelist = &Apache::loncommon::get_env_multiple('form.actionlist');
} else {
@changelist = &Apache::loncommon::get_env_multiple('form.droplist');
}
my %result_text = ( ok => { 'revoke' => 'Revoked',
'delete' => 'Deleted',
'reenable' => 'Re-enabled',
'activate' => 'Activated',
},
error => {'revoke' => 'revoking',
'delete' => 'deleting',
'reenable' => 're-enabling',
'activate' => 'activating',
},
);
my ($startdate,$enddate);
if ($choice eq 'chgdates' || $choice eq 'reenable' || $choice eq 'activate') {
($startdate,$enddate) = &get_dates_from_form();
}
foreach my $item (@changelist) {
my ($role,$uname,$udom,$cid,$sec,$scope,$result,$type,$locktype,@sections,
$scopestem);
if ($context eq 'course') {
($uname,$udom,$role,$sec,$type,$locktype) = split(/\:/,$item,-1);
$cid = $env{'request.course.id'};
$scopestem = '/'.$cid;
$scopestem =~s/\_/\//g;
if ($sec eq '') {
$scope = $scopestem;
} else {
$scope = $scopestem.'/'.$sec;
}
} elsif ($context eq 'construction_space') {
($uname,$udom,$role) = split(/\:/,$item,-1);
$scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'};
} elsif ($context eq 'domain') {
if ($setting eq 'domain') {
($role,$uname,$udom) = split(/\:/,$item,-1);
$scope = '/'.$env{'request.role.domain'}.'/';
} elsif ($setting eq 'construction_space') {
($uname,$udom,$role,$scope) = split(/\:/,$item);
} elsif ($setting eq 'course') {
($uname,$udom,$role,$cid,$sec,$type,$locktype) =
split(/\:/,$item);
$scope = '/'.$cid;
$scope =~s/\_/\//g;
if ($sec ne '') {
$scope .= '/'.$sec;
}
}
}
my $plrole = &Apache::lonnet::plaintext($role);
my ($uid,$first,$middle,$last,$gene,$sec);
my $start = $env{'form.'.$item.'_start'};
my $end = $env{'form.'.$item.'_end'};
# revoke or delete user role
if ($choice eq 'revoke') {
$end = $now;
if ($role eq 'st') {
$result =
&Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid);
} else {
$result =
&Apache::lonnet::revokerole($udom,$uname,$scope,$role);
}
} elsif ($choice eq 'delete') {
$start = -1;
$end = -1;
if ($role eq 'st') {
# FIXME - how does role deletion affect classlist?
&Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid);
} else {
$result =
&Apache::lonnet::assignrole($udom,$uname,$scope,$role,$now,
0,1);
}
} else {
#reenable, activate, change access dates or change section
if ($choice ne 'chgsec') {
$start = $startdate;
$end = $enddate;
}
if ($choice eq 'reenable') {
if ($role eq 'st') {
$result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid);
} else {
$result =
&Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end,
$now);
}
} elsif ($choice eq 'activate') {
if ($role eq 'st') {
$result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid);
} else {
$result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end,
$now);
}
} elsif ($choice eq 'chgdates') {
if ($role eq 'st') {
$result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid);
} else {
$result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end,
$start);
}
} elsif ($choice eq 'chgsec') {
my (@newsecs,$revresult,$nochg,@retained);
if ($role ne 'cc') {
@newsecs = split(/,/,$env{'form.newsecs'});
}
# remove existing section if not to be retained.
if (!$env{'form.retainsec'}) {
if ($sec eq '') {
if (@newsecs == 0) {
$result = &mt('No change in section assignment (none)');
$nochg = 1;
}
} else {
if (!grep(/^\Q$sec\E$/,@newsecs)) {
$revresult =
&Apache::lonnet::revokerole($udom,$uname,$scope,$role);
} else {
push(@retained,$sec);
}
}
} else {
push(@retained,$sec);
}
# add new sections
if (@newsecs == 0) {
if (!$nochg) {
if ($sec ne '') {
if ($role eq 'st') {
$result =
&Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,undef,$end,$start,$type,$locktype,$cid);
} else {
my $newscope = $scopestem;
$result = &Apache::lonnet::assignrole($udom,$uname,$newscope,$role,$end,$start);
}
}
}
} else {
foreach my $newsec (@newsecs) {
if (!grep(/^\Q$newsec\E$/,@retained)) {
if ($role eq 'st') {
$result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$newsec,$end,$start,$type,$locktype,$cid);
} else {
my $newscope = $scopestem;
if ($newsec ne '') {
$newscope .= '/'.$newsec;
}
$result = &Apache::lonnet::assignrole($udom,$uname,
$newscope,$role,$end,$start);
}
}
}
}
}
}
if ($result eq 'ok' || $result eq 'ok:') {
$r->print(&mt("$result_text{'ok'}{$choice} role of '[_1]' in [_2] for [_3]",
$plrole,$scope,$uname.':'.$udom).' ');
$count++;
} else {
$r->print(
&mt("Error $result_text{'error'}{$choice} [_1] in [_2] for [_3]:[_4]",
$plrole,$scope,$uname.':'.$udom,$result).' ');
}
}
$r->print('
'.&mt("$result_text{'ok'}{$choice} role(s) for [quant,_1,user,users,users].",$count).'
');
if ($count > 0) {
if ($choice eq 'revoke') {
$r->print('
'.&mt('Re-enabling will re-activate data for the role.
'));
}
# Flush the course logs so reverse user roles immediately updated
&Apache::lonnet::flushcourselogs();
}
if ($env{'form.makedatesdefault'}) {
if ($choice eq 'chgdates' || $choice eq 'reenable' || $choice eq 'activate') {
$r->print(&make_dates_default($startdate,$enddate));
}
}
}
sub classlist_drop {
my ($scope,$uname,$udom,$now,$action) = @_;
my ($cdom,$cnum) = ($scope=~m{^/($match_domain)/($match_courseid)});
my $cid=$cdom.'_'.$cnum;
my $user = $uname.':'.$udom;
if ($action eq 'drop') {
if (!&active_student_roles($cnum,$cdom,$uname,$udom)) {
my $result =
&Apache::lonnet::cput('classlist',
{ $user => $now },
$env{'course.'.$cid.'.domain'},
$env{'course.'.$cid.'.num'});
return &mt('Drop from classlist: [_1]',
''.$result.'').' ';
}
}
}
sub active_student_roles {
my ($cnum,$cdom,$uname,$udom) = @_;
my %roles =
&Apache::lonnet::get_my_roles($uname,$udom,'userroles',
['future','active'],['st']);
return exists($roles{"$cnum:$cdom:st"});
}
sub section_check_js {
my $groupslist= &get_groupslist();
return <<"END";
function validate(caller) {
var groups = new Array($groupslist);
var secname = caller.value;
if ((secname == 'all') || (secname == 'none')) {
alert("'"+secname+"' may not be used as the name for a section, as it is a reserved word.\\nPlease choose a different section name.");
return 'error';
}
if (secname != '') {
for (var k=0; k'.$authformkrb.''.
&Apache::loncommon::end_data_table_row()."\n";
}
if ($can_assign{'int'}) {
$response .= &Apache::loncommon::start_data_table_row().
'
'.$authformint.'
'.
&Apache::loncommon::end_data_table_row()."\n"
}
if ($can_assign{'loc'}) {
$response .= &Apache::loncommon::start_data_table_row().
'
'.$authformloc.'
'.
&Apache::loncommon::end_data_table_row()."\n";
}
$response .= &Apache::loncommon::end_data_table();
}
return $response;
}
sub course_sections {
my ($sections_count,$role) = @_;
my $output = '';
my @sections = (sort {$a <=> $b} keys %{$sections_count});
if (scalar(@sections) == 1) {
$output = ''."\n".
' '."\n".
' '."\n".
' '."\n";
} else {
$output = ''."\n";
foreach my $sec (@sections) {
$output .= '\n";
}
}
$output .= '';
return $output;
}
sub get_groupslist {
my $groupslist;
my %curr_groups = &Apache::longroup::coursegroups();
if (%curr_groups) {
$groupslist = join('","',sort(keys(%curr_groups)));
$groupslist = '"'.$groupslist.'"';
}
return $groupslist;
}
sub setsections_javascript {
my ($form,$groupslist) = @_;
my ($checkincluded,$finish,$roleplace,$setsection_js);
if ($form eq 'cu') {
$checkincluded = 'formname.elements[i-1].checked == true';
$finish = 'formname.submit()';
$roleplace = 3;
} else {
$checkincluded = 'formname.name == "'.$form.'"';
$finish = "seccheck = 'ok';";
$roleplace = 1;
$setsection_js = "var seccheck = 'alert';";
}
my %alerts = &Apache::lonlocal::texthash(
secd => 'Section designations do not apply to Course Coordinator roles.',
accr => 'A course coordinator role will be added with access to all sections.',
inea => 'In each course, each user may only have one student role at a time.',
youh => 'You had selected ',
secs => 'sections.',
plmo => 'Please modify your selections so they include no more than one section.',
mayn => 'may not be used as the name for a section, as it is a reserved word.',
plch => 'Please choose a different section name.',
mnot => 'may not be used as a section name, as it is the name of a course group.',
secn => 'Section names and group names must be distinct. Please choose a different section name.',
);
$setsection_js .= <<"ENDSECCODE";
function setSections(formname) {
var re1 = /^currsec_/;
var groups = new Array($groupslist);
for (var i=0;i 0) {
if (formname.elements[i+1].value != "" && formname.elements[i+1].value != null) {
sections = sections + "," + formname.elements[i+1].value;
}
}
else {
sections = formname.elements[i+1].value;
}
var newsecs = formname.elements[i+1].value;
var numsplit;
if (newsecs != null && newsecs != "") {
numsplit = newsecs.split(/,/g);
numsec = numsec + numsplit.length;
}
if ((role == 'st') && (numsec > 1)) {
alert("$alerts{'inea'} $alerts{'youh'} "+numsec+" $alerts{'secs'}\\n$alerts{'plmo'}")
return;
}
else {
if (numsplit != null) {
for (var j=0; j