--- loncom/interface/lonuserutils.pm 2019/09/11 14:20:38 1.204
+++ loncom/interface/lonuserutils.pm 2023/11/04 00:06:00 1.219
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Utility functions for managing LON-CAPA user accounts
#
-# $Id: lonuserutils.pm,v 1.204 2019/09/11 14:20:38 raeburn Exp $
+# $Id: lonuserutils.pm,v 1.219 2023/11/04 00:06:00 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -50,6 +50,7 @@ use strict;
use Apache::lonnet;
use Apache::loncommon();
use Apache::lonhtmlcommon;
+use Apache::loncoursequeueadmin;
use Apache::lonlocal;
use Apache::longroup;
use HTML::Entities;
@@ -114,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);
@@ -153,6 +158,560 @@ sub modifyuserrole {
return ($userresult,$authresult,$roleresult,$idresult);
}
+sub role_approval {
+ my ($dom,$context,$process_by,$notifydc) = @_;
+ if (ref($process_by) eq 'HASH') {
+ my %domconfig = &Apache::lonnet::get_dom('configuration',['privacy'],$dom);
+ if (ref($domconfig{'privacy'}) eq 'HASH') {
+ if (ref($notifydc) eq 'ARRAY') {
+ if ($domconfig{'privacy'}{'notify'} ne '') {
+ @{$notifydc} = split(/,/,$domconfig{'privacy'}{'notify'});
+ }
+ }
+ if (ref($domconfig{'privacy'}{'approval'}) eq 'HASH') {
+ my %approvalconf = %{$domconfig{'privacy'}{'approval'}};
+ foreach my $key ('instdom','extdom') {
+ if (ref($approvalconf{$key}) eq 'HASH') {
+ if (keys(%{$approvalconf{$key}})) {
+ $process_by->{$key} = $approvalconf{$key}{$context};
+ }
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub get_instdoms {
+ my ($udom,$instdoms) = @_;
+ return unless (ref($instdoms) eq 'ARRAY');
+ my @intdoms;
+ my %iphost = &Apache::lonnet::get_iphost();
+ my $primary_id = &Apache::lonnet::domain($udom,'primary');
+ my $primary_ip = &Apache::lonnet::get_host_ip($primary_id);
+ if (ref($iphost{$primary_ip}) eq 'ARRAY') {
+ foreach my $id (@{$iphost{$primary_ip}}) {
+ my $intdom = &Apache::lonnet::internet_dom($id);
+ unless(grep(/^\Q$intdom\E$/,@intdoms)) {
+ push(@intdoms,$intdom);
+ }
+ }
+ }
+ foreach my $ip (keys(%iphost)) {
+ if (ref($iphost{$ip}) eq 'ARRAY') {
+ foreach my $id (@{$iphost{$ip}}) {
+ my $location = &Apache::lonnet::internet_dom($id);
+ if ($location) {
+ if (grep(/^\Q$location\E$/,@intdoms)) {
+ my $dom = &Apache::lonnet::host_domain($id);
+ unless (grep(/^\Q$dom\E/,@{$instdoms})) {
+ push(@{$instdoms},$dom);
+ }
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+sub restricted_dom {
+ my ($context,$key,$udom,$uname,$role,$start,$end,$cdom,$cnum,$csec,$credits,
+ $process_by,$instdoms,$got_role_approvals,$got_instdoms,$reject,$pending,
+ $notifydc,$status,$unauthorized,$currqueued) = @_;
+ return if ($udom eq $cdom);
+ return unless ((ref($process_by) eq 'HASH') && (ref($instdoms) eq 'HASH') &&
+ (ref($got_role_approvals) eq 'HASH') && (ref($got_instdoms) eq 'HASH') &&
+ (ref($reject) eq 'HASH') && (ref($pending) eq 'HASH') &&
+ (ref($notifydc) eq 'HASH') && (ref($status) eq 'HASH') &&
+ (ref($unauthorized) eq 'HASH') && (ref($currqueued) eq 'HASH'));
+ my (%approval,@notify,$gotdata,$skip);
+ if (ref($got_role_approvals->{$context}) eq 'HASH') {
+ if ($got_role_approvals->{$context}{$udom}) {
+ $gotdata = 1;
+ if (ref($process_by->{$context}{$udom}) eq 'HASH') {
+ %approval = %{$process_by->{$context}{$udom}};
+ }
+ }
+ }
+ unless ($gotdata) {
+ &role_approval($udom,$context,\%approval,\@notify);
+ $process_by->{$context} = {
+ $udom => \%approval,
+ };
+ $got_role_approvals->{$context} = {
+ $udom => 1,
+ };
+ $notifydc->{$udom} = \@notify;
+ }
+ if (ref($process_by->{$context}) eq 'HASH') {
+ if (ref($process_by->{$context}{$udom}) eq 'HASH') {
+ my @inst;
+ if ($got_instdoms->{$udom}) {
+ if (ref($instdoms->{$udom}) eq 'ARRAY') {
+ @inst = @{$instdoms->{$udom}};
+ }
+ } else {
+ &get_instdoms(\@inst);
+ $instdoms->{$udom} = \@inst;
+ $got_instdoms->{$udom} = 1;
+ }
+ if (grep(/^\Q$cdom\E$/,@inst)) {
+ if (exists($approval{'instdom'})) {
+ my $rule = $approval{'instdom'};
+ if (($rule eq 'none') || ($rule eq 'user') || ($rule eq 'domain')) {
+ my ($id,$currstatus,$curradj) = &get_othdomreq_status($key,$uname,$udom,$role,$cdom,$cnum,$csec);
+ if (($currstatus ne '') && ($curradj eq $rule)) {
+ $status->{$key}->{$uname.':'.$udom} = $currstatus;
+ }
+ if ($rule eq 'none') {
+ $reject->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ };
+ $skip = 1;
+ } elsif (($rule eq 'user') || ($rule eq 'domain')) {
+ if ($curradj eq $rule) {
+ unless ($currstatus eq 'approved') {
+ if ($currstatus eq 'rejected') {
+ $unauthorized->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ };
+ } elsif ($currstatus eq 'pending') {
+ $currqueued->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ adj => $rule,
+ };
+ }
+ $skip = 1;
+ }
+ } else {
+ $pending->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ start => $start,
+ end => $end,
+ adj => $rule,
+ };
+ if (($role eq 'st') && ($credits ne '')) {
+ $pending->{$key}->{$uname.':'.$udom}->{'credits'} = $credits;
+ }
+ $skip = 1;
+ }
+ }
+ }
+ }
+ } elsif (exists($approval{'extdom'})) {
+ my $rule = $approval{'extdom'};
+ if (($rule eq 'none') || ($rule eq 'user') || ($rule eq 'domain')) {
+ my ($id,$currstatus,$curradj) = &get_othdomreq_status($key,$uname,$udom,$role,$cdom,$cnum,$csec);
+ if (($currstatus ne '') && ($curradj eq $rule)) {
+ $status->{$key}->{$uname.':'.$udom} = $currstatus;
+ }
+ if ($rule eq 'none') {
+ $reject->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ };
+ $skip = 1;
+ } elsif (($rule eq 'user') || ($rule eq 'domain')) {
+ if ($curradj eq $rule) {
+ unless ($currstatus eq 'approved') {
+ if ($currstatus eq 'rejected') {
+ $unauthorized->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ };
+ } elsif ($currstatus eq 'pending') {
+ $currqueued->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ adj => $rule,
+ };
+ }
+ $skip = 1;
+ }
+ } else {
+ $pending->{$key}->{$uname.':'.$udom} = {
+ cdom => $cdom,
+ cnum => $cnum,
+ csec => $csec,
+ udom => $udom,
+ uname => $uname,
+ role => $role,
+ start => $start,
+ end => $end,
+ adj => $rule,
+ };
+ if (($role eq 'st') && ($credits ne '')) {
+ $pending->{$key}->{$uname.':'.$udom}->{'credits'} = $credits;
+ }
+ $skip = 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ return $skip;
+}
+
+sub get_othdomreq_status {
+ my ($key,$uname,$udom,$role,$cdom,$cnum,$csec) = @_;
+ my $id = $uname.':'.$udom.':'.$role;
+ my ($dbnum,$currstatus,$curradj);
+ if (($role eq 'ca') || ($role eq 'aa')) {
+ $dbnum = $cnum;
+ } elsif ($key eq $cdom.'_'.$role) {
+ $dbnum = &Apache::lonnet::get_domainconfiguser($cdom);
+ } else {
+ $id .= ':'.$csec;
+ $dbnum = $cnum;
+ }
+ my $statusid = 'status&'.$id;
+ my %curr = &Apache::lonnet::get('nohist_othdomqueued',[$id,$statusid],$cdom,$dbnum);
+ if (ref($curr{$id}) eq 'HASH') {
+ $curradj = $curr{$id}{'adj'};
+ }
+ $currstatus = $curr{$statusid};
+ return ($id,$currstatus,$curradj);
+}
+
+sub print_roles_rejected {
+ my ($context,$reject,$unauthorized) = @_;
+ return unless ((ref($reject) eq 'HASH') || (ref($unauthorized) eq 'HASH'));
+ my $output;
+ if (keys(%{$reject}) > 0) {
+ $output = '
'.
+ &mt("The following roles could not be assigned because the user is from another domain, and that domain's policies disallow it").'
';
+ foreach my $key (sort(keys(%{$reject}))) {
+ if (ref($reject->{$key}) eq 'HASH') {
+ foreach my $user (sort(keys(%{$reject->{$key}}))) {
+ if (ref($reject->{$key}->{$user}) eq 'HASH') {
+ my ($crstype,$role,$cdom,$cnum,$csec,$title,$plainrole);
+ $role = $reject->{$key}->{$user}{'role'};
+ $cdom = $reject->{$key}->{$user}{'cdom'};
+ $cnum = $reject->{$key}->{$user}{'cnum'};
+ $csec = $reject->{$key}->{$user}{'csec'};
+ if (($context eq 'domain') && ($cnum ne '')) {
+ if (($role eq 'ca') || ($role eq 'aa')) {
+ $title = &Apache::loncommon::plainname($cnum,$cdom);
+ } else {
+ if (&Apache::lonnet::is_course($cdom,$cnum)) {
+ my %coursedata = &Apache::lonnet::coursedescription($cdom.'_'.$cnum);
+ $crstype = $coursedata{'type'};
+ $title = $coursedata{'description'};
+ }
+ }
+ } elsif ($context eq 'course') {
+ $crstype = &Apache::loncommon::course_type();
+ }
+ my $plainrole = &Apache::lonnet::plaintext($role,$crstype);
+ $output .= '- '.&mt('User: [_1]',$reject->{$key}->{$user}{'uname'}).' | '.
+ &mt('Domain: [_1]',$reject->{$key}->{$user}{'udom'}).' | '.
+ &mt('Role: [_1]',$plainrole);
+ if ($crstype) {
+ if ($csec ne'') {
+ $output .= ' | '.&mt('Section: [_1]',$csec);
+ }
+ } elsif (($context eq 'domain') && (($role eq 'ca') || ($role eq 'aa'))) {
+ $output .= ' | '.&mt('Authoring Space belonging to: [_1]',$title);
+
+ }
+ if (($context eq 'domain') && ($crstype)) {
+ $output .= ' | '.&mt("$crstype: [_1]",$title);
+ }
+ $output .= '
';
+ }
+ }
+ }
+ }
+ $output .= '
';
+ }
+ if (keys(%{$unauthorized}) > 0) {
+ $output = ''.
+ &mt("The following roles could not be assigned because the user is from another domain, and that domain's policies require approval by the user themselves or by a domain coordinator in that domain, and approval has been withheld.").'
';
+ foreach my $key (sort(keys(%{$unauthorized}))) {
+ if (ref($unauthorized->{$key}) eq 'HASH') {
+ foreach my $user (sort(keys(%{$unauthorized->{$key}}))) {
+ if (ref($unauthorized->{$key}->{$user}) eq 'HASH') {
+ my ($crstype,$role,$cdom,$cnum,$csec,$title,$plainrole);
+ $role = $unauthorized->{$key}->{$user}{'role'};
+ $cdom = $unauthorized->{$key}->{$user}{'cdom'};
+ $cnum = $unauthorized->{$key}->{$user}{'cnum'};
+ $csec = $unauthorized->{$key}->{$user}{'csec'};
+ if (($context eq 'domain') && ($cnum ne '')) {
+ if (($role eq 'ca') || ($role eq 'aa')) {
+ $title = &mt('Authoring Space belonging to: [_1]',
+ &Apache::loncommon::plainname($cnum,$cdom));
+ } else {
+ if (&Apache::lonnet::is_course($cdom,$cnum)) {
+ my %coursedata = &Apache::lonnet::coursedescription($cdom.'_'.$cnum);
+ $crstype = $coursedata{'type'};
+ $title = &mt("$crstype: [_1]",$coursedata{'description'});
+ }
+ }
+ } elsif ($context eq 'course') {
+ $crstype = &Apache::loncommon::course_type();
+ }
+ $plainrole = &Apache::lonnet::plaintext($role,$crstype);
+ $output .= '- '.&mt('User: [_1]',$unauthorized->{$key}->{$user}{'uname'}).' | '.
+ &mt('Domain: [_1]',$unauthorized->{$key}->{$user}{'udom'}).' | '.
+ &mt('Role: [_1]',$plainrole);
+ if ($crstype) {
+ if ($csec ne'') {
+ $output .= ' | '.&mt('Section: [_1]',$csec);
+ }
+ }
+ if ($title ne '') {
+ $output .= ' | '.$title;
+ }
+ $output .= '
';
+ }
+ }
+ }
+ }
+ $output .= '
';
+ }
+ return $output;
+}
+
+sub print_roles_queued {
+ my ($context,$pending,$notifydc,$currqueued) = @_;
+ return unless ((ref($pending) eq 'HASH') && (ref($notifydc) eq 'HASH') &&
+ (ref($currqueued) eq 'HASH'));
+ my $output;
+ if (keys(%{$pending}) > 0) {
+ my $now = time;
+ $output = ''.
+ &mt("The following role assignments have been queued because the user is from another domain, and that domain's policies require approval by the user themselves or by a domain coordinator in that domain").'
';
+ my (%todom,%touser,%crsqueue,%caqueue,%domqueue);
+ my $requester = $env{'user.name'}.':'.$env{'user.domain'};
+ foreach my $key (sort(keys(%{$pending}))) {
+ if (ref($pending->{$key}) eq 'HASH') {
+ foreach my $user (sort(keys(%{$pending->{$key}}))) {
+ if (ref($pending->{$key}->{$user}) eq 'HASH') {
+ my $role = $pending->{$key}->{$user}{'role'};
+ my $uname = $pending->{$key}->{$user}{'uname'};
+ my $udom = $pending->{$key}->{$user}{'udom'};
+ my $csec = $pending->{$key}->{$user}{'csec'};
+ my $cdom = $pending->{$key}->{$user}{'cdom'};
+ my $cnum = $pending->{$key}->{$user}{'cnum'};
+ my $adj = $pending->{$key}->{$user}{'adj'};
+ my $start = $pending->{$key}->{$user}{'start'};
+ my $end = $pending->{$key}->{$user}{'end'};
+ my $credits = $pending->{$key}->{$user}{'credits'};
+ my $now = time;
+ my ($crstype,$title,$plainrole,$extent,$id,$status);
+ if ($context eq 'course') {
+ $crstype = &Apache::loncommon::course_type();
+ $title = $env{'course.'.$env{'request.course.id'}.'.description'};
+ } elsif ($context eq 'domain') {
+ if (&Apache::lonnet::is_course($cdom,$cnum)) {
+ my %coursedata = &Apache::lonnet::coursedescription($cdom.'_'.$cnum);
+ $crstype = $coursedata{'type'};
+ $title = $coursedata{'description'};
+ } elsif (($role eq 'ca') || ($role eq 'aa')) {
+ $title = &Apache::loncommon::plainname($cnum,$cdom);
+ }
+ }
+ $plainrole = &Apache::lonnet::plaintext($role,$crstype);
+ $extent = "/$cdom/$cnum";
+ $id = $uname.':'.$udom.':'.$role;
+ if (($context eq 'course') || ($crstype)) {
+ $id .= ':'.$csec;
+ }
+ $output .= '- '.&mt('User: [_1]',$uname).' | '.
+ &mt('Domain: [_1]',$udom).' | '.
+ &mt('Role: [_1]',$plainrole);
+ if ($crstype) {
+ if ($csec ne'') {
+ $output .= ' | '.&mt('Section: [_1]',$csec);
+ }
+ } elsif (($context eq 'domain') && (($role eq 'ca') || ($role eq 'aa'))) {
+ $output .= ' | '.&mt('Authoring Space belonging to: [_1]',$title);
+ }
+ if (($context eq 'domain') && ($crstype)) {
+ $output .= ' | '.&mt("$crstype: [_1]",$title);
+ }
+ if (($crstype) && ($csec ne '')) {
+ $extent .= "/$csec";
+ }
+ if ($adj eq 'user') {
+ $output .= '
'.&mt('Message sent to user for approval');
+ $touser{$uname.':'.$udom}{'pending:'.$extent.':'.$role} = {
+ timestamp => $now,
+ requester => $requester,
+ start => $start,
+ end => $end,
+ credits => $credits,
+ context => $context,
+ };
+ } elsif ($adj eq 'domain') {
+ $output .= '
'.&mt("Message sent to user's domain coordinator for approval");
+ $todom{$udom}{'pending:'.$uname.':'.$extent.':'.$role} = {
+ timestamp => $now,
+ requester => $requester,
+ start => $start,
+ end => $end,
+ credits => $credits,
+ context => $context,
+ };
+ }
+ $output .= ' ';
+ if (($context eq 'course') || ($crstype)) {
+ $crsqueue{$cdom.'_'.$cnum}{$id} = {
+ timestamp => $now,
+ requester => $requester,
+ adj => $adj,
+ };
+ $crsqueue{$cdom.'_'.$cnum}{'status&'.$id} = 'pending';
+ } elsif (($context eq 'author') ||
+ (($context eq 'domain') && (($role eq 'ca') || ($role eq 'aa')))) {
+ $caqueue{$cnum.':'.$cdom}{$id} = {
+ timestamp => $now,
+ requester => $requester,
+ adj => $adj,
+ };
+ $caqueue{$cnum.':'.$cdom}{'status&'.$id} = 'pending';
+ } elsif ($context eq 'domain') {
+ $domqueue{$id} = {
+ timestamp => $now,
+ requester => $requester,
+ adj => $adj,
+ };
+ $domqueue{'status&'.$id} = 'pending';
+ }
+ }
+ }
+ }
+ }
+ $output .= '
';
+ if (keys(%touser)) {
+ foreach my $key (keys(%touser)) {
+ my ($uname,$udom) = split(/:/,$key);
+ if (&Apache::lonnet::put('nohist_queuedrolereqs',$touser{$key},$udom,$uname) eq 'ok') {
+ my $owndomdesc = &Apache::lonnet::domain($udom);
+ &Apache::loncoursequeueadmin::send_selfserve_notification($uname.':'.$udom,
+ '','',$owndomdesc,$now,'othdomroleuser',$requester);
+ }
+ }
+ }
+ if (keys(%todom)) {
+ foreach my $dom (keys(%todom)) {
+ if (ref($todom{$dom}) eq 'HASH') {
+ my $confname = &Apache::lonnet::get_domainconfiguser($dom);
+ if (&Apache::lonnet::put('nohist_queuedrolereqs',$todom{$dom},$dom,$confname) eq 'ok') {
+ if (ref($notifydc->{$dom}) eq 'ARRAY') {
+ if (@{$notifydc->{$dom}} > 0) {
+ my $notifylist = join(',',@{$notifydc->{$dom}});
+ &Apache::loncoursequeueadmin::send_selfserve_notification($notifylist,
+ '','','',$now,'othdomroledc',$requester);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (keys(%crsqueue)) {
+ foreach my $key (keys(%crsqueue)) {
+ my ($cdom,$cnum) = split(/_/,$key);
+ if (ref($crsqueue{$key}) eq 'HASH') {
+ &Apache::lonnet::put('nohist_othdomqueued',$crsqueue{$key},$cdom,$cnum);
+ }
+ }
+ }
+ if (keys(%caqueue)) {
+ foreach my $key (keys(%caqueue)) {
+ my ($auname,$audom) = split(/:/,$key);
+ if (ref($caqueue{$key}) eq 'HASH') {
+ &Apache::lonnet::put('nohist_othdomqueued',$caqueue{$key},$audom,$auname);
+ }
+ }
+ }
+ if (keys(%domqueue)) {
+ my $confname = &Apache::lonnet::get_domainconfiguser($env{'request.role.domain'});
+ &Apache::lonnet::put('nohist_othdomqueued',\%domqueue,$env{'request.role.domain'},$confname);
+ }
+ }
+ if (keys(%{$currqueued}) > 0) {
+ $output = ''.
+ &mt("The following role assignments were already queued because the user is from another domain, and that domain's policies require approval by the user themselves or by a domain coordinator in that domain").'
';
+ my $requester = $env{'user.name'}.':'.$env{'user.domain'};
+ foreach my $key (sort(keys(%{$currqueued}))) {
+ if (ref($currqueued->{$key}) eq 'HASH') {
+ foreach my $user (sort(keys(%{$currqueued->{$key}}))) {
+ if (ref($currqueued->{$key}->{$user}) eq 'HASH') {
+ my $role = $currqueued->{$key}->{$user}{'role'};
+ my $csec = $currqueued->{$key}->{$user}{'csec'};
+ my $cdom = $currqueued->{$key}->{$user}{'cdom'};
+ my $cnum = $currqueued->{$key}->{$user}{'cnum'};
+ my ($crstype,$title,$plainrole);
+ if ($context eq 'course') {
+ $crstype = &Apache::loncommon::course_type();
+ } elsif (($context eq 'domain') && ($cnum ne '')) {
+ if (($role eq 'ca') || ($role eq 'aa')) {
+ $title = &mt('Authoring Space belonging to: [_1]',
+ &Apache::loncommon::plainname($cnum,$cdom));
+ } elsif (&Apache::lonnet::is_course($cdom,$cnum)) {
+ my %coursedata = &Apache::lonnet::coursedescription($cdom.'_'.$cnum);
+ $crstype = $coursedata{'type'};
+ $title = &mt("$crstype: [_1]",$coursedata{'description'});
+ }
+ }
+ $plainrole = &Apache::lonnet::plaintext($role,$crstype);
+ $output .= '- '.&mt('User: [_1]',$currqueued->{$key}->{$user}{'uname'}).' | '.
+ &mt('Domain: [_1]',$currqueued->{$key}->{$user}{'udom'}).' | '.
+ &mt('Role: [_1]',$plainrole);
+ if ($title ne '') {
+ $output .= ' | '.$title;
+ }
+ if ($crstype) {
+ if ($csec ne '') {
+ $output .= ' | '.&mt('Section: [_1]',$csec);
+ }
+ }
+ $output .= '
';
+ }
+ }
+ }
+ }
+ $output .= '
';
+ }
+ return $output;
+}
+
sub propagate_id_change {
my ($uname,$udom,$user) = @_;
my (@types,@roles);
@@ -510,7 +1069,7 @@ END
";
} elsif ($mode eq 'modifycourse') {
$auth_checks .= "
- if (vf.elements[current.argfield].value == null || vf.elements[current.argfield].value == '') {
+ if ((current.argfield !== null) && (current.argfield !== undefined) && (current.argfield !== '') && (vf.elements[current.argfield].value == null || vf.elements[current.argfield].value == '')) {
";
}
if ( ($mode eq 'createcourse') || ($mode eq 'modifycourse') ) {
@@ -532,7 +1091,7 @@ END
END
} else {
my ($numrules,$intargjs) =
- &passwd_validation_js('vf.elements[current.argfield].value',$domain);
+ &Apache::loncommon::passwd_validation_js('vf.elements[current.argfield].value',$domain);
$auth_checks .= (< $min) {
- $min = $passwdconf{'min'};
- }
- }
- if ($passwdconf{'max'} =~ /^\d+$/) {
- $max = $passwdconf{'max'};
- $numrules ++;
- }
- @chars = @{$passwdconf{'chars'}};
- if (@chars) {
- $numrules ++;
- }
- }
- if ($min > 0) {
- $numrules ++;
- }
- if (($min > 0) || ($max ne '') || (@chars > 0)) {
- my $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n';
- if ($min) {
- $alert{'min'} = &mt('minimum [quant,_1,character]',$min).'\n';
- }
- if ($max) {
- $alert{'max'} = &mt('maximum [quant,_1,character]',$max).'\n';
- }
- my (@charalerts,@charrules);
- if (@chars) {
- if (grep(/^uc$/,@chars)) {
- push(@charalerts,&mt('contain at least one upper case letter'));
- push(@charrules,'uc');
- }
- if (grep(/^lc$/,@chars)) {
- push(@charalerts,&mt('contain at least one lower case letter'));
- push(@charrules,'lc');
- }
- if (grep(/^num$/,@chars)) {
- push(@charalerts,&mt('contain at least one number'));
- push(@charrules,'num');
- }
- if (grep(/^spec$/,@chars)) {
- push(@charalerts,&mt('contain at least one non-alphanumeric'));
- push(@charrules,'spec');
- }
- }
- $intargjs = qq| var rulesmsg = '';\n|.
- qq| var currpwval = $currpasswdval;\n|;
- if ($min) {
- $intargjs .= qq|
- if (currpwval.length < $min) {
- rulesmsg += ' - $alert{min}';
- }
-|;
- }
- if ($max) {
- $intargjs .= qq|
- if (currpwval.length > $max) {
- rulesmsg += ' - $alert{max}';
- }
-|;
- }
- if (@chars > 0) {
- my $charrulestr = '"'.join('","',@charrules).'"';
- my $charalertstr = '"'.join('","',@charalerts).'"';
- $intargjs .= qq| var brokerules = new Array();\n|.
- qq| var charrules = new Array($charrulestr);\n|.
- qq| var charalerts = new Array($charalertstr);\n|;
- my %rules;
- map { $rules{$_} = 1; } @chars;
- if ($rules{'uc'}) {
- $intargjs .= qq|
- var ucRegExp = /[A-Z]/;
- if (!ucRegExp.test(currpwval)) {
- brokerules.push('uc');
- }
-|;
- }
- if ($rules{'lc'}) {
- $intargjs .= qq|
- var lcRegExp = /[a-z]/;
- if (!lcRegExp.test(currpwval)) {
- brokerules.push('lc');
- }
-|;
- }
- if ($rules{'num'}) {
- $intargjs .= qq|
- var numRegExp = /[0-9]/;
- if (!numRegExp.test(currpwval)) {
- brokerules.push('num');
- }
-|;
- }
- if ($rules{'spec'}) {
- $intargjs .= q|
- var specRegExp = /[!"#$%&'()*+,\-.\/:;<=>?@[\\^\]_`{\|}~]/;
- if (!specRegExp.test(currpwval)) {
- brokerules.push('spec');
- }
-|;
- }
- $intargjs .= qq|
- if (brokerules.length > 0) {
- for (var i=0; i "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');
@@ -2590,6 +3057,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;
}
@@ -2664,6 +3132,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');
@@ -2981,6 +3452,7 @@ END
foreach my $idx (@$keylist) {
$index{$idx} = $i++;
}
+ my $now = time;
my $usercount = 0;
my ($secfilter,$grpfilter);
if ($context eq 'course') {
@@ -2998,10 +3470,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.
@@ -3101,6 +3575,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'};
@@ -3669,6 +4153,8 @@ END
setSections(formname,'$crstype');
if (seccheck == 'ok') {
opener.document.$callingform.newsecs.value = formname.sections.value;
+ } else {
+ return;
}
END
} else {
@@ -4294,7 +4780,7 @@ sub upfile_drop_add {
$fieldstype{$field.'_choice'} = 'scalar';
}
&Apache::loncommon::store_course_settings('enrollment_upload',\%fieldstype);
- my ($cid,$crstype,$setting,$crsdom);
+ my ($cid,$crstype,$setting,$crsdom,$crsnum,$oldcrsuserdoms);
if ($context eq 'domain') {
$setting = $env{'form.roleaction'};
}
@@ -4302,11 +4788,18 @@ sub upfile_drop_add {
$cid = $env{'request.course.id'};
$crstype = &Apache::loncommon::course_type();
$crsdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ $crsnum = $env{'course.'.$env{'request.course.id'}.'.num'};
} 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'};
+ $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();
@@ -4491,6 +4984,8 @@ sub upfile_drop_add {
$r->print(''.&mt('Adding/Modifying Users')."
\n\n");
}
$r->rflush;
+ my (%got_role_approvals,%got_instdoms,%process_by,%instdoms,
+ %pending,%reject,%notifydc,%status,%unauthorized,%currqueued);
my %counts = (
user => 0,
@@ -5007,12 +5502,33 @@ sub upfile_drop_add {
my (%userres,%authres,%roleres,%idres);
my $singlesec = '';
if ($role eq 'st') {
+ if (($context eq 'domain') && ($changeauth eq 'Yes') && (!$newuser)) {
+ if ((&Apache::lonnet::allowed('mau',$userdomain)) &&
+ (&Apache::lonnet::homeserver($username,$userdomain) ne 'no_host')) {
+ if ((($amode =~ /^krb4|krb5|internal$/) && $password ne '') ||
+ ($amode eq 'localauth')) {
+ $authresult =
+ &Apache::lonnet::modifyuserauth($userdomain,$username,$amode,$password);
+ }
+ }
+ }
my $sec;
if (ref($userinfo{$i}{'sections'}) eq 'ARRAY') {
if (@secs > 0) {
$sec = $secs[0];
}
}
+ if ($userdomain ne $env{'request.role.domain'}) {
+ my $item = "/$crsdom/$crsnum" ;
+ if ($sec ne '') {
+ $item .= "/$sec";
+ }
+ $item .= '_st';
+ next if (&restricted_dom($context,$item,$userdomain,$username,$role,$startdate,
+ $enddate,$crsdom,$crsnum,$sec,$credits,\%process_by,
+ \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+ \%pending,\%notifydc,\%status,\%unauthorized,\%currqueued));
+ }
&modifystudent($userdomain,$username,$cid,$sec,
$desiredhost,$context);
$roleresult =
@@ -5024,16 +5540,34 @@ sub upfile_drop_add {
'',$context,$inststatus,$credits);
$userresult = $roleresult;
} else {
- if ($role ne '') {
+ my $possrole;
+ if ($role ne '') {
if ($context eq 'course' || $setting eq 'course') {
if ($customroles{$role}) {
$role = 'cr_'.$env{'user.domain'}.'_'.
$env{'user.name'}.'_'.$role;
}
- if (($role ne 'cc') && ($role ne 'co')) {
+ $possrole = $role;
+ if ($possrole =~ /^cr_/) {
+ $possrole =~ s{_}{/}g;
+ }
+ if (($role ne 'cc') && ($role ne 'co')) {
if (@secs > 1) {
$multiple = 1;
+ my $prefix = "/$crsdom/$crsnum";
foreach my $sec (@secs) {
+ if ($userdomain ne $env{'request.role.domain'}) {
+ my $item = $prefix;
+ if ($sec ne '') {
+ $item .= "/$sec";
+ }
+ $item .= '_'.$possrole;
+ next if (&restricted_dom($context,$item,$userdomain,$username,$possrole,
+ $startdate,$enddate,$crsdom,$crsnum,$sec,
+ $credits,\%process_by,\%instdoms,\%got_role_approvals,
+ \%got_instdoms,\%reject,\%pending,\%notifydc,
+ \%status,\%unauthorized,\%currqueued));
+ }
($userres{$sec},$authres{$sec},$roleres{$sec},$idres{$sec}) =
&modifyuserrole($context,$setting,
$changeauth,$cid,$userdomain,$username,
@@ -5047,9 +5581,22 @@ sub upfile_drop_add {
$singlesec = $secs[0];
}
}
+ } else {
+ $possrole = $role;
}
}
if (!$multiple) {
+ if (($userdomain ne $env{'request.role.domain'}) && ($role ne '')) {
+ my $item = "/$crsdom/$crsnum";
+ if ($singlesec ne '') {
+ $item .= "/$singlesec";
+ }
+ $item .= '_'.$possrole;
+ next if (&restricted_dom($context,$item,$userdomain,$username,$possrole,$startdate,$enddate,
+ $crsdom,$crsnum,$singlesec,$credits,\%process_by,\%instdoms,
+ \%got_role_approvals,\%got_instdoms,\%reject,\%pending,\%notifydc,
+ \%status,\%unauthorized,\%currqueued));
+ }
($userresult,$authresult,$roleresult,$idresult) =
&modifyuserrole($context,$setting,
$changeauth,$cid,$userdomain,$username,
@@ -5079,6 +5626,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);
@@ -5099,6 +5653,12 @@ sub upfile_drop_add {
}
$r->print(&print_namespacing_alerts($domain,\%alerts,\%curr_rules));
$r->print(&passwdrule_alerts($domain,\%showpasswdrules));
+ if ((keys(%reject)) || (keys(%unauthorized))) {
+ $r->print(&print_roles_rejected($context,\%reject,\%unauthorized));
+ }
+ if ((keys(%pending)) || (keys(%currqueued))) {
+ $r->print(&print_roles_queued($context,\%pending,\%notifydc,\%currqueued));
+ }
#####################################
# Display list of students to drop #
#####################################
@@ -5177,7 +5737,11 @@ sub passwdrule_alerts {
my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
$warning = ''.&mt('Password requirement(s) unmet for one or more users:').'
';
if ($showrules{'min'}) {
- $warning .= '- '.&mt('minimum [quant,_1,character]',$passwdconf{'min'}).'
';
+ my $min = $passwdconf{'min'};
+ if ($min eq '') {
+ $min = $Apache::lonnet::passwdmin;
+ }
+ $warning .= '- '.&mt('minimum [quant,_1,character]',$min).'
';
}
if ($showrules{'max'}) {
$warning .= '- '.&mt('maximum [quant,_1,character]',$passwdconf{'max'}).'
';
@@ -5288,7 +5852,8 @@ sub update_user_list {
if ($context eq 'course') {
$crstype = &Apache::loncommon::course_type();
}
- my @changelist;
+ my (@changelist,%got_role_approvals,%got_instdoms,%process_by,%instdoms,
+ %pending,%reject,%notifydc,%status,%unauthorized,%currqueued);
if ($choice eq 'drop') {
@changelist = &Apache::loncommon::get_env_multiple('form.droplist');
} else {
@@ -5318,7 +5883,7 @@ sub update_user_list {
foreach my $item (@changelist) {
my ($role,$uname,$udom,$cid,$sec,$scope,$result,$type,$locktype,
@sections,$scopestem,$singlesec,$showsecs,$warn_singlesec,
- $nothingtodo,$keepnosection,$credits,$instsec);
+ $nothingtodo,$keepnosection,$credits,$instsec,$cdom,$cnum);
if ($choice eq 'drop') {
($uname,$udom,$sec) = split(/:/,$item,-1);
$role = 'st';
@@ -5335,6 +5900,8 @@ sub update_user_list {
split(/\:/,$item,8);
$instsec = &unescape($instsec);
$cid = $env{'request.course.id'};
+ $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
$scopestem = '/'.$cid;
$scopestem =~s/\_/\//g;
if ($sec eq '') {
@@ -5345,15 +5912,20 @@ sub update_user_list {
} elsif ($context eq 'author') {
($uname,$udom,$role) = split(/\:/,$item,-1);
$scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'};
+ $cdom = $env{'user.domain'};
+ $cnum = $env{'user.name'};
} elsif ($context eq 'domain') {
if ($setting eq 'domain') {
($role,$uname,$udom) = split(/\:/,$item,-1);
$scope = '/'.$env{'request.role.domain'}.'/';
+ $cdom = $env{'request.role.domain'};
} elsif ($setting eq 'author') {
($uname,$udom,$role,$scope) = split(/\:/,$item);
+ (undef,$cdom,$cnum) = split(/\//,$scope);
} elsif ($setting eq 'course') {
($uname,$udom,$role,$cid,$sec,$type,$locktype,$credits,$instsec) =
split(/\:/,$item,9);
+ ($cdom,$cnum) = split('_',$cid);
$instsec = &unescape($instsec);
$scope = '/'.$cid;
$scope =~s/\_/\//g;
@@ -5395,7 +5967,12 @@ sub update_user_list {
$start = $startdate;
$end = $enddate;
}
+ my $id = $scope.'_'.$role;
if ($choice eq 'reenable') {
+ next if (&restricted_dom($context,$id,$udom,$uname,$role,$now,$end,$cdom,$cnum,
+ $sec,$credits,\%process_by,\%instdoms,\%got_role_approvals,
+ \%got_instdoms,\%reject,\%pending,\%notifydc,
+ \%status,\%unauthorized,\%currqueued));
if ($role eq 'st') {
$result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
} else {
@@ -5404,6 +5981,10 @@ sub update_user_list {
$now,'','',$context);
}
} elsif ($choice eq 'activate') {
+ next if (&restricted_dom($context,$id,$udom,$uname,$role,$now,$end,$cdom,$cnum,
+ $sec,$credits,\%process_by,\%instdoms,\%got_role_approvals,
+ \%got_instdoms,\%reject,\%pending,\%notifydc,
+ \%status,\%unauthorized,\%currqueued));
if ($role eq 'st') {
$result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
} else {
@@ -5411,6 +5992,10 @@ sub update_user_list {
$now,'','',$context);
}
} elsif ($choice eq 'chgdates') {
+ next if (&restricted_dom($context,$id,$udom,$uname,$role,$start,$end,$cdom,$cnum,
+ $sec,$credits,\%process_by,\%instdoms,\%got_role_approvals,
+ \%got_instdoms,\%reject,\%pending,\%notifydc,
+ \%status,\%unauthorized,\%currqueued));
if ($role eq 'st') {
$result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
} else {
@@ -5620,6 +6205,12 @@ sub update_user_list {
$r->print(&make_dates_default($startdate,$enddate,$context,$crstype));
}
}
+ if ((keys(%reject)) || (keys(%unauthorized))) {
+ $r->print(&print_roles_rejected($context,\%reject,\%unauthorized));
+ }
+ if ((keys(%pending)) || (keys(%currqueued))) {
+ $r->print(&print_roles_queued($context,\%pending,\%notifydc,\%currqueued));
+ }
my $linktext = &mt('Display User Lists');
if ($choice eq 'drop') {
$linktext = &mt('Display current class roster');
@@ -6258,6 +6849,9 @@ sub get_permission {
$permission{'selfenrolladmin'} = 1;
}
}
+ unless ($permission{'selfenrolladmin'}) {
+ $permission{'selfenrollview'} = 1;
+ }
}
if ($env{'request.course.id'}) {
my $user;
@@ -6277,8 +6871,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) {
@@ -6306,8 +6915,9 @@ sub get_permission {
}
}
my $allowed = 0;
- foreach my $perm (values(%permission)) {
- if ($perm) { $allowed=1; last; }
+ foreach my $key (keys(%permission)) {
+ next if (($key eq 'owner') || ($key eq 'co-owner') || ($key eq 'author'));
+ if ($permission{$key}) { $allowed=1; last; }
}
return (\%permission,$allowed);
}
@@ -6320,6 +6930,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);
@@ -6516,7 +7138,7 @@ sub verify_authen {
$finish = "document.$formname.submit();";
}
my ($numrules,$intargjs) =
- &passwd_validation_js('argpicked',$domain);
+ &Apache::loncommon::passwd_validation_js('argpicked',$domain);
my $outcome = <<"ENDSCRIPT";
function auth_check() {
@@ -6720,7 +7342,7 @@ sub selfenrollment_administration {
}
}
if ($settings{'internal.selfenrollmgrdc'} ne '') {
- my @in_domain = split(/,/,$settings{'internal.selfenrollmgrdc'});
+ @in_domain = split(/,/,$settings{'internal.selfenrollmgrdc'});
my @diffs = &Apache::loncommon::compare_arrays(\@in_domain,$possconfigs);
unless (@diffs) {
return (\@in_course,\@in_domain);