--- loncom/interface/lonuserutils.pm 2023/11/04 00:06:00 1.219
+++ loncom/interface/lonuserutils.pm 2025/01/13 01:00:58 1.225
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Utility functions for managing LON-CAPA user accounts
#
-# $Id: lonuserutils.pm,v 1.219 2023/11/04 00:06:00 raeburn Exp $
+# $Id: lonuserutils.pm,v 1.225 2025/01/13 01:00:58 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -104,7 +104,7 @@ sub modifystudent {
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) = @_;
+ $end,$start,$checkid,$inststatus,$emptyok) = @_;
my ($scope,$userresult,$authresult,$roleresult,$idresult);
if ($setting eq 'course' || $context eq 'course') {
$scope = '/'.$cid;
@@ -139,6 +139,25 @@ sub modifyuserrole {
generation => $gene,
id => $uid,
);
+
+ # When "Update ID in user's course(s)" and "Force change of existing ID"
+ # checkboxes both checked, prevent replacement of name information
+ # in classlist.db file(s) for the user's course(s) with blank(s),
+ # in the case where the uploaded csv file was without column(s) for
+ # the particular field. Fields are: First Name, Middle Names/Initials,
+ # Last Name (or the composite: Last Name, First Names), and Generation.
+
+ my %emptyallowed;
+ if ((ref($emptyok) eq 'HASH') && (keys(%{$emptyok}) > 0)) {
+ %emptyallowed = %{$emptyok};
+ }
+ foreach my $field (keys(%userupdate)) {
+ if ($userupdate{$field} eq '') {
+ unless ($emptyallowed{$field}) {
+ delete($userupdate{$field});
+ }
+ }
+ }
$idresult = &propagate_id_change($uname,$udom,\%userupdate);
}
}
@@ -2164,6 +2183,24 @@ sub print_userlist {
if (! exists($env{'form.sortby'})) {
$env{'form.sortby'} = 'username';
}
+ my ($showstart,$showend);
+ if (($env{'form.Status'} eq '') && ($env{'form.phase'} eq '') &&
+ ($env{'form.showrole'} eq '') && ($context eq 'course') &&
+ ($env{'request.course.id'} ne '') &&
+ ($env{'course.'.$env{'request.course.id'}.'.internal.coursecode'} ne '')) {
+ my $now = time;
+ my $startaccess = $env{'course.'.$env{'request.course.id'}.'.default_enrollment_start_date'};
+ my $endaccess = $env{'course.'.$env{'request.course.id'}.'.default_enrollment_end_date'};
+ if (($startaccess) && ($startaccess > $now)) {
+ $env{'form.Status'} = 'Future';
+ $showstart = 1;
+ }
+ if (($endaccess ne '') && ($endaccess != 0) && ($endaccess < $now)) {
+ $env{'form.Status'} = 'Expired';
+ undef($showstart);
+ $showend = 1;
+ }
+ }
if ($env{'form.Status'} !~ /^(Any|Expired|Active|Future)$/) {
$env{'form.Status'} = 'Active';
}
@@ -2231,7 +2268,7 @@ sub print_userlist {
$r->print(§ion_group_filter($cnum,$cdom));
}
$r->print('
'.
- &column_checkboxes($context,$mode,$formname,$showcredits).
+ &column_checkboxes($context,$mode,$formname,$showcredits,$showstart,$showend).
'
');
if ($env{'form.phase'} eq '') {
$r->print('
'.
@@ -2642,7 +2679,7 @@ sub get_cols_array {
}
sub column_checkboxes {
- my ($context,$mode,$formname,$showcredits) = @_;
+ my ($context,$mode,$formname,$showcredits,$showstart,$showend) = @_;
my @cols = &get_cols_array($context,$mode,$showcredits);
my @showncols = &Apache::loncommon::get_env_multiple('form.showcol');
my (%disabledchk,%unchecked);
@@ -2655,11 +2692,21 @@ sub column_checkboxes {
if ($showcredits) {
$unchecked{'credits'} = 1;
}
- } elsif ($context eq 'domain') {
+ my %curr_groups = &Apache::longroup::coursegroups();
+ unless (keys(%curr_groups)) {
+ $unchecked{'groups'} = 1;
+ }
+ } elsif ($context eq 'domain') {
$unchecked{'extent'} = 1;
}
- $unchecked{'start'} = 1;
- $unchecked{'end'} = 1;
+ if ($showstart) {
+ $unchecked{'lastlogin'} = 1;
+ } else {
+ $unchecked{'start'} = 1;
+ }
+ unless ($showend) {
+ $unchecked{'end'} = 1;
+ }
} else {
if ($env{'form.Status'} ne 'Any') {
$disabledchk{'status'} = 1;
@@ -2797,6 +2844,7 @@ sub gather_userinfo {
($userdata{'username'},$userdata{'domain'},$userdata{'role'}) =
split(/:/,$item);
($userdata{'start'},$userdata{'end'})=split(/:/,$rolehash->{$item});
+ next if (($userdata{'username'} eq '') && ($userdata{'domain'} eq ''));
&build_user_record($context,\%userdata,$userinfo,$indexhash,
$item,$userlist);
} elsif ($context eq 'course') {
@@ -3470,15 +3518,28 @@ END
Future => 'Future',
Expired => 'Expired',
);
- my (%crslogins,%camanagers);
+ my (%crslogins,%camanagers,%othdoms);
if ($context eq 'course') {
# If this is for a single course get last course "log-in".
%crslogins=&Apache::lonnet::dump('nohist_crslastlogin',$cdom,$cnum);
} elsif ($context eq 'author') {
- map { $camanagers{$_.':ca'} = 1; } split(/,/,$env{'environment.authormanagers'});
+ my $authormanagers;
+ if ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)$}) {
+ my %envhash = &Apache::lonnet::userenvironment($1,$2,'authormanagers');
+ $authormanagers = $envhash{'authormanagers'};
+ } else {
+ $authormanagers = $env{'environment.authormanagers'};
+ }
+ if ($authormanagers ne '') {
+ map { $camanagers{$_.':ca'} = 1; } split(/,/,$authormanagers);
+ }
}
# Get groups, role, permanent e-mail so we can sort on them if
# necessary.
+ # Compare user's domain with $env{'request.role.dom'}, and if
+ # different add to the domains for which to retrieve data on
+ # viewable user information, by institutional status, for users
+ # from "other" domains.
foreach my $user (keys(%{$userlist})) {
if ($user eq '' ) {
delete($userlist->{$user});
@@ -3609,6 +3670,9 @@ END
$userlist->{$user}->[$index{'authorquota'}] = sprintf("%.2f",$disk_quota);
}
}
+ unless ($env{'request.role.domain'} eq $udom) {
+ $othdoms{$udom} = 1;
+ }
$usercount ++;
}
my $autocount = 0;
@@ -3650,16 +3714,83 @@ END
$disabled = ' disabled="disabled"';
}
}
+ my (%shownfields_by_dom,%checkshown);
+ if (keys(%othdoms)) {
+ my @userinfo = ('firstname','middlename','lastname','generation',
+ 'permanentemail','id');
+ foreach my $dom (keys(%othdoms)) {
+ my %shownfields = &get_othdom_shownfields($dom,\@userinfo);
+ $shownfields_by_dom{$dom} = \%shownfields;
+ }
+ foreach my $item (@userinfo) {
+ if ($item eq 'id') {
+ if (grep(/^\Q$item\E$/,@cols)) {
+ $checkshown{$item} = 1;
+ }
+ } elsif ($item eq 'permamentemail') {
+ if (grep(/^email$/,@cols)) {
+ $checkshown{$item} = 1;
+ }
+ } elsif (grep(/^fullname$/,@cols)) {
+ $checkshown{$item} = 1;
+ }
+ }
+ }
foreach my $user (@sorted_users) {
my %in;
my $sdata = $userlist->{$user};
- $rowcount ++;
+ $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;
+ if ((grep(/^clicker$/,@cols)) || (keys(%checkshown) && (exists($othdoms{$in{'domain'}})))) {
+ my %info = &Apache::lonnet::userenvironment($in{'domain'},$in{'username'},'clickers','inststatus',
+ 'firstname','middlename','lastname','generation');
+ if (grep(/^clicker$/,@cols)) {
+ if ($info{'clickers'} !~/\w/) { $info{'clickers'} = '-'; }
+ $in{'clicker'} = $info{'clickers'};
+ }
+ if (keys(%checkshown) && exists($othdoms{$in{'domain'}})) {
+ my @statuses;
+ if ($info{'inststatus'} ne '') {
+ @statuses = split(/:/,$info{'inststatus'});
+ } else {
+ @statuses = ('default');
+ }
+ if (ref($shownfields_by_dom{$in{'domain'}}) eq 'HASH') {
+ my (%shown,$rebuildname,%names);
+ foreach my $type (@statuses) {
+ if (ref($shownfields_by_dom{$in{'domain'}}{$type}) eq 'HASH') {
+ foreach my $key (keys(%{$shownfields_by_dom{$in{'domain'}}{$type}})) {
+ $shown{$key} = 1;
+ }
+ }
+ }
+ foreach my $item ('id','permanentemail') {
+ if ($checkshown{$item} && !$shown{$item}) {
+ if ($item eq 'permanentemail') {
+ $in{'email'} = &mt('not shown');
+ } else {
+ $in{$item} = &mt('not shown');
+ }
+ }
+ }
+ foreach my $item ('firstname','middlename','lastname','generation') {
+ if ($checkshown{$item} && !$shown{$item}) {
+ $rebuildname = 1;
+ } else {
+ $names{$item} = $info{$item};
+ }
+ }
+ if ($rebuildname) {
+ $in{'fullname'} = &Apache::lonnet::format_name($names{'firstname'},
+ $names{'middlename'},
+ $names{'lastname'},
+ $names{'generation'},'lastname');
+ }
+ }
+ }
+ }
my $role = $in{'role'};
$in{'role'}=&Apache::lonnet::plaintext($sdata->[$index{'role'}],$crstype);
unless ($mode eq 'excel') {
@@ -3781,13 +3912,10 @@ END
} elsif ($item eq 'clicker') {
if (($context eq 'course') && ($mode ne 'autoenroll')) {
if ($env{'form.showrole'} eq 'st' || $env{'form.showrole'} eq 'Any') {
- my $clickers =
- (&Apache::lonnet::userenvironment($in{'domain'},$in{'username'},'clickers'))[1];
- if ($clickers!~/\w/) { $clickers='-'; }
- $r->print(''.$clickers.' | ');
+ $r->print(''.$in{'clicker'}.' | ');
} else {
- $r->print(' | '."\n");
- }
+ $r->print(' | '."\n");
+ }
}
} elsif (($item eq 'authorquota') || ($item eq 'authorusage')) {
$r->print(''.$in{$item}.' | '."\n");
@@ -4403,10 +4531,16 @@ sub results_header_row {
$description .= ' ('.$constraint.')';
}
} elsif ($context eq 'author') {
+ my ($auname,$audom);
+ if ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)$}) {
+ ($audom,$auname) = ($1,$2);
+ } else {
+ ($audom,$auname) = ($env{'user.domain'},$env{'user.name'});
+ }
$description =
&mt('Author space for [_1]'
,''
- .&Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'})
+ .&Apache::loncommon::plainname($auname,$audom)
.'')
.': ';
if ($statusmode eq 'Expired') {
@@ -4780,9 +4914,25 @@ sub upfile_drop_add {
$fieldstype{$field.'_choice'} = 'scalar';
}
&Apache::loncommon::store_course_settings('enrollment_upload',\%fieldstype);
- my ($cid,$crstype,$setting,$crsdom,$crsnum,$oldcrsuserdoms);
+ my ($cid,$crstype,$setting,$crsdom,$crsnum,$oldcrsuserdoms,%emptyok);
if ($context eq 'domain') {
$setting = $env{'form.roleaction'};
+ if (exists($fields{'names'})) {
+ map { $emptyok{$_} = 1; } ('lastname','firstname','middlename');
+ } else {
+ if (exists($fields{'lname'})) {
+ $emptyok{'lastname'} = 1;
+ }
+ if (exists($fields{'fname'})) {
+ $emptyok{'firstname'} = 1;
+ }
+ if (exists($fields{'mname'})) {
+ $emptyok{'middlename'} = 1;
+ }
+ }
+ if (exists($fields{'gen'})) {
+ $emptyok{'generation'} = 1;
+ }
}
if ($env{'request.course.id'} ne '') {
$cid = $env{'request.course.id'};
@@ -5604,7 +5754,7 @@ sub upfile_drop_add {
$mname,$lname,$gen,$singlesec,
$env{'form.forceid'},$desiredhost,
$email,$role,$enddate,$startdate,
- $checkid,$inststatus);
+ $checkid,$inststatus,\%emptyok);
}
}
if ($multiple) {
@@ -6611,12 +6761,20 @@ sub can_modify_userinfo {
my %domconfig =
&Apache::lonnet::get_dom('configuration',['usermodification'],
$dom);
+ if (($context eq 'author') &&
+ ($env{'request.role'} =~ m{^(ca|aa)\./$match_domain/$match_username$})) {
+ $context = 'coauthor';
+ }
my %canmodify;
if (ref($fields) eq 'ARRAY') {
foreach my $field (@{$fields}) {
$canmodify{$field} = 0;
if (&Apache::lonnet::allowed('mau',$dom)) {
$canmodify{$field} = 1;
+ } elsif (($context ne 'selfcreate') &&
+ ($env{'request.role.dom'} ne $dom) &&
+ ($env{'user.domain'} ne $dom)) {
+ $canmodify{$field} = 0;
} else {
if (ref($domconfig{'usermodification'}) eq 'HASH') {
if (ref($domconfig{'usermodification'}{$context}) eq 'HASH') {
@@ -7810,6 +7968,27 @@ sub adhoc_staff {
return $output;
}
+sub get_othdom_shownfields {
+ my ($dom,$userinfo) = @_;
+ return unless (ref($userinfo) eq 'ARRAY');
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
+ my @insttypes;
+ if (ref($domdefaults{'inststatustypes'}) eq 'HASH') {
+ @insttypes = keys(%{$domdefaults{'inststatustypes'}});
+ }
+ push(@insttypes,'default');
+ my %shownfields;
+ if (ref($domdefaults{'privacyothdom'}) eq 'HASH') {
+ %shownfields = %{$domdefaults{'privacyothdom'}};
+ } else {
+ foreach my $type (@insttypes) {
+ foreach my $field (@{$userinfo}) {
+ $shownfields{$type}{$field} = 1;
+ }
+ }
+ }
+ return %shownfields;
+}
1;