# The LearningOnline Network with CAPA
# Utility functions for managing LON-CAPA user accounts
#
# $Id: lonuserutils.pm,v 1.211 2022/11/17 19:07:21 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;
=pod
=head1 NAME
Apache::lonuserutils.pm
=head1 SYNOPSIS
Utilities for management of users and custom roles
Provides subroutines called by loncreateuser.pm
=head1 OVERVIEW
=cut
use strict;
use Apache::lonnet;
use Apache::loncommon();
use Apache::lonhtmlcommon;
use Apache::lonlocal;
use Apache::longroup;
use HTML::Entities;
use LONCAPA qw(:DEFAULT :match);
###############################################################
###############################################################
# Drop student from all sections of a course, except optional $csec
sub modifystudent {
my ($udom,$unam,$courseid,$csec,$desiredhost,$context)=@_;
# 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 ($cnum,$cdom) = &get_course_identity($courseid);
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,'','manual',
'',$courseid,'',$context);
$result .= $reply.':';
}
}
}
}
if ($result eq '') {
$result = &mt('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,$inststatus) = @_;
my ($scope,$userresult,$authresult,$roleresult,$idresult);
if ($setting eq 'course' || $context eq 'course') {
$scope = '/'.$cid;
$scope =~ s/\_/\//g;
if (($role ne 'cc') && ($role ne 'co') && ($sec ne '')) {
$scope .='/'.$sec;
}
} elsif ($context eq 'domain') {
$scope = '/'.$env{'request.role.domain'}.'/';
} elsif ($context eq 'author') {
$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,$inststatus);
if ($userresult eq 'ok') {
if ($role ne '') {
$role =~ s/_/\//g;
$roleresult = &Apache::lonnet::assignrole($udom,$uname,$scope,
$role,$end,$start,'',
'',$context);
}
}
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,-1);
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,$newend) = @_;
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;
my $endidx = &Apache::loncoursedata::CL_END() - 2;
my $startidx = &Apache::loncoursedata::CL_START() - 2;
for (my $i=0; $i<@classinfo; $i++) {
if ($i == $endidx) {
if ($newend ne '') {
$classlistentry .= $newend.':';
} else {
$classlistentry .= $classinfo[$i].':';
}
} elsif ($i == $startidx) {
if ($newend ne '') {
if ($classinfo[$i] > $newend) {
$classlistentry .= $newend.':';
} else {
$classlistentry .= $classinfo[$i].':';
}
} else {
$classlistentry .= $classinfo[$i].':';
}
} elsif ($i == $ididx) {
if (defined($user->{'id'})) {
$classlistentry .= $user->{'id'}.':';
} else {
$classlistentry .= $classinfo[$i].':';
}
} elsif ($i == $nameidx) {
if (defined($user->{'lastname'})) {
$classlistentry .= $fullname.':';
} else {
$classlistentry .= $classinfo[$i].':';
}
} 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','author','course','community');
my %lt = &role_type_names();
my $onchangefirst = "updateCols('showrole')";
my $onchangesecond = "updateCols('showrole')";
#
# 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};
my $crstype;
if ($roletype eq 'community') {
$crstype = 'Community';
}
# 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 'author') {
@roles = &construction_space_roles();
} else {
my $custom = 1;
@roles = &course_roles('domain',undef,$custom,$roletype);
}
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,$crstype);
}
}
$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','author','course','community'],$onchangefirst,
$onchangesecond);
return $result;
}
###############################################################
###############################################################
sub hidden_input {
my ($name,$value) = @_;
return ''."\n";
}
sub print_upload_manager_header {
my ($r,$datatoken,$distotal,$krbdefdom,$context,$permission,$crstype,
$can_assign)=@_;
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($can_assign);
} else {
$javascript=&upload_manager_javascript_forward_associate($can_assign);
}
#
# 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 $groupslist;
if ($context eq 'course') {
$groupslist = &get_groupslist();
}
my $javascript_validations =
&javascript_validations('upload',$krbdefdom,$password_choice,undef,
$env{'request.role.domain'},$context,
$groupslist,$crstype);
my $checked=(($env{'form.noFirstLine'})?' checked="checked"':'');
$r->print(
'
'. &mt('Total number of records found in file: [_1]' ,''.$distotal.''). "
\n" ); if ($distotal == 0) { $r->print(''.&mt('None found').'
'); } $r->print( ''.
&mt('Enter as many fields as you can.').'
'.
&mt('The system will inform you and bring you back to this page,[_1]if the data selected are insufficient to add users.','
').
"
' .&mt('Change authentication for existing users in domain "[_1]" to these settings?' ,$defdom) .' ' .' ' .'
'; } else { $Str .= ''."\n". &mt('This will not take effect if the user already exists.'). &Apache::loncommon::help_open_topic('Auth_Options'). "
\n"; } $Str .= &set_login($defdom,$krbform,$intform,$locform,$ltiform); my ($home_server_pick,$numlib) = &Apache::loncommon::home_server_form_item($defdom,'lcserver', 'default','hide'); if ($numlib > 1) { $Str .= &Apache::lonhtmlcommon::row_closure() .&Apache::lonhtmlcommon::row_title( &mt('LON-CAPA Home Server for New Users')) .&mt('LON-CAPA domain: [_1] with home server:','"'.$defdom.'"') .$home_server_pick .&Apache::lonhtmlcommon::row_closure(); } else { $Str .= $home_server_pick. &Apache::lonhtmlcommon::row_closure(); } my ($trusted,$untrusted); if ($context eq 'course') { ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('enroll',$defdom); } elsif ($context eq 'author') { ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('othcoau',$defdom); } $Str .= &Apache::lonhtmlcommon::row_title(&mt('Default domain')) .&Apache::loncommon::select_dom_form($defdom,'defaultdomain',undef,1,undef,$trusted,$untrusted) .&Apache::lonhtmlcommon::row_closure(); $Str .= &Apache::lonhtmlcommon::row_title(&mt('Starting and Ending Dates')) ."\n".$date_table."
\n" .&Apache::lonhtmlcommon::row_closure(); if ($context eq 'domain') { $Str .= &Apache::lonhtmlcommon::row_title( &mt('Settings for assigning roles')) .&mt('Pick the action to take on roles for these users:').''
.''.&mt('Domain Level').'
'
.$options
.'
' .''.&mt('Course Level').'' .'
' .$cb_script.$coursepick .&Apache::lonhtmlcommon::row_closure(); } elsif ($context eq 'author') { $Str .= $options .&Apache::lonhtmlcommon::row_closure(1); # last row in pick_box } } else { my ($cnum,$cdom) = &get_course_identity(); my $rowtitle = &mt('section'); my $defaultcredits; if ($showcredits) { $defaultcredits = &get_defaultcredits(); } my $secbox = §ion_picker($cdom,$cnum,'Any',$rowtitle,$permission, $context,'upload',$crstype,$showcredits, $defaultcredits); $Str .= $secbox .&Apache::lonhtmlcommon::row_closure(); my %lt; if ($crstype eq 'Community') { %lt = &Apache::lonlocal::texthash ( disp => 'Display members with current/future access who are not in the uploaded file', stus => 'Members selected from this list can be dropped.' ); } else { %lt = &Apache::lonlocal::texthash ( disp => 'Display students with current/future access who are not in the uploaded file', stus => 'Students selected from this list can be dropped.' ); } $Str .= &Apache::lonhtmlcommon::row_title(&mt('Full Update')) .''.&mt('Error').': '. &mt('Invalid datatoken').'
'); return 'missingdata'; } my @records=&Apache::loncommon::upfile_record_sep(); if($env{'form.noFirstLine'}){ $firstLine=shift(@records); } my $total=$#records; my $distotal=$total+1; my $today=time; my $halfyear=$today+15552000; # # Restore memorized settings my $col_setting_names = { 'username_choice' => 'scalar', # column settings 'names_choice' => 'scalar', 'fname_choice' => 'scalar', 'mname_choice' => 'scalar', 'lname_choice' => 'scalar', 'gen_choice' => 'scalar', 'id_choice' => 'scalar', 'sec_choice' => 'scalar', 'ipwd_choice' => 'scalar', 'email_choice' => 'scalar', 'role_choice' => 'scalar', 'domain_choice' => 'scalar', 'inststatus_choice' => 'scalar', }; if ($showcredits) { $col_setting_names->{'credits_choice'} = 'scalar'; } if ($context eq 'course') { &Apache::loncommon::restore_course_settings('enrollment_upload', $col_setting_names); } else { &Apache::loncommon::restore_settings($context,'user_upload', $col_setting_names); } my $defdom = $env{'request.role.domain'}; # # Determine kerberos parameters as appropriate my ($krbdef,$krbdefdom) = &Apache::loncommon::get_kerberos_defaults($defdom); # my ($authnum,%can_assign) = &Apache::loncommon::get_assignable_auth($defdom); &print_upload_manager_header($r,$datatoken,$distotal,$krbdefdom,$context, $permission,$crstype,\%can_assign); my $i; my $keyfields; if ($total>=0) { my @field= (['username',&mt('Username'), $env{'form.username_choice'}], ['names',&mt('Last Name, First Names'),$env{'form.names_choice'}], ['fname',&mt('First Name'), $env{'form.fname_choice'}], ['mname',&mt('Middle Names/Initials'),$env{'form.mname_choice'}], ['lname',&mt('Last Name'), $env{'form.lname_choice'}], ['gen', &mt('Generation'), $env{'form.gen_choice'}], ['id', &mt('Student/Employee ID'),$env{'form.id_choice'}], ['sec', &mt('Section'), $env{'form.sec_choice'}], ['ipwd', &mt('Initial Password'),$env{'form.ipwd_choice'}], ['email',&mt('E-mail Address'), $env{'form.email_choice'}], ['role',&mt('Role'), $env{'form.role_choice'}], ['domain',&mt('Domain'), $env{'form.domain_choice'}], ['inststatus',&mt('Affiliation'), $env{'form.inststatus_choice'}]); if ($showcredits) { push(@field, ['credits',&mt('Student Credits'), $env{'form.credits_choice'}]); } if ($env{'form.upfile_associate'} eq 'reverse') { &Apache::loncommon::csv_print_samples($r,\@records); $i=&Apache::loncommon::csv_print_select_table($r,\@records, \@field); foreach (@field) { $keyfields.=$_->[0].','; } chop($keyfields); } else { unshift(@field,['none','']); $i=&Apache::loncommon::csv_samples_select_table($r,\@records, \@field); my %sone=&Apache::loncommon::record_sep($records[0]); $keyfields=join(',',sort(keys(%sone))); } } &print_upload_manager_footer($r,$i,$keyfields,$defdom,$today,$halfyear, $context,$permission,$crstype,$showcredits); return 'ok'; } sub setup_date_selectors { my ($starttime,$endtime,$mode,$nolink,$formname) = @_; if ($formname eq '') { $formname = 'studentform'; } if (! defined($starttime)) { $starttime = time; unless ($mode eq 'create_enrolldates' || $mode eq 'create_defaultdates') { if (exists($env{'course.'.$env{'request.course.id'}. '.default_enrollment_start_date'})) { $starttime = $env{'course.'.$env{'request.course.id'}. '.default_enrollment_start_date'}; } } } if (! defined($endtime)) { $endtime = time+(6*30*24*60*60); # 6 months from now, approx unless ($mode eq 'createcourse') { if (exists($env{'course.'.$env{'request.course.id'}. '.default_enrollment_end_date'})) { $endtime = $env{'course.'.$env{'request.course.id'}. '.default_enrollment_end_date'}; } } } my $startdateform = &Apache::lonhtmlcommon::date_setter($formname,'startdate',$starttime, undef,undef,undef,undef,undef,undef,undef,$nolink); my $enddateform = &Apache::lonhtmlcommon::date_setter($formname,'enddate',$endtime, undef,undef,undef,undef,undef,undef,undef,$nolink); if ($mode eq 'create_enrolldates') { $startdateform = &Apache::lonhtmlcommon::date_setter('ccrs', 'startenroll', $starttime); $enddateform = &Apache::lonhtmlcommon::date_setter('ccrs', 'endenroll', $endtime); } if ($mode eq 'create_defaultdates') { $startdateform = &Apache::lonhtmlcommon::date_setter('ccrs', 'startaccess', $starttime); $enddateform = &Apache::lonhtmlcommon::date_setter('ccrs', 'endaccess', $endtime); } return ($startdateform,$enddateform); } sub get_dates_from_form { my ($startname,$endname) = @_; if ($startname eq '') { $startname = 'startdate'; } if ($endname eq '') { $endname = 'enddate'; } my $startdate = &Apache::lonhtmlcommon::get_date_from_form($startname); my $enddate = &Apache::lonhtmlcommon::get_date_from_form($endname); if ($env{'form.no_end_date'}) { $enddate = 0; } return ($startdate,$enddate); } sub date_setting_table { my ($starttime,$endtime,$mode,$bulkaction,$formname,$permission,$crstype) = @_; my $nolink; if ($bulkaction) { $nolink = 1; } my ($startform,$endform) = &setup_date_selectors($starttime,$endtime,$mode,$nolink,$formname); my $dateDefault; if ($mode eq 'create_enrolldates' || $mode eq 'create_defaultdates') { $dateDefault = ' '; } elsif ($mode ne 'author' && $mode ne 'domain') { if (($bulkaction eq 'reenable') || ($bulkaction eq 'activate') || ($bulkaction eq 'chgdates') || ($env{'form.action'} eq 'upload')) { if ($env{'request.course.sec'} eq '') { $dateDefault = ''. ''; } } } my $perpetual = ''; if ($mode eq 'create_enrolldates') { $perpetual = ' '; } my $result = &Apache::lonhtmlcommon::start_pick_box()."\n"; $result .= &Apache::lonhtmlcommon::row_title(&mt('Starting Date'), 'LC_oddrow_value')."\n". $startform."\n". &Apache::lonhtmlcommon::row_closure(1). &Apache::lonhtmlcommon::row_title(&mt('Ending Date'), 'LC_oddrow_value')."\n". $endform.' '.$perpetual. &Apache::lonhtmlcommon::row_closure(1). &Apache::lonhtmlcommon::end_pick_box(); if ($dateDefault) { $result .= $dateDefault.''. &list_submit_button(&mt('Update Display')). "\n".'
'.$warning.''."\n"); $clearcoursepick = 0; if (($env{'form.origroletype'} ne '') && ($env{'form.origroletype'} ne $env{'form.roletype'})) { $clearcoursepick = 1; } if (($env{'form.coursepick'}) && (!$clearcoursepick)) { $r->print(''.$msg.'
'."\n") if $msg; } else { # Print out the available choices my $usercount; if ($env{'form.action'} eq 'modifystudent') { ($usercount) = &show_users_list($r,$context,'view',$permission, $env{'form.Status'},\%userlist,$keylist,'', $showcredits); } else { ($usercount) = &show_users_list($r,$context,$env{'form.output'}, $permission,$env{'form.Status'},\%userlist, $keylist,'',$showcredits,$needauthorquota,$needauthorusage); } if (!$usercount) { $r->print('' .&mt('Problems occurred in writing the CSV file.') .' '.&mt('This error has been logged.') .' '.&mt('Please alert your LON-CAPA administrator.') .'
' ); $CSVfile = undef; } # # Write headers and data to file print $CSVfile '"'.$results_description.'"'."\n"; print $CSVfile '"'.join('","',map { &Apache::loncommon::csv_translate($lt{$_}) } (@cols))."\"\n"; } elsif ($mode eq 'excel') { # Create the excel spreadsheet ($excel_workbook,$excel_filename,$format) = &Apache::loncommon::create_workbook($r); return if (! defined($excel_workbook)); $excel_sheet = $excel_workbook->addworksheet('userlist'); $excel_sheet->write($row++,0,$results_description,$format->{'h2'}); # my @colnames = map {$lt{$_}} (@cols); $excel_sheet->write($row++,0,\@colnames,$format->{'bold'}); } # Done with header lines in all formats my %index; my $i; foreach my $idx (@$keylist) { $index{$idx} = $i++; } my $usercount = 0; my ($secfilter,$grpfilter); if ($context eq 'course') { $secfilter = $env{'form.secfilter'}; $grpfilter = $env{'form.grpfilter'}; if ($secfilter eq '') { $secfilter = 'all'; } if ($grpfilter eq '') { $grpfilter = 'all'; } } my %ltstatus = &Apache::lonlocal::texthash( Active => 'Active', Future => 'Future', Expired => 'Expired', ); # If this is for a single course get last course "log-in". my %crslogins; if ($context eq 'course') { %crslogins=&Apache::lonnet::dump('nohist_crslastlogin',$cdom,$cnum); } # Get groups, role, permanent e-mail so we can sort on them if # necessary. foreach my $user (keys(%{$userlist})) { if ($user eq '' ) { delete($userlist->{$user}); next; } if ($context eq 'domain' && $user eq $env{'request.role.domain'}.'-domainconfig:'.$env{'request.role.domain'}) { delete($userlist->{$user}); next; } my ($uname,$udom,$role,$groups,$email); if (($statusmode ne 'Any') && ($userlist->{$user}->[$index{'status'}] ne $statusmode)) { delete($userlist->{$user}); next; } if ($context eq 'domain') { if ($env{'form.roletype'} eq 'domain') { ($role,$uname,$udom) = split(/:/,$user); if (($uname eq $env{'request.role.domain'}.'-domainconfig') && ($udom eq $env{'request.role.domain'})) { delete($userlist->{$user}); next; } } elsif ($env{'form.roletype'} eq 'author') { ($uname,$udom,$role) = split(/:/,$user,-1); } elsif (($env{'form.roletype'} eq 'course') || ($env{'form.roletype'} eq 'community')) { ($uname,$udom,$role) = split(/:/,$user); } } else { ($uname,$udom,$role) = split(/:/,$user,-1); if (($context eq 'course') && $role eq '') { $role = 'st'; } } $userlist->{$user}->[$index{'role'}] = $role; if (($env{'form.showrole'} ne 'Any') && (!($env{'form.showrole'} eq 'cr' && $role =~ /^cr\//)) && ($role ne $env{'form.showrole'})) { delete($userlist->{$user}); next; } if ($context eq 'course') { my @ac_groups; if (ref($classgroups) eq 'HASH') { $groups = $classgroups->{$user}; } if (ref($groups->{'active'}) eq 'HASH') { @ac_groups = keys(%{$groups->{'active'}}); $userlist->{$user}->[$index{'groups'}] = join(', ',@ac_groups); } if ($mode ne 'autoenroll') { my $section = $userlist->{$user}->[$index{'section'}]; if (($env{'request.course.sec'} ne '') && ($section ne $env{'request.course.sec'})) { if ($role eq 'st') { delete($userlist->{$user}); next; } } if ($secfilter eq 'none') { if ($section ne '') { delete($userlist->{$user}); next; } } elsif ($secfilter ne 'all') { if ($section ne $secfilter) { delete($userlist->{$user}); next; } } if ($grpfilter eq 'none') { if (@ac_groups > 0) { delete($userlist->{$user}); next; } } elsif ($grpfilter ne 'all') { if (!grep(/^\Q$grpfilter\E$/,@ac_groups)) { delete($userlist->{$user}); next; } } if ($env{'course.'.$env{'request.course.id'}.'.internal.showphoto'}) { if ((grep/^photo$/,@cols) && ($role eq 'st')) { $userlist->{$user}->[$index{'photo'}] = &Apache::lonnet::retrievestudentphoto($udom,$uname,'jpg'); $userlist->{$user}->[$index{'thumbnail'}] = &Apache::lonnet::retrievestudentphoto($udom,$uname, 'gif','thumbnail'); } } if (($role eq 'st') && ($defaultcredits)) { if ($userlist->{$user}->[$index{'credits'}] eq '') { $userlist->{$user}->[$index{'credits'}] = $defaultcredits; } } } } my %emails = &Apache::loncommon::getemails($uname,$udom); if ($emails{'permanentemail'} =~ /\S/) { $userlist->{$user}->[$index{'email'}] = $emails{'permanentemail'}; } if (($context eq 'domain') && ($env{'form.roletype'} eq 'domain') && ($role eq 'au')) { my ($disk_quota,$current_disk_usage,$percent); if (($needauthorusage) || ($needauthorquota)) { $disk_quota = &Apache::loncommon::get_user_quota($uname,$udom,'author'); } if ($needauthorusage) { $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,"$londocroot/priv/$udom/$uname"); if ($disk_quota == 0) { $percent = 100.0; } else { $percent = $current_disk_usage/(10 * $disk_quota); } $userlist->{$user}->[$index{'authorusage'}] = sprintf("%.0f",$percent); } if ($needauthorquota) { $userlist->{$user}->[$index{'authorquota'}] = sprintf("%.2f",$disk_quota); } } $usercount ++; } my $autocount = 0; my $manualcount = 0; my $lockcount = 0; my $unlockcount = 0; if ($usercount) { $r->print($output); } else { if ($mode eq 'autoenroll') { return ($usercount,$autocount,$manualcount,$lockcount,$unlockcount); } else { return; } } # # Sort the users my $index = $index{$sortby}; my $second = $index{'username'}; my $third = $index{'domain'}; my @sorted_users; if (($sortby eq 'authorquota') || ($sortby eq 'authorusage')) { @sorted_users = sort { $userlist->{$b}->[$index] <=> $userlist->{$a}->[$index] || lc($userlist->{$a}->[$second]) cmp lc($userlist->{$b}->[$second]) || lc($userlist->{$a}->[$third]) cmp lc($userlist->{$b}->[$third]) } (keys(%$userlist)); } else { @sorted_users = sort { lc($userlist->{$a}->[$index]) cmp lc($userlist->{$b}->[$index]) || lc($userlist->{$a}->[$second]) cmp lc($userlist->{$b}->[$second]) || lc($userlist->{$a}->[$third]) cmp lc($userlist->{$b}->[$third]) } (keys(%$userlist)); } my $rowcount = 0; my $disabled; if ($mode eq 'autoenroll') { unless ($permission->{'cusr'}) { $disabled = ' disabled="disabled"'; } } foreach my $user (@sorted_users) { my %in; my $sdata = $userlist->{$user}; $rowcount ++; foreach my $item (@{$keylist}) { $in{$item} = $sdata->[$index{$item}]; } my $clickers = (&Apache::lonnet::userenvironment($in{'domain'},$in{'username'},'clickers'))[1]; if ($clickers!~/\w/) { $clickers='-'; } $in{'clicker'} = $clickers; my $role = $in{'role'}; $in{'role'}=&Apache::lonnet::plaintext($sdata->[$index{'role'}],$crstype); unless ($mode eq 'excel') { if (! defined($in{'start'}) || $in{'start'} == 0) { $in{'start'} = &mt('none'); } else { $in{'start'} = &Apache::lonlocal::locallocaltime($in{'start'}); } if (! defined($in{'end'}) || $in{'end'} == 0) { $in{'end'} = &mt('none'); } else { $in{'end'} = &Apache::lonlocal::locallocaltime($in{'end'}); } } if ($context eq 'course') { my $lastlogin = $crslogins{$in{'username'}.':'.$in{'domain'}.':'.$in{'section'}.':'.$role}; if ($lastlogin ne '') { $in{'lastlogin'} = &Apache::lonlocal::locallocaltime($lastlogin); } } if ($mode eq 'view' || $mode eq 'html' || $mode eq 'autoenroll' || $mode eq 'pickauthor') { $r->print(&Apache::loncommon::start_data_table_row()); my $checkval; if ($mode eq 'autoenroll') { my $cellentry; if ($in{'type'} eq 'auto') { $cellentry = ''.&mt('auto').' '; $autocount ++; } else { $cellentry = ''.&mt('manual').' | |
'; $manualcount ++; if ($in{'lockedtype'}) { $cellentry .= ''; $unlockcount ++; } else { $cellentry .= ''; $lockcount ++; } $cellentry .= ' |
'.&mt('[_1]Your Excel spreadsheet[_2] is ready for download.', '','')."
\n"); } elsif ($mode eq 'csv') { close($CSVfile); $r->print(''.&mt('[_1]Your CSV file[_2] is ready for download.', '','')."
\n"); $r->rflush(); } if ($mode eq 'autoenroll') { return ($usercount,$autocount,$manualcount,$lockcount,$unlockcount); } else { return ($usercount); } } sub bulkaction_javascript { my ($formname,$caller) = @_; my $docstart = 'document'; if ($caller eq 'popup') { $docstart = 'opener.document'; } my %lt = &Apache::lonlocal::texthash( acwi => 'Access will be set to start immediately', asyo => 'as you did not select an end date in the pop-up window', accw => 'Access will be set to continue indefinitely', asyd => 'as you did not select an end date in the pop-up window', sewi => "Sections will be switched to 'No section'", ayes => "as you either selected the 'No section' option", oryo => 'or you did not select a section in the pop-up window', arol => 'A role with no section will be added', swbs => 'Sections will be switched to:', rwba => 'Roles will be added for section(s):', ); my $alert = &mt("You must select at least one user by checking a user's 'Select' checkbox"); my $noaction = &mt("You need to select an action to take for the user(s) you have selected"); my $singconfirm = &mt(' for a single user?'); my $multconfirm = &mt(' for multiple users?'); &js_escape(\$alert); &js_escape(\$noaction); &js_escape(\$singconfirm); &js_escape(\$multconfirm); my $output = <<"ENDJS"; function verify_action (field) { var numchecked = 0; var singconf = '$singconfirm'; var multconf = '$multconfirm'; if ($docstart.$formname.elements[field].length > 0) { for (i=0; i<$docstart.$formname.elements[field].length; i++) { if ($docstart.$formname.elements[field][i].checked == true) { numchecked ++; } } } else { if ($docstart.$formname.elements[field].checked == true) { numchecked ++; } } if (numchecked == 0) { alert("$alert"); return; } else { var message = $docstart.$formname.bulkaction[$docstart.$formname.bulkaction.selectedIndex].text; var choice = $docstart.$formname.bulkaction[$docstart.$formname.bulkaction.selectedIndex].value; if (choice == '') { alert("$noaction"); return; } else { if (numchecked == 1) { message += singconf; } else { message += multconf; } ENDJS if ($caller ne 'popup') { $output .= <<"NEWWIN"; if (choice == 'chgdates' || choice == 'reenable' || choice == 'activate' || choice == 'chgsec') { opendatebrowser(document.$formname,'$formname','go'); return; } else { if (confirm(message)) { document.$formname.phase.value = 'bulkchange'; document.$formname.submit(); return; } } NEWWIN } else { $output .= <<"POPUP"; if (choice == 'chgdates' || choice == 'reenable' || choice == 'activate') { var datemsg = ''; if (($docstart.$formname.startdate_month.value == '') && ($docstart.$formname.startdate_day.value == '') && ($docstart.$formname.startdate_year.value == '')) { datemsg = "\\n$lt{'acwi'},\\n$lt{'asyo'}.\\n"; } if (($docstart.$formname.enddate_month.value == '') && ($docstart.$formname.enddate_day.value == '') && ($docstart.$formname.enddate_year.value == '')) { datemsg += "\\n$lt{'accw'},\\n$lt{'asyd'}.\\n"; } if (datemsg != '') { message += "\\n"+datemsg; } } if (choice == 'chgsec') { var rolefilter = $docstart.$formname.showrole.options[$docstart.$formname.showrole.selectedIndex].value; var retained = $docstart.$formname.retainsec.value; var secshow = $docstart.$formname.newsecs.value; if (secshow == '') { if (rolefilter == 'st' || retained == 0 || retained == "") { message += "\\n\\n$lt{'sewi'},\\n$lt{'ayes'},\\n$lt{'oryo'}.\\n"; } else { message += "\\n\\n$lt{'arol'}\\n$lt{'ayes'},\\n$lt{'oryo'}.\\n"; } } else { if (rolefilter == 'st' || retained == 0 || retained == "") { message += "\\n\\n$lt{'swbs'} "+secshow+".\\n"; } else { message += "\\n\\n$lt{'rwba'} "+secshow+".\\n"; } } } if (confirm(message)) { $docstart.$formname.phase.value = 'bulkchange'; $docstart.$formname.submit(); window.close(); } POPUP } $output .= ' } } } '; return $output; } sub print_username_link { my ($mode,$in) = @_; my $output; if ($mode eq 'autoenroll') { $output = $in->{'username'}; } else { $output = '{'username'}','$in->{'domain'}'".')">'. $in->{'username'}.''; } return $output; } sub role_type_names { my %lt = &Apache::lonlocal::texthash ( 'domain' => 'Domain Roles', 'author' => 'Co-Author Roles', 'course' => 'Course Roles', 'community' => 'Community Roles', ); return %lt; } sub select_actions { my ($context,$setting,$statusmode,$formname) = @_; my %lt = &Apache::lonlocal::texthash( revoke => "Revoke user roles", delete => "Delete user roles", reenable => "Re-enable expired user roles", activate => "Make future user roles active now", chgdates => "Change starting/ending dates", chgsec => "Change section associated with user roles", ); # FIXME Add an option to change credits for student roles. my ($output,$options,%choices); # FIXME Disable actions for now for roletype=course in domain context if ($context eq 'domain' && $setting eq 'course') { return; } if ($context eq 'course') { if ($env{'form.showrole'} ne 'Any') { my $showactions; if (&Apache::lonnet::allowed('c'.$env{'form.showrole'}, $env{'request.course.id'})) { $showactions = 1; } elsif ($env{'request.course.sec'} ne '') { if (&Apache::lonnet::allowed('c'.$env{'form.showrole'},$env{'request.course.id'}.'/'.$env{'request.course.sec'})) { $showactions = 1; } } unless ($showactions) { unless (&is_courseowner($env{'request.course.id'}, $env{'course.'.$env{'request.course.id'}.'.internal.courseowner'})) { return; } } } } if ($statusmode eq 'Any') { $options .= ' '; $choices{'dates'} = 1; } else { if ($statusmode eq 'Future') { $options .= ' '; $choices{'dates'} = 1; } elsif ($statusmode eq 'Expired') { $options .= ' '; $choices{'dates'} = 1; } if ($statusmode eq 'Active' || $statusmode eq 'Future') { $options .= ' '; $choices{'dates'} = 1; } } if ($context eq 'domain') { $options .= ' '; } if (($context eq 'course') || ($context eq 'domain' && $setting eq 'course')) { if (($statusmode ne 'Expired') && ($env{'request.course.sec'} eq '')) { $options .= ' '; $choices{'sections'} = 1; } } if ($options) { $output = ''.&mt('Existing sections')."\n".
' '.$sections_select.' | '.
&mt('New section').' '."\n". ''."\n". ''."\n". ' |
'.$msg.'
'); return; } my ($classgroups) = &Apache::loncoursedata::get_group_memberships( $classlist,$keylist,$cdom,$cnum); my %lt=&Apache::lonlocal::texthash('usrn' => "username", 'dom' => "domain", 'id' => "ID", 'sn' => "student name", 'mn' => "member name", 'sec' => "section", 'start' => "start date", 'end' => "end date", 'groups' => "active groups", ); my $nametitle = $lt{'sn'}; if ($crstype eq 'Community') { $nametitle = $lt{'mn'}; } if ($nosort) { $r->print(&Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row()); $r->print(<
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 .= &Apache::grades::checkforfile_js(); $str .= '
'."\n" .&mt('Please upload an UTF8 encoded file to ensure a correct character encoding in your classlist.')."\n" .'
'."\n"; } } $str .= &Apache::loncommon::upfile_select_html() .&Apache::lonhtmlcommon::row_closure() .&Apache::lonhtmlcommon::row_title( '') .'' .&Apache::lonhtmlcommon::row_closure(1) .&Apache::lonhtmlcommon::end_pick_box(); $str .= '' .'' .'
'; $r->print($str); return; } # ================================================= Drop/Add from uploaded file sub upfile_drop_add { my ($r,$context,$permission,$showcredits) = @_; my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'}); if ($datatoken ne '') { &Apache::loncommon::load_tmp_file($r,$datatoken); } my @userdata=&Apache::loncommon::upfile_record_sep(); if($env{'form.noFirstLine'}){shift(@userdata);} my @keyfields = split(/\,/,$env{'form.keyfields'}); my %fields=(); for (my $i=0; $i<=$env{'form.nfields'}; $i++) { if ($env{'form.upfile_associate'} eq 'reverse') { if ($env{'form.f'.$i} ne 'none') { $fields{$keyfields[$i]}=$env{'form.f'.$i}; } } else { $fields{$env{'form.f'.$i}}=$keyfields[$i]; } } # # Store the field choices away my @storefields = qw/username names fname mname lname gen id sec ipwd email role domain inststatus/; if ($showcredits) { push (@storefields,'credits'); } my %fieldstype; foreach my $field (@storefields) { $env{'form.'.$field.'_choice'}=$fields{$field}; $fieldstype{$field.'_choice'} = 'scalar'; } &Apache::loncommon::store_course_settings('enrollment_upload',\%fieldstype); my ($cid,$crstype,$setting,$crsdom); if ($context eq 'domain') { $setting = $env{'form.roleaction'}; } if ($env{'request.course.id'} ne '') { $cid = $env{'request.course.id'}; $crstype = &Apache::loncommon::course_type(); $crsdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; } elsif ($setting eq 'course') { if (&Apache::lonnet::is_course($env{'form.dcdomain'},$env{'form.dccourse'})) { $cid = $env{'form.dcdomain'}.'_'.$env{'form.dccourse'}; $crstype = &Apache::loncommon::course_type($cid); $crsdom = $env{'form.dcdomain'}; } } my ($startdate,$enddate) = &get_dates_from_form(); if ($env{'form.makedatesdefault'}) { $r->print(&make_dates_default($startdate,$enddate,$context,$crstype)); } # Determine domain and desired host (home server) my $defdom=$env{'request.role.domain'}; my $domain; if ($env{'form.defaultdomain'} ne '') { if (($context eq 'course') || ($setting eq 'course')) { if ($env{'form.defaultdomain'} eq $crsdom) { $domain = $env{'form.defaultdomain'}; } else { if (&Apache::lonnet::will_trust('enroll',$crsdom,$env{'form.defaultdomain'})) { $domain = $env{'form.defaultdomain'}; } else { $r->print(''.&mt('Error').': '. &mt('Enrollment of users not permitted for specified default domain: [_1].', &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).''); return 'untrusted'; } } } elsif ($context eq 'author') { if ($env{'form.defaultdomain'} eq $defdom) { $domain = $env{'form.defaultdomain'}; } else { if ((&Apache::lonnet::will_trust('othcoau',$defdom,$env{'form.defaultdomain'})) && (&Apache::lonnet::will_trust('coaurem',$env{'form.defaultdomain'},$defdom))) { $domain = $env{'form.defaultdomain'}; } else { $r->print(''.&mt('Error').': '. &mt('Addition of users not permitted for specified default domain: [_1].', &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).''); return 'untrusted'; } } } elsif (($context eq 'domain') && ($setting eq 'domain')) { if ($env{'form.defaultdomain'} eq $defdom) { $domain = $env{'form.defaultdomain'}; } else { if (&Apache::lonnet::will_trust('domroles',$defdom,$env{'form.defaultdomain'})) { $domain = $env{'form.defaultdomain'}; } else { $r->print(''.&mt('Error').': '. &mt('Addition of users not permitted for specified default domain: [_1].', &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).''); return 'untrusted'; } } } } else { $domain = $defdom; } my $desiredhost = $env{'form.lcserver'}; if (lc($desiredhost) eq 'default') { $desiredhost = undef; } else { my %home_servers = &Apache::lonnet::get_servers($defdom,'library'); if (! exists($home_servers{$desiredhost})) { $r->print(''.&mt('Error').': '. &mt('Invalid home server specified').'
'); return 'invalidhome'; } } # Determine authentication mechanism my $changeauth; if ($context eq 'domain') { $changeauth = $env{'form.changeauth'}; } my $amode = ''; my $genpwd = ''; my @genpwdfail; if ($env{'form.login'} eq 'krb') { $amode='krb'; $amode.=$env{'form.krbver'}; $genpwd=$env{'form.krbarg'}; } elsif ($env{'form.login'} eq 'int') { $amode='internal'; if ((defined($env{'form.intarg'})) && ($env{'form.intarg'})) { $genpwd=$env{'form.intarg'}; @genpwdfail = &Apache::loncommon::check_passwd_rules($domain,$genpwd); } } elsif ($env{'form.login'} eq 'loc') { $amode='localauth'; if ((defined($env{'form.locarg'})) && ($env{'form.locarg'})) { $genpwd=$env{'form.locarg'}; } } elsif ($env{'form.login'} eq 'lti') { $amode='lti'; } if ($amode =~ /^krb/) { if (! defined($genpwd) || $genpwd eq '') { $r->print(''. &mt('Unable to enroll users').' '. &mt('No Kerberos domain was specified.').''); $amode = ''; # This causes the loop below to be skipped } } my ($defaultsec,$defaultrole,$defaultcredits,$commoncredits); if ($context eq 'domain') { if ($setting eq 'domain') { $defaultrole = $env{'form.defaultrole'}; } elsif ($setting eq 'course') { $defaultrole = $env{'form.courserole'}; $defaultsec = $env{'form.sections'}; if ($showcredits) { $commoncredits = $env{'form.credits'}; if ($crstype ne 'Community') { my %coursehash=&Apache::lonnet::coursedescription($cid); $defaultcredits = $coursehash{'internal.defaultcredits'}; } } } } elsif ($context eq 'author') { $defaultrole = $env{'form.defaultrole'}; } elsif ($context eq 'course') { $defaultrole = $env{'form.defaultrole'}; $defaultsec = $env{'form.sections'}; if ($showcredits) { $commoncredits = $env{'form.credits'}; $defaultcredits = $env{'course.'.$cid.'.internal.defaultcredits'}; } } # Check to see if user information can be changed my @userinfo = ('firstname','middlename','lastname','generation', 'permanentemail','id'); my %canmodify; if (&Apache::lonnet::allowed('mau',$domain)) { push(@userinfo,'inststatus'); foreach my $field (@userinfo) { $canmodify{$field} = 1; } } my (%userlist,%modifiable_fields,@poss_roles); my $secidx = &Apache::loncoursedata::CL_SECTION(); my @courseroles = &roles_by_context('course',1,$crstype); if (!&Apache::lonnet::allowed('mau',$domain)) { if ($context eq 'course' || $context eq 'author') { @poss_roles = &curr_role_permissions($context,'','',$crstype); my @statuses = ('active','future'); my ($indexhash,$keylist) = &make_keylist_array(); my %info; foreach my $role (@poss_roles) { %{$modifiable_fields{$role}} = &can_modify_userinfo($context,$domain, \@userinfo,[$role]); } if ($context eq 'course') { my ($cnum,$cdom) = &get_course_identity(); my $roster = &Apache::loncoursedata::get_classlist(); if (ref($roster) eq 'HASH') { %userlist = %{$roster}; } my %advrolehash = &Apache::lonnet::get_my_roles($cnum,$cdom,undef, \@statuses,\@poss_roles); &gather_userinfo($context,'view',\%userlist,$indexhash,\%info, \%advrolehash,$permission); } elsif ($context eq 'author') { my %cstr_roles = &Apache::lonnet::get_my_roles(undef,undef,undef, \@statuses,\@poss_roles); &gather_userinfo($context,'view',\%userlist,$indexhash,\%info, \%cstr_roles,$permission); } } } if ($datatoken eq '') { $r->print(''.&mt('Error').': '. &mt('Invalid datatoken').'
'); return 'missingdata'; } if ( $domain eq &LONCAPA::clean_domain($domain) && ($amode ne '')) { ####################################### ## Add/Modify Users ## ####################################### if ($context eq 'course') { $r->print('\n"); } elsif ($context eq 'author') { $r->print('
\n"); } else { $r->print('
\n");
}
$r->rflush;
my %counts = (
user => 0,
auth => 0,
role => 0,
);
my $flushc=0;
my %student=();
my (%curr_groups,@sections,@cleansec,$defaultwarn,$groupwarn);
my %userchg;
if ($context eq 'course' || $setting eq 'course') {
if ($context eq 'course') {
# Get information about course groups
%curr_groups = &Apache::longroup::coursegroups();
} elsif ($setting eq 'course') {
if ($cid) {
%curr_groups =
&Apache::longroup::coursegroups($env{'form.dcdomain'},
$env{'form.dccourse'});
}
}
# determine section number
if ($defaultsec =~ /,/) {
push(@sections,split(/,/,$defaultsec));
} else {
push(@sections,$defaultsec);
}
# remove non alphanumeric values from section
foreach my $item (@sections) {
$item =~ s/\W//g;
if ($item eq "none" || $item eq 'all') {
$defaultwarn = &mt('Default section name [_1] could not be used as it is a reserved word.',$item);
} elsif ($item ne '' && exists($curr_groups{$item})) {
$groupwarn = &mt('Default section name "[_1]" is the name of a course group. Section names and group names must be distinct.',$item);
} elsif ($item ne '') {
push(@cleansec,$item);
}
}
if ($defaultwarn) {
$r->print($defaultwarn.'
');
}
if ($groupwarn) {
$r->print($groupwarn.'
');
}
}
my (%curr_rules,%got_rules,%alerts,%cancreate);
my %customroles = &my_custom_roles($crstype);
my @permitted_roles =
&roles_on_upload($context,$setting,$crstype,%customroles);
my %longtypes = &Apache::lonlocal::texthash(
official => 'Institutional',
unofficial => 'Non-institutional',
);
my $newuserdom = $env{'request.role.domain'};
map { $cancreate{$_} = &can_create_user($newuserdom,$context,$_); } keys(%longtypes);
# Get new users list
my (%existinguser,%userinfo,%disallow,%rulematch,%inst_results,%alerts,%checkuname,
%showpasswdrules,$haspasswdmap);
my $counter = -1;
my (%willtrust,%trustchecked);
foreach my $line (@userdata) {
$counter ++;
my @secs;
my %entries=&Apache::loncommon::record_sep($line);
# Determine user name
$entries{$fields{'username'}} =~ s/^\s+|\s+$//g;
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'}})) {
my $nowhitespace;
if ($entries{$fields{'username'}} =~ /\s/) {
$nowhitespace = ' - '.&mt('usernames may not contain spaces.');
}
$disallow{$counter} =
&mt('Unacceptable username [_1] for user [_2] [_3] [_4] [_5]',
'"'.$entries{$fields{'username'}}.'"',
$fname,$mname,$lname,$gen).$nowhitespace;
next;
} else {
$entries{$fields{'domain'}} =~ s/^\s+|\s+$//g;
if ($entries{$fields{'domain'}}
ne &LONCAPA::clean_domain($entries{$fields{'domain'}})) {
$disallow{$counter} =
&mt('Unacceptable domain [_1] for user [_2] [_3] [_4] [_5]',
'"'.$entries{$fields{'domain'}}.'"',
$fname,$mname,$lname,$gen);
next;
} elsif ($entries{$fields{'domain'}} ne $domain) {
my $possdom = $entries{$fields{'domain'}};
if ($context eq 'course' || $setting eq 'course') {
unless ($trustchecked{$possdom}) {
$willtrust{$possdom} = &Apache::lonnet::will_trust('enroll',$domain,$possdom);
$trustchecked{$possdom} = 1;
}
} elsif ($context eq 'author') {
unless ($trustchecked{$possdom}) {
$willtrust{$possdom} = &Apache::lonnet::will_trust('othcoau',$domain,$possdom);
}
if ($willtrust{$possdom}) {
$willtrust{$possdom} = &Apache::lonnet::will_trust('coaurem',$possdom,$domain);
}
}
unless ($willtrust{$possdom}) {
$disallow{$counter} =
&mt('Unacceptable domain [_1] for user [_2] [_3] [_4] [_5]',
'"'.$possdom.'"',
$fname,$mname,$lname,$gen);
next;
}
}
my $username = $entries{$fields{'username'}};
my $userdomain = $entries{$fields{'domain'}};
if ($userdomain eq '') {
$userdomain = $domain;
}
if (defined($fields{'sec'})) {
if (defined($entries{$fields{'sec'}})) {
$entries{$fields{'sec'}} =~ s/\W//g;
my $item = $entries{$fields{'sec'}};
if ($item eq "none" || $item eq 'all') {
$disallow{$counter} =
&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]" - this is a reserved word.',
''.$username.'',$fname,$mname,$lname,$gen,$item);
next;
} elsif (exists($curr_groups{$item})) {
$disallow{$counter} =
&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]" - this is a course group.',
''.$username.'',$fname,$mname,$lname,$gen,$item).' '.
&mt('Section names and group names must be distinct.');
next;
} else {
push(@secs,$item);
}
}
}
if ($env{'request.course.sec'} ne '') {
@secs = ($env{'request.course.sec'});
if (ref($userlist{$username.':'.$userdomain}) eq 'ARRAY') {
my $currsec = $userlist{$username.':'.$userdomain}[$secidx];
if ($currsec ne $env{'request.course.sec'}) {
$disallow{$counter} =
&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]".',
''.$username.'',$fname,$mname,$lname,$gen,$secs[0]);
if ($currsec eq '') {
$disallow{$counter} .=
&mt('This user already has an active/future student role in the course, unaffiliated to any section.');
} else {
$disallow{$counter} .=
&mt('This user already has an active/future role in section "[_1]" of the course.',$currsec);
}
$disallow{$counter} .=
'
'.
&mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments in other sections.',
$secs[0]);
next;
}
}
} elsif ($context eq 'course' || $setting eq 'course') {
if (@secs == 0) {
@secs = @cleansec;
}
}
# 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'})) {
$entries{$fields{'email'}} =~ s/^\s+|\s+$//g;
if (defined($entries{$fields{'email'}})) {
$email=$entries{$fields{'email'}};
unless ($email=~/^[^\@]+\@[^\@]+$/) { $email=''; }
}
}
# determine affiliation
my $inststatus='';
if (defined($fields{'inststatus'})) {
if (defined($entries{$fields{'inststatus'}})) {
$inststatus=$entries{$fields{'inststatus'}};
}
}
# determine user password
my $password;
my $passwdfromfile;
if (defined($fields{'ipwd'})) {
if ($entries{$fields{'ipwd'}}) {
$password=$entries{$fields{'ipwd'}};
$passwdfromfile = 1;
if ($env{'form.login'} eq 'int') {
my $uhome=&Apache::lonnet::homeserver($username,$userdomain);
if (($uhome eq 'no_host') || ($changeauth)) {
my @brokepwdrules =
&Apache::loncommon::check_passwd_rules($domain,$password);
if (@brokepwdrules) {
$disallow{$counter} = &mt('[_1]: Password included in file for this user did not meet requirements.',
''.$username.'');
map { $showpasswdrules{$_} = 1; } @brokepwdrules;
next;
}
}
}
}
}
unless ($passwdfromfile) {
if ($env{'form.login'} eq 'int') {
if (@genpwdfail) {
my $uhome=&Apache::lonnet::homeserver($username,$userdomain);
if (($uhome eq 'no_host') || ($changeauth)) {
$disallow{$counter} = &mt('[_1]: No specific password in file for this user; default password did not meet requirements',
''.$username.'');
unless ($haspasswdmap) {
map { $showpasswdrules{$_} = 1; } @genpwdfail;
$haspasswdmap = 1;
}
}
next;
}
}
$password = $genpwd;
}
# determine user role
my $role = '';
if (defined($fields{'role'})) {
if ($entries{$fields{'role'}}) {
$entries{$fields{'role'}} =~ s/(\s+$|^\s+)//g;
if ($entries{$fields{'role'}} ne '') {
if (grep(/^\Q$entries{$fields{'role'}}\E$/,@permitted_roles)) {
$role = $entries{$fields{'role'}};
}
}
if ($role eq '') {
my $rolestr = join(', ',@permitted_roles);
$disallow{$counter} =
&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);
next;
}
}
}
if ($role eq '') {
$role = $defaultrole;
}
# Clean up whitespace
foreach (\$id,\$fname,\$mname,\$lname,\$gen,\$inststatus) {
$$_ =~ s/(\s+$|^\s+)//g;
}
my $credits;
if ($showcredits) {
if (($role eq 'st') && ($crstype ne 'Community')) {
$credits = $entries{$fields{'credits'}};
if ($credits ne '') {
$credits =~ s/[^\d\.]//g;
}
if ($credits eq '') {
$credits = $commoncredits;
}
if ($credits eq $defaultcredits) {
undef($credits);
}
}
}
# check against rules
my $checkid = 0;
my $newuser = 0;
my $uhome=&Apache::lonnet::homeserver($username,$userdomain);
if ($uhome eq 'no_host') {
if ($userdomain ne $newuserdom) {
if ($context eq 'course') {
$disallow{$counter} =
&mt('[_1]: The domain specified ([_2]) is different to that of the course.',
''.$username.'',$userdomain);
} elsif ($context eq 'author') {
$disallow{$counter} =
&mt('[_1]: The domain specified ([_2]) is different to that of the author.',
''.$username.'',$userdomain);
} else {
$disallow{$counter} =
&mt('[_1]: The domain specified ([_2]) is different to that of your current role.',
''.$username.'',$userdomain);
}
$disallow{$counter} .=
&mt('The user does not already exist, and you may not create a new user in a different domain.');
next;
} else {
unless (($password ne '') || ($env{'form.login'} eq 'loc') || ($env{'form.login'} eq 'lti')) {
$disallow{$counter} =
&mt('[_1]: This is a new user but no default password was provided, and the authentication type requires one.',
''.$username.'');
next;
}
}
$checkid = 1;
$newuser = 1;
$checkuname{$username.':'.$newuserdom} = { 'newuser' => $newuser, 'id' => $id };
} else {
if ($context eq 'course' || $context eq 'author') {
if ($userdomain eq $domain ) {
if ($role eq '') {
my @checkroles;
foreach my $role (@poss_roles) {
my $endkey;
if ($role ne 'st') {
$endkey = ':'.$role;
}
if (exists($userlist{$username.':'.$userdomain.$endkey})) {
if (!grep(/^\Q$role\E$/,@checkroles)) {
push(@checkroles,$role);
}
}
}
if (@checkroles > 0) {
%canmodify = &can_modify_userinfo($context,$domain,\@userinfo,\@checkroles);
}
} elsif (ref($modifiable_fields{$role}) eq 'HASH') {
%canmodify = %{$modifiable_fields{$role}};
}
}
my @newinfo = (\$fname,\$mname,\$lname,\$gen,\$email,\$id);
for (my $i=0; $i<@newinfo; $i++) {
if (${$newinfo[$i]} ne '') {
if (!$canmodify{$userinfo[$i]}) {
${$newinfo[$i]} = '';
}
}
}
}
if ($id) {
$existinguser{$userdomain}{$username} = $id;
}
}
$userinfo{$counter} = {
username => $username,
domain => $userdomain,
fname => $fname,
mname => $mname,
lname => $lname,
gen => $gen,
email => $email,
id => $id,
password => $password,
inststatus => $inststatus,
role => $role,
sections => \@secs,
credits => $credits,
newuser => $newuser,
checkid => $checkid,
};
}
}
} # end of foreach (@userdata)
if ($counter > -1) {
my $total = $counter + 1;
my %checkids;
if ((keys(%existinguser)) || (keys(%checkuname))) {
$r->print(&mt('Please be patient -- checking for institutional data ...'));
$r->rflush();
if (keys(%existinguser)) {
foreach my $dom (keys(%existinguser)) {
if (ref($existinguser{$dom}) eq 'HASH') {
my %idhash = &Apache::lonnet::idrget($dom,keys(%{$existinguser{$dom}}));
foreach my $username (keys(%{$existinguser{$dom}})) {
if ($idhash{$username} ne $existinguser{$dom}{$username}) {
$checkids{$username.':'.$dom} = {
'id' => $existinguser{$dom}{$username},
};
}
}
if (keys(%checkids)) {
&Apache::loncommon::user_rule_check(\%checkids,{ 'id' => 1 },
\%alerts,\%rulematch,
\%inst_results,\%curr_rules,
\%got_rules);
}
}
}
}
if (keys(%checkuname)) {
&Apache::loncommon::user_rule_check(\%checkuname,{ 'username' => 1, 'id' => 1, },
\%alerts,\%rulematch,\%inst_results,
\%curr_rules,\%got_rules);
}
$r->print(' '.&mt('done').'
');
$r->rflush();
}
my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($r,$total);
$r->print('
\n".&mt('Processed [quant,_1,user].',$counts{'user'}). "
\n"); if ($counts{'role'} > 0) { $r->print("\n". &mt('Roles added for [quant,_1,user].',$counts{'role'}).' '. &mt('If a user is currently logged-in to LON-CAPA, any new roles which are active will be available when the user next logs in.'). "
\n"); } else { $r->print(''.&mt('No roles added').'
'); } if ($counts{'auth'} > 0) { $r->print("\n". &mt('Authentication changed for [_1] existing users.', $counts{'auth'})."
\n"); } $r->print(&print_namespacing_alerts($domain,\%alerts,\%curr_rules)); $r->print(&passwdrule_alerts($domain,\%showpasswdrules)); ##################################### # Display list of students to drop # ##################################### if ($env{'form.fullup'} eq 'yes') { $r->print(''. &mt('There are no students with current/future access to the course.'). '
'."\n"); } elsif (ref($classlist) eq 'HASH') { # Remove the students we just added from the list of students. foreach my $line (@userdata) { my %entries=&Apache::loncommon::record_sep($line); 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,'nosort',$permission); } } } # end of unless return 'ok'; } sub print_namespacing_alerts { my ($domain,$alerts,$curr_rules) = @_; my $output; if (ref($alerts) eq 'HASH') { 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') { $output .= &Apache::loncommon::instrule_disallow_msg( 'username',$domdesc,$count,'upload'); } $output .= &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') { $output .= &Apache::loncommon::instrule_disallow_msg( 'id',$domdesc,$count,'upload'); } $output .= &Apache::loncommon::user_rule_formats($dom, $domdesc,$curr_rules->{$dom}{'id'},'id'); } } } } } sub passwdrule_alerts { my ($domain,$passwdrules) = @_; my $warning; if (ref($passwdrules) eq 'HASH') { my %showrules = %{$passwdrules}; if (keys(%showrules)) { my %passwdconf = &Apache::lonnet::get_passwdconf($domain); $warning = ''.&mt('Password requirement(s) unmet for one or more users:').'The server encountered an internal error or misconfiguration and was unable to complete your request.
Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.
More information about this error may be available in the server error log.