# The LearningOnline Network with CAPA
# Utility functions for managing LON-CAPA user accounts
#
# $Id: lonuserutils.pm,v 1.207 2020/07/08 14:26:37 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:').'