--- loncom/interface/lonuserutils.pm 2023/08/01 15:56:32 1.216
+++ loncom/interface/lonuserutils.pm 2024/01/05 04:01:40 1.221
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Utility functions for managing LON-CAPA user accounts
#
-# $Id: lonuserutils.pm,v 1.216 2023/08/01 15:56:32 raeburn Exp $
+# $Id: lonuserutils.pm,v 1.221 2024/01/05 04:01:40 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;
@@ -115,7 +115,11 @@ sub modifyuserrole {
} elsif ($context eq 'domain') {
$scope = '/'.$env{'request.role.domain'}.'/';
} elsif ($context eq 'author') {
- $scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'};
+ if ($env{'request.role'} =~ m{^ca\.(/$match_domain/$match_username)$}) {
+ $scope = $1;
+ } else {
+ $scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'};
+ }
}
if ($context eq 'domain') {
my $uhome = &Apache::lonnet::homeserver($uname,$udom);
@@ -135,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);
}
}
@@ -2048,6 +2071,14 @@ sub construction_space_roles {
foreach my $role (@allroles) {
if (&Apache::lonnet::allowed('c'.$role,$env{'user.domain'}.'/'.$env{'user.name'})) {
push(@roles,$role);
+ } elsif ($env{'request.role'} =~ m{^ca\./($match_domain)/($match_username)$}) {
+ my ($audom,$auname) = ($1,$2);
+ if (($role eq 'ca') || ($role eq 'aa')) {
+ if ((&Apache::lonnet::allowed('v'.$role,,$audom.'/'.$auname)) &&
+ ($env{"environment.internal.manager./$audom/$auname"})) {
+ push(@roles,$role);
+ }
+ }
}
}
return @roles;
@@ -2152,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';
}
@@ -2219,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('
'.
@@ -2305,12 +2354,31 @@ sub print_userlist {
} else {
my (%cstr_roles,%dom_roles);
if ($context eq 'author') {
- # List co-authors and assistant co-authors
my @possroles = &roles_by_context($context);
- %cstr_roles = &Apache::lonnet::get_my_roles(undef,undef,undef,
- \@statuses,\@possroles);
- &gather_userinfo($context,$format,\%userlist,$indexhash,\%userinfo,
- \%cstr_roles,$permission);
+ my @allowedroles;
+ # List co-authors and assistant co-authors
+ my ($auname,$audom);
+ if ($env{'request.role'} =~ m{^ca\./($match_domain)/($match_username)$}) {
+ ($audom,$auname) = ($1,$2);
+ foreach my $role (@possroles) {
+ if ((&Apache::lonnet::allowed('v'.$role,"$audom/$auname")) ||
+ (&Apache::lonnet::allowed('c'.$role,"$audom/$auname"))) {
+ push(@allowedroles,$role);
+ }
+ }
+ } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
+ if ($1 eq $env{'user.domain'}) {
+ $auname = $env{'user.name'};
+ $audom = $env{'user.domain'};
+ }
+ @allowedroles = @possroles;
+ }
+ if (($auname ne '') && ($audom ne '')) {
+ %cstr_roles = &Apache::lonnet::get_my_roles($auname,$audom,undef,
+ \@statuses,\@allowedroles);
+ &gather_userinfo($context,$format,\%userlist,$indexhash,\%userinfo,
+ \%cstr_roles,$permission);
+ }
} elsif ($context eq 'domain') {
if ($env{'form.roletype'} eq 'domain') {
if (grep(/^authorusage$/,@cols)) {
@@ -2601,14 +2669,17 @@ sub get_cols_array {
push(@cols,'photo');
}
if ($context eq 'domain') {
- push (@cols,('authorusage','authorquota','extent'));
+ push(@cols,('authorusage','authorquota','extent'));
+ }
+ if ($context eq 'author') {
+ push(@cols,'manager');
}
}
return @cols;
}
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);
@@ -2621,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;
@@ -2642,6 +2723,11 @@ sub column_checkboxes {
} elsif ($env{'form.roletype'} eq 'domain') {
$disabledchk{'extent'} = 1;
}
+ } elsif ($context eq 'author') {
+ if (($env{'form.Status'} eq 'Expired') ||
+ ($env{'form.showrole'} eq 'aa')) {
+ $disabledchk{'manager'} = 1;
+ }
}
}
my $numposs = scalar(@cols);
@@ -2733,6 +2819,7 @@ sub get_column_names {
'ca' => "check all",
'ua' => "uncheck all",
'clicker' => "clicker-ID",
+ 'manager' => "co-author manager",
);
if ($context eq 'domain' && $env{'form.roletype'} eq 'course') {
$lt{'extent'} = &mt('course(s): description, section(s), status');
@@ -3017,6 +3104,7 @@ sub make_keylist_array {
$index->{'instsec'} = &Apache::loncoursedata::CL_INSTSEC();
$index->{'authorquota'} = &Apache::loncoursedata::CL_AUTHORQUOTA();
$index->{'authorusage'} = &Apache::loncoursedata::CL_AUTHORUSAGE();
+ $index->{'manager'} = &Apache::loncoursedata::CL_CAMANAGER();
foreach my $key (keys(%{$index})) {
$keylist->[$index->{$key}] = $key;
}
@@ -3091,6 +3179,9 @@ sub show_users_list {
(($env{'form.showrole'} eq 'Any') || ($env{'form.showrole'} eq 'au'))) {
push(@sortable,('authorusage','authorquota'));
}
+ if ($context eq 'author') {
+ push(@sortable,'manager');
+ }
}
if ($mode eq 'pickauthor') {
@sortable = ('username','fullname','email','status');
@@ -3408,6 +3499,7 @@ END
foreach my $idx (@$keylist) {
$index{$idx} = $i++;
}
+ my $now = time;
my $usercount = 0;
my ($secfilter,$grpfilter);
if ($context eq 'course') {
@@ -3425,10 +3517,12 @@ END
Future => 'Future',
Expired => 'Expired',
);
- # If this is for a single course get last course "log-in".
- my %crslogins;
+ my (%crslogins,%camanagers);
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'});
}
# Get groups, role, permanent e-mail so we can sort on them if
# necessary.
@@ -3528,6 +3622,16 @@ END
}
}
}
+ if ($context eq 'author') {
+ if (($camanagers{$user}) &&
+ ((!defined($userlist->{$user}->[$index{'end'}])) ||
+ ($userlist->{$user}->[$index{'end'}] == 0) ||
+ ($userlist->{$user}->[$index{'end'}] > $now))) {
+ $userlist->{$user}->[$index{'manager'}] = &mt('Yes');
+ } else {
+ $userlist->{$user}->[$index{'manager'}] = &mt('No');
+ }
+ }
my %emails = &Apache::loncommon::getemails($uname,$udom);
if ($emails{'permanentemail'} =~ /\S/) {
$userlist->{$user}->[$index{'email'}] = $emails{'permanentemail'};
@@ -4723,9 +4827,25 @@ sub upfile_drop_add {
$fieldstype{$field.'_choice'} = 'scalar';
}
&Apache::loncommon::store_course_settings('enrollment_upload',\%fieldstype);
- my ($cid,$crstype,$setting,$crsdom,$crsnum);
+ 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'};
@@ -4738,6 +4858,11 @@ sub upfile_drop_add {
$crstype = &Apache::loncommon::course_type($cid);
$crsdom = $env{'form.dcdomain'};
$crsnum = $env{'form.dccourse'};
+ if (exists($env{'course.'.$cid.'.internal.userdomains'})) {
+ $oldcrsuserdoms = 1;
+ }
+ my %coursedesc = &Apache::lonnet::coursedescription($cid,{ one_time => 1 });
+ $env{'course.'.$cid.'.internal.userdomains'} = $coursedesc{'internal.userdomains'};
}
}
my ($startdate,$enddate) = &get_dates_from_form();
@@ -5542,7 +5667,7 @@ sub upfile_drop_add {
$mname,$lname,$gen,$singlesec,
$env{'form.forceid'},$desiredhost,
$email,$role,$enddate,$startdate,
- $checkid,$inststatus);
+ $checkid,$inststatus,\%emptyok);
}
}
if ($multiple) {
@@ -5564,6 +5689,13 @@ sub upfile_drop_add {
} # end of loop
$r->print('');
&Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
+ if (($context eq 'domain') && ($setting eq 'course')) {
+ unless ($oldcrsuserdoms) {
+ if (exists($env{'course.'.$cid.'.internal.userdomains'})) {
+ delete($env{'course.'.$cid.'.internal.userdomains'});
+ }
+ }
+ }
}
# Flush the course logs so reverse user roles immediately updated
$r->register_cleanup(\&Apache::lonnet::flushcourselogs);
@@ -6802,8 +6934,23 @@ sub get_permission {
}
}
} elsif ($context eq 'author') {
- $permission{'cusr'} = &authorpriv($env{'user.name'},$env{'request.role.domain'});
- $permission{'view'} = $permission{'cusr'};
+ my $audom = $env{'request.role.domain'};
+ my $auname = $env{'user.name'};
+ if ((&Apache::lonnet::allowed('cca',"$audom/$auname")) ||
+ (&Apache::lonnet::allowed('caa',"$audom/$auname"))) {
+ $permission{'author'} = 1;
+ $permission{'cusr'} = 1;
+ $permission{'view'} = 1;
+ }
+ } elsif ($context eq 'coauthor') {
+ my ($audom,$auname) = ($env{'request.role'} =~ m{^ca\./($match_domain)/($match_username)$});
+ if ((&Apache::lonnet::allowed('vca',"$audom/$auname")) ||
+ (&Apache::lonnet::allowed('vaa',"$audom/$auname"))) {
+ if ($env{"environment.internal.manager./$audom/$auname"}) {
+ $permission{'cusr'} = 1;
+ $permission{'view'} = 1;
+ }
+ }
} else {
my @allroles = &roles_by_context($context);
foreach my $role (@allroles) {
@@ -6832,7 +6979,7 @@ sub get_permission {
}
my $allowed = 0;
foreach my $key (keys(%permission)) {
- next if (($key eq 'owner') || ($key eq 'co-owner'));
+ next if (($key eq 'owner') || ($key eq 'co-owner') || ($key eq 'author'));
if ($permission{$key}) { $allowed=1; last; }
}
return (\%permission,$allowed);
@@ -6846,6 +6993,18 @@ sub authorpriv {
|| (&Apache::lonnet::allowed('caa',$audom.'/'.$auname))) { return ''; } return 1;
}
+sub coauthorpriv {
+ my ($auname,$audom)=@_;
+ my $uname = $env{'user.name'};
+ my $udom = $env{'user.domain'};
+ if (((&Apache::lonnet::allowed('vca',"$udom/$uname")) ||
+ (&Apache::lonnet::allowed('vaa',"$udom/$uname"))) &&
+ ($env{"environment.internal.manager./$audom/$auname"})) {
+ return 1;
+ }
+ return '';
+}
+
sub roles_on_upload {
my ($context,$setting,$crstype,%customroles) = @_;
my (@possible_roles,@permitted_roles);