[LON-CAPA-cvs] cvs: loncom /interface loncoursequeueadmin.pm loncreateuser.pm lonuserutils.pm /lonnet/perl lonnet.pm

raeburn raeburn at source.lon-capa.org
Tue Nov 22 21:55:37 EST 2022


raeburn		Wed Nov 23 02:55:37 2022 EDT

  Modified files:              
    /loncom/interface	loncoursequeueadmin.pm loncreateuser.pm 
                     	lonuserutils.pm 
    /loncom/lonnet/perl	lonnet.pm 
  Log:
  - Take into account domain configuration in user's domain for whether approval
    (from user's DC or user) is needed to complete assignment of a role in a
    domain, authoring space, or course that does not belog to the user's domain.
    Work in progress.   
  
  
-------------- next part --------------
Index: loncom/interface/loncoursequeueadmin.pm
diff -u loncom/interface/loncoursequeueadmin.pm:1.62 loncom/interface/loncoursequeueadmin.pm:1.63
--- loncom/interface/loncoursequeueadmin.pm:1.62	Wed Nov 24 04:25:02 2021
+++ loncom/interface/loncoursequeueadmin.pm	Wed Nov 23 02:55:37 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Utilities to administer domain course requests and course self-enroll requests
 #
-# $Id: loncoursequeueadmin.pm,v 1.62 2021/11/24 04:25:02 raeburn Exp $
+# $Id: loncoursequeueadmin.pm,v 1.63 2022/11/23 02:55:37 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -144,7 +144,6 @@
         push(@rawmsg,{
                       mt  => $msgtxt,
                       args => ["\n  ".$contextdesc.",\n",$timestamp.",\n"],
-
                      });
         if (ref($textstr) eq 'ARRAY') {
             push(@rawmsg,@{$textstr});
@@ -270,6 +269,32 @@
         if (ref($textstr) eq 'ARRAY') {
             push(@rawmsg,@{$textstr});
         }
+    } elsif ($context eq 'othdomroleuser') {
+        my $linktext = 'Roles';
+        if (&Apache::loncommon::show_course()) {
+            $linktext = 'Courses';
+        }
+        $rawsubj = 'Role Assignment Approval';
+        push(@rawmsg,{
+                      mt  => 'A domain different to your own LON-CAPA domain ([_1]) wants to assign you a role in their domain.',
+                      args => ["\n$contextdesc\n"],
+                     },
+                     {
+                      mt =>"[_1]Click $linktext at top right, then click 'Manage Role Requests' in the gray Functions bar ".
+                           "to display a list of pending role assignments in other domain(s), which you can either accept or reject.",
+                      args => ["\n\n"],
+                     });
+    } elsif ($context eq 'othdomroledc') {
+        $rawsubj = 'Role Assignment Authorization';
+        push(@rawmsg,{
+                      mt  => 'Another LON-CAPA domain wants to assign a role in their domain to a user from your domain.',
+                      args => [],
+                     },
+                     {
+                      mt =>'[_1]As Domain Coordinator, use: [_2]Main Menu -> Create users or modify the roles and privileges of users
+ -> Queued Role Assignments (this domain) [_3]to display a list of pending requests, which you can either approve or reject.',
+                      args => ["\n","\n\n  ","\n\n"],
+                     });
     }
     my @to_notify = split(/,/,$notifylist);
     my $numsent = 0;
@@ -371,7 +396,7 @@
 }
 
 sub display_queued_requests {
-    my ($context,$dom,$cnum) = @_;
+    my ($context,$dom,$cnum,$secondary) = @_;
     my ($namespace,$formaction,$nextelement,%requesthash);
     if ($context eq 'course') {
         $formaction = '/adm/createuser';
@@ -388,6 +413,34 @@
         $namespace = 'usernamequeue';
         %requesthash = &Apache::lonnet::dump_dom($namespace,$dom);
         $nextelement = '<input type="hidden" name="state" value="done" />';
+    } elsif ($context eq 'othdomqueue') {
+        $formaction = '/adm/createuser';
+        $namespace = 'othdomqueued';
+        if ($secondary eq 'domain') {
+            %requesthash = &Apache::lonnet::dump_dom($namespace,$dom);
+            foreach my $key (keys(%requesthash)) {
+                delete($requesthash{$key}) if ($key =~ /:(ca|aa)$/);
+            }
+        } elsif ($secondary eq 'author') {
+            %requesthash = &Apache::lonnet::dump($namespace,$dom,$cnum);
+            if ($cnum eq &Apache::lonnet::get_domainconfiguser($dom)) {
+                foreach my $key (keys(%requesthash)) {
+                    delete($requesthash{$key}) if ($key !~ /:(ca|aa)$/);
+                }
+            }
+        } else {
+            %requesthash = &Apache::lonnet::dump($namespace,$dom,$cnum);
+        }
+    } elsif ($context eq 'othdomaction') {
+        $formaction = '/adm/createuser';
+        $namespace = 'queuedrolereqs';
+        if ($secondary eq 'domain') {
+            my $confname = &Apache::lonnet::get_domainconfiguser($dom);
+            %requesthash = &Apache::lonnet::dump($namespace,$dom,$confname);
+        } else {
+            %requesthash = &Apache::lonnet::dump($namespace,$dom,$cnum);
+        }
+        $nextelement = '<input type="hidden" name="state" value="done" />';
     } else {
         $formaction = '/adm/createcourse';
         $namespace = 'courserequestqueue';
@@ -418,9 +471,23 @@
             } elsif ($context eq 'requestusername') {
                 $timestamp = $requesthash{$item};
                 ($entry) = (&unescape($item) =~ /^($match_username)_approval$/);
+            } elsif ($context eq 'othdomqueue') {
+                if (ref($requesthash{$item}) eq 'HASH') {
+                    my ($puname,$pudom,$prole,$psec) = split(/:/,$item);
+                    $timestamp = $requesthash{$item}{'timestamp'};
+                    my $adj = $requesthash{$item}{'adj'};
+                    $entry = join(':',$puname,$pudom,$prole,$adj,
+                                      &escape($requesthash{$item}{'requester'}),
+                                      $psec);
+                }
+            } elsif ($context eq 'othdomaction') {
+               if (ref($requesthash{$item}) eq 'HASH') {
+                   $timestamp = $requesthash{$item}{'timestamp'};
+                   $entry = &escape($item).':'.&escape($requesthash{$item}{'requester'});
+               }
             } else {
-                $timestamp = $requesthash{$item}{'timestamp'};
                 if (ref($requesthash{$item}) eq 'HASH') {
+                    $timestamp = $requesthash{$item}{'timestamp'};
                     my ($cnum,$disposition) = split('_',$item);
                     $entry = $cnum.':'.$requesthash{$item}{'ownername'}.':'.
                              $requesthash{$item}{'ownerdom'}.':';
@@ -453,10 +520,26 @@
                 $output .= '<h3>'.&mt('Requests for Authoring Space queued pending approval by a Domain Coordinator').'</h3>';
             } elsif ($context eq 'requestusername') {
                 $output .= '<h3>'.&mt('Requests for LON-CAPA accounts queued pending approval by a Domain Coordinator').'</h3>';
+            } elsif ($context eq 'othdomqueue') {
+                if ($secondary eq 'domain') {
+                    $output .= '<h3>'.&mt('Domain role assignments for users from another domain, queued pending approval').'</h3>';
+                } elsif ($secondary eq 'author') {
+                    $output .= '<h3>'.&mt('Co-author role assignments for users from another domain, queued pending approval').'</h3>';
+                } elsif ($secondary eq 'course') {
+                    $output .= '<h3>'.&mt('Course role assignments for users from another domain, queued pending approval').'</h3>';
+                } elsif ($secondary eq 'community') {
+                    $output .= '<h3>'.&mt('Community role assignments for users from another domain, queued pending approval').'</h3>';
+                }
+            } elsif ($context eq 'othdomaction') {
+                if ($secondary eq 'user') {
+                    $output .= '<h3>'.&mt('Role assignments for you in other domains, queued pending your acceptance of the role.').'</h3>';
+                } elsif ($secondary eq 'domain') {
+                    $output .= '<h3>'.&mt('Role assignments in other domains, queued pending domain coordinator approval in this domain.').'</h3>';
+                }
             } else {
                 $output .= '<h3>'.&mt('Course/Community requests queued pending approval by a Domain Coordinator').'</h3>';
             } 
-            $output .= &build_queue_display($dom,$context,\%queue_by_date).
+            $output .= &build_queue_display($dom,$context,\%queue_by_date,$secondary).
                        '<input type="hidden" name="queue" value="approval" />';
         } else {
             $output .= '<div class="LC_info">';
@@ -470,15 +553,31 @@
                 $output .= &mt('There are currently no requests for LON-CAPA accounts awaiting approval.');
             } elsif ($context eq 'domain') {
                 $output .= &mt('There are currently no course or community requests awaiting approval.');
+            } elsif ($context eq 'othdomqueue') {
+                if ($secondary eq 'domain') {
+                    $output .= &mt('There are currently no domain role assignment(s) for user(s) from another domain queued pending approval');
+                } elsif ($secondary eq 'author') {
+                    $output .= &mt('There are currently no co-author role assignment(s) for user(s) from another domain queued pending approval');
+                } elsif ($secondary eq 'course') {
+                    $output .= &mt('There are currently no course role assignment(s) for user(s) from another domain queued pending approval');
+                } elsif ($secondary eq 'community') {
+                    $output .= &mt('There are currently no community role assignment(s) for user(s) from another domain queued pending approval');
+                }
+            } elsif ($context eq 'othdomaction') {
+                if ($secondary eq 'user') {
+                    $output .= &mt('There are currently no pending role assignments for you in other domains, queued pending your acceptance of the role.');
+                } elsif ($secondary eq 'domain') {
+                    $output .= &mt('There are currently no pending role assignments in other domains, queued pending domain coordinator approval in this domain.');
+                }
             }
-            $output .= '</div>'; 
+            $output .= '</div>';
         }
         if ($context eq 'pending') {
             $output .= '<br /><input type="submit" name="validationcheck" value="'.
                        &mt('Validate').'" /><br />'."\n".
                        '<p>'.&mt('Any course/community requests which are successfully validated will be created immediately.').' '.
                              &mt('Unvalidated requests will be listed for manual approval/rejection.').'</p>';
-        } elsif (($context ne 'helpdesk') && ($context ne 'displaypending')) {
+        } elsif (($context ne 'helpdesk') && ($context ne 'displaypending') && ($context ne 'othdomqueue')) {
             $output .= '<br /><input type="submit" name="processqueue" value="'.&mt('Save').'" />';
         }
         $output .= '</form>';
@@ -492,6 +591,22 @@
             $output .= &mt('There are currently no requests for Authoring Space awaiting approval.');
         } elsif ($context eq 'requestusername') {
             $output .= &mt('There are currently no requests for LON-CAPA accounts awaiting approval.');
+        } elsif ($context eq 'othdomqueue') {
+            if ($secondary eq 'domain') {
+                $output .= &mt('There are currently no domain role assignment(s) for user(s) from another domain queued pending approval');
+            } elsif ($secondary eq 'author') {
+                $output .= &mt('There are currently no co-author role assignment(s) for user(s) from another domain queued pending approval');
+            } elsif ($secondary eq 'course') {
+                $output .= &mt('There are currently no course role assignment(s) for user(s) from another domain queued pending approval');
+            } elsif ($secondary eq 'community') {
+                $output .= &mt('There are currently no community role assignment(s) for user(s) from another domain queued pending approval');
+            }
+        } elsif ($context eq 'othdomaction') {
+            if ($secondary eq 'user') {
+                $output .= &mt('There are currently no pending role assignments for you in other domains, queued pending your acceptance of the role.');
+            } elsif ($secondary eq 'domain') {
+                $output .= &mt('There are currently no pending role assignments in other domains, queued pending domain coordinator approval in this domain.');
+            }
         } else {
             $output .= &mt('There are currently no course or community requests awaiting approval.');
         }
@@ -501,15 +616,18 @@
 }
 
 sub build_queue_display {
-    my ($dom,$context,$queue) = @_;
+    my ($dom,$context,$queue,$secondary) = @_;
     return unless (ref($queue) eq 'HASH');
-    my %crstypes;
-    my $output =  &Apache::loncommon::start_data_table().
-                  &Apache::loncommon::start_data_table_header_row();
-    unless (($context eq 'pending') || ($context eq 'displaypending') || ($context eq 'helpdesk')) { 
+    my (%crstypes,%roles_by_context);
+    my $output = &Apache::loncommon::start_data_table().
+                 &Apache::loncommon::start_data_table_header_row();
+    unless (($context eq 'pending') || ($context eq 'displaypending') || 
+            ($context eq 'helpdesk') || ($context eq 'othdomqueue')) {
         $output .= '<th>'.&mt('Action').'</th>';
     }
-    $output .= '<th>'.&mt('Requestor').'</th>';
+    unless (($context eq 'othdomqueue') || (($context eq 'othdomaction') && ($secondary eq 'user'))) {
+        $output .= '<th>'.&mt('Requestor').'</th>';
+    }
     if ($context eq 'course') {
         $output .= '<th>'.&mt('Section').'</th>'.
                    '<th>'.&mt('Date requested').'</th>';
@@ -518,6 +636,25 @@
     } elsif ($context eq 'requestusername') {
         $output .= '<th>'.&mt('Date requested').'</th>'.
                    '<th>'.&mt('Details').'</th>';
+    } elsif ($context eq 'othdomqueue') {
+        $output .= '<th>'.&mt('Date requested').'</th>'.
+                   '<th>'.&mt('Role').'</th>';
+        if ($secondary eq 'course') {
+            $output .= '<th>'.&mt('Section').'</th>';
+        }
+        $output .= '<th>'.&mt('Requested for').'</th>'.
+                   '<th>'.&mt('Approval needed from').'</th>';
+    } elsif ($context eq 'othdomaction') {
+        $output .= '<th>'.&mt('Date requested').'</th>'.
+                   '<th>'.&mt('Role type').'</th>'.
+                   '<th>'.&mt('Location').'</th>';
+        if ($secondary eq 'domain') {
+            $output .= '<th>'.&mt('Affected User').'</th>';
+        }
+        foreach my $type ('domain','course') {
+            my @possroles = &Apache::lonuserutils::roles_by_context($type);
+            $roles_by_context{$type} = \@possroles;
+        }
     } elsif ($context eq 'pending' || $context eq 'displaypending' || $context eq 'stillpending') {
         $output .= '<th>'.&mt('Institutional code').'</th>'.
                    '<th>'.&mt('Date requested').'</th>'.
@@ -541,7 +678,8 @@
         if (ref($queue->{$item}) eq 'ARRAY') {
             foreach my $request (sort(@{$queue->{$item}})) {
                 my ($row,$approve,$reject,$showtime,$showsec,$namelink,
-                    $detailslink,$crstype,$instcode);
+                    $detailslink,$crstype,$instcode,$showrole,$adjudicator,
+                    $location,$showrequester);
                 $showtime = &Apache::lonlocal::locallocaltime($item);
                 if ($context eq 'course') {
                     my ($puname,$pudom,$pusec) = split(/:/,$request);
@@ -557,7 +695,7 @@
                 } elsif ($context eq 'requestauthor') {
                     if (&Apache::lonnet::homeserver($request,$dom) ne 'no_host') {
                         $approve = $count.':'.$request;
-                        $reject = $request; 
+                        $reject = $request;
                         $namelink = &Apache::loncommon::aboutmewrapper(
                                     &Apache::loncommon::plainname($request,$dom),
                                     $request,$dom);
@@ -571,6 +709,102 @@
                                      "'$dom','$request','$queued'".');">'.$request.'</a>';
                         $namelink = $request;
                     }
+                } elsif ($context eq 'othdomqueue') {
+                    my ($uname,$udom,$role,$adj,$requester,$sec) = split(/:/,$request);
+                    if ($adj eq 'user') {
+                        $adjudicator = &mt('Role Assignee');
+                    } elsif ($adj eq 'domain') {
+                        $adjudicator = &mt("[_1] in user's domain",
+                                           &Apache::lonnet::plaintext('dc'));
+                    }
+                    my $crstype;
+                    $showrole = &Apache::lonnet::plaintext($role,$crstype);
+                    unless (&Apache::lonnet::homeserver($uname,$udom) eq 'no_host') {
+                        $namelink = &Apache::loncommon::plainname($uname,$udom)." ($uname:$udom)";
+                    }
+                    if ($secondary eq 'course') {
+                        $showsec = $sec;
+                        if ($showsec eq '') {
+                            $showsec = &mt('none');
+                        }
+                    }
+                } elsif ($context eq 'othdomaction') {
+                    my ($extent,$role,$crstype);
+                    my ($info,$requester) = map { &unescape($_); } split(/:/,$request);
+                    if ($secondary eq 'user') {
+                        ($extent,$role) = split(/:/,$info);
+                        $approve = $count.':'.$extent.':'.$role;
+                        $reject = $extent.':'.$role;
+                    } elsif ($secondary eq 'domain') {
+                        (my $uname,$extent,$role) = split(/:/,$info);
+                        $approve = $count.':'.$extent.':'.$role.':'.$uname.':'.$dom;
+                        $reject = $extent.':'.$role.':'.$uname.':'.$dom;
+                        unless (&Apache::lonnet::homeserver($uname,$dom) eq 'no_host') {
+                            $namelink = &Apache::loncommon::plainname($uname,$dom);
+                            unless ($namelink eq $uname.':'.$dom) {
+                                $namelink .= ' ('.$uname.':'.$dom.')';
+                            }
+                        }
+                    }
+                    if (($role eq 'ca') || ($role eq 'aa')) {
+                        my ($audom,$auname) = ($extent =~ m{^/($match_domain)/($match_username)$});
+                        $location = &mt('Author').': '.&Apache::loncommon::plainname($auname,$audom);
+                    } elsif ($role eq 'co') {
+                        my ($cdom,$cnum)  = ($extent =~ m{^/($match_domain)/($match_courseid)});
+                        if (&Apache::lonnet::is_course($cdom,$cnum)) {
+                            my %info = &Apache::lonnet::coursedescription("$cdom/$cnum",{'one_time' => 1});
+                            $crstype = $info{'type'};
+                            $location = &mt('Community').': '.$info{'description'};
+                            $showrole = &Apache::lonnet::plaintext($role,'Community');
+                        }
+                    } elsif ($role =~ m{^cr/}) {
+                        $showrole = &Apache::lonnet::plaintext($role,$crstype);
+                        my ($cdom,$cnum,$csec)  = ($extent =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$});
+                        if (&Apache::lonnet::is_course($cdom,$cnum)) {
+                            my %info = &Apache::lonnet::coursedescription("$cdom/$cnum",{'one_time' => 1});
+                            $crstype = $info{'type'};
+                            $location = &mt($crstype).': '.$info{'description'};
+                            if ($csec ne '') {
+                                $location .= ' '.&mt('Section').': '.$csec;
+                            }
+                        }
+                    } else {
+                        foreach my $type ('course','domain') {
+                            if (grep(/^\Q$role\E$/,@{$roles_by_context{$type}})) {
+                                if ($type eq 'course') {
+                                    my ($cdom,$cnum,$csec) = ($extent =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$});
+                                    if (&Apache::lonnet::is_course($cdom,$cnum)) {
+                                        my %info = &Apache::lonnet::coursedescription("$cdom/$cnum",{'one_time' => 1});
+                                        $crstype = $info{'type'};
+                                        $location = &mt($crstype).': '.$info{'description'};
+                                        if ($csec ne '') {
+                                            $location .= ' '.&mt('Section').': '.$csec;
+                                        }
+                                    }
+                                } else {
+                                    my ($domain)  = ($extent =~ m{^/($match_domain)/});
+                                    $location = &mt('Domain').': '.&Apache::lonnet::domain($domain,'description');
+                                }
+                                last;
+                            }
+                        }
+                    }
+                    if ($role =~ m{^cr/($match_domain)/($match_username)/(\w+)$}) {
+                        my ($crudom,$cruname,$rolename) = ($1,$2,$3);
+                        my $creator = &Apache::loncommon::plainname($cruname,$crudom);
+                        unless ($creator eq $cruname.':'.$crudom) {
+                            $creator .= ' ('.$cruname.':'.$crudom.')';
+                        }
+                        $showrole = &mt('Custom role').': '.$rolename.'<br />'.
+                                    &mt('Created by').' '.$creator;
+                    } else {
+                        $showrole = &Apache::lonnet::plaintext($role,$crstype);
+                    }
+                    my ($requname,$requdom) = split(/:/,$requester);
+                    $showrequester = &Apache::loncommon::plainname($requname,$requdom);
+                    unless ($showrequester eq $requname.':'.$requdom) {
+                        $showrequester .= ' ('.$requname.':'.$requdom.')';
+                    }
                 } else {
                     my ($cnum,$ownername,$ownerdom,$type,$cdesc);
                     my $queued = 'approval'; 
@@ -592,7 +826,8 @@
                                 &Apache::loncommon::plainname($ownername,$ownerdom),
                                 $ownername,$ownerdom);
                 }
-                unless (($context eq 'pending') || ($context eq 'displaypending') || ($context eq 'helpdesk')) {
+                unless (($context eq 'pending') || ($context eq 'displaypending') || 
+                        ($context eq 'helpdesk') || ($context eq 'othdomqueue')) {
                     $row = '<td><span class="LC_nobreak"><label>'.
                            '<input type="radio" value="'.$approve.'" name="'.$count.'radioreq" />'.&mt('Approve').'</label>'.
                            '<label>'.(' 'x2).
@@ -601,7 +836,9 @@
                            '<input type="radio" value="'."later:".$reject.'" name="'.$count.'radioreq" checked />'.&mt('Decide Later').
                            '</label></span><br /></td>';
                 }
-                $row .= '<td>'.$namelink.'</td>'."\n";
+                unless (($context eq 'othdomqueue') || ($context eq 'othdomaction')) {
+                    $row .= '<td>'.$namelink.'</td>'."\n";
+                }
                 if ($context eq 'course') {
                     $row .= '<td>'.$showsec.'</td>'."\n".
                             '<td>'.$showtime.'</td>'."\n";
@@ -610,7 +847,25 @@
                 } elsif ($context eq 'requestusername') {
                     $row .= '<td>'.$showtime.'</td>'."\n".
                             '<td>'.$detailslink.'</td>'."\n";
-                } else { 
+                } elsif ($context eq 'othdomqueue') {
+                    $row .= '<td>'.$showtime.'</td>'."\n".
+                            '<td>'.$showrole.'</td>'."\n";
+                    if ($secondary eq 'course') {
+                        $row .= '<td>'.$showsec.'</td>'."\n";
+                    }
+                    $row .= '<td>'.$namelink.'</td>'."\n".
+                            '<td>'.$adjudicator.'</td>'."\n";
+                } elsif ($context eq 'othdomaction') {
+                    if ($secondary eq 'domain') {
+                        $row .= '<td>'.$showrequester.'</td>'."\n";
+                    }
+                    $row .= '<td>'.$showtime.'</td>'."\n".
+                            '<td>'.$showrole.'</td>'."\n".
+                            '<td>'.$location.'</td>'."\n";
+                    if ($secondary eq 'domain') {
+                        $row .= '<td>'.$namelink.'</td>'."\n";
+                    }
+                } else {
                     if ($context eq 'pending' || $context eq 'displaypending' || $context eq 'stillpending') {
                         $row .= '<td>'.$instcode.'</td>'."\n";
                     } else {
@@ -639,7 +894,8 @@
         @processing_errors, at warn_approves, at warn_rejects, at approvals, at warn_dels,
         @rejections, at rejectionerrors, at nopermissions,%courseroles, at toremove,
         %communityroles,%domdefs,%approvalmsg,%rejectionmsg,$crstype,$queue,
-        $firsturl,$uniquecode,%codes);
+        $firsturl,$uniquecode,%codes,%roles_by_context,%requesteractive,
+        %gotroles);
     my $count=0;
     while (my $item = $env{'form.'.$count.'radioreq'}) {
         if ($item =~ /^\d+:/) {
@@ -649,7 +905,6 @@
         }
         $count ++;
     }
-
     $now = time;
     $sender = $env{'user.name'}.':'.$env{'user.domain'};
     if ($context eq 'course') {
@@ -723,6 +978,32 @@
                             mt => 'Your request for a LON-CAPA account has not been approved.',
                         }];
         $domdesc = &Apache::lonnet::domain($cdom);
+    } elsif (($context eq 'othdombydc') || ($context eq 'othdombyuser')) {
+        $namespace = 'queuedrolereqs';
+        $beneficiary = 'requester';
+        foreach my $type ('domain','course') {
+            my @possroles = &Apache::lonuserutils::roles_by_context($type);
+            $roles_by_context{$type} = \@possroles;
+        }
+        if ($context eq 'othdombydc') {
+            my $confname = &Apache::lonnet::get_domainconfiguser($cdom);
+            %requesthash = &Apache::lonnet::dump($namespace,$cdom,$confname);
+            $approvedmsg = [{
+                                mt => 'The role assignment you requested for a user from another domain has been approved and the role assigned.',
+                            }];
+            $rejectedmsg = [{
+                                mt => 'The role assignment you requested for a user from another domain has not been approved.',
+                           }];
+        } else {
+            %requesthash = &Apache::lonnet::dump($namespace,$cdom,$cnum);
+            $approvedmsg = [{
+                                mt => 'The role assignment you requested for a user from another domain has been has been agreed to by the user.',
+                            }];
+            $rejectedmsg = [{
+                                mt => 'The role assignment you requested for a user from another domain has not been agreed to by the user.',
+                           }];
+        }
+        $domdesc = &Apache::lonnet::domain($cdom);
     } else {
         $domdesc = &Apache::lonnet::domain($cdom);
         $namespace = 'courserequestqueue';
@@ -948,6 +1229,146 @@
                 push(@invalidusers,$uname);
             }
             push(@toremove, at invalidusers);
+        } elsif (($context eq 'othdombydc') || ($context eq 'othdombyuser')) {
+            if ($context eq 'othdombydc') {
+                my ($num,$extent,$role,$uname,$udom) = split(/:/,$item);
+                my ($logmsg,$result);
+                if ($udom eq $cdom) {
+                    my $key = $uname.':'.$extent.':'.$role;
+                    if (exists($requesthash{$key})) {
+                        if (ref($requesthash{$key}) eq 'HASH') {
+                            my $requester = $requesthash{$key}->{'requester'};
+                            my ($requname,$requdom) = split(/:/,$requester);
+                            my $start = $requesthash{$key}->{'start'};
+                            my $end = $requesthash{$key}->{'end'};
+                            my $credits = $requesthash{$key}->{'credits'};
+                            my $reqcontext = $requesthash{$key}->{'context'};
+                            if ((&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') &&
+                                (&Apache::lonnet::homeserver($requname,$requdom) ne 'no_host')) {
+                                if (($role eq 'ca') || ($role eq 'aa')) {
+                                    my ($audom,$auname) = ($extent =~ m{^/($match_domain)/($match_username)$});
+                                    if (&Apache::lonnet::homeserver($auname,$audom) ne 'no_host') {
+                                        if ($requester eq $auname.':'.$audom) {
+                                            unless ($gotroles{$requester}) {
+                                                &requester_roles($auname,$audom,\%requesteractive);
+                                                $gotroles{$requester} = 1;
+                                            }
+                                            if (ref($requesteractive{$requester}) eq 'HASH') {
+                                                if ($requesteractive{$requester}{':'.$audom.':au'}) {
+                                                    $result = &Apache::lonnet::assignrole($udom,$uname,$extent,$role,
+                                                                                          $end,$start,'','',$reqcontext);
+                                                }
+                                            }
+                                        }
+                                    }
+                                } elsif (($role eq 'co') || ($role eq 'cc')) {
+                                    my ($crsdom,$crsnum)  = ($extent =~ m{^/($match_domain)/($match_courseid)});
+                                    if (&Apache::lonnet::is_course($crsdom,$crsnum)) {
+                                        my %info = &Apache::lonnet::coursedescription("$crsdom/$crsnum",{'one_time' => 1});
+                                        if ((($role eq 'co') && ($info{'type'} eq 'Community')) ||
+                                           (($role eq 'cc') && ($info{'type'} ne 'Community'))) {
+                                            if ($info{'internal.courseowner'} eq $requester) {
+                                                unless ($gotroles{$requester}) {
+                                                    &requester_roles($requname,$requdom,\%requesteractive);
+                                                    $gotroles{$requester} = 1;
+                                                }
+                                                if (ref($requesteractive{$requester}) eq 'HASH') {
+                                                    if ($requesteractive{$requester}{"$crsnum:$crsdom:$role"}) {
+                                                        $result =
+                                                            &Apache::loncommon::commit_standardrole($udom,$uname,$extent,$role,$start,
+                                                                                                    $end,$crsdom,$crsnum,'',
+                                                                                                    $reqcontext);
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                } elsif ($role =~ m{^(cr)/($match_domain)/($match_username)/(\w+)$}) {
+                                    my ($mrole,$crudom,$cruname,$rolename) = ($1,$2,$3,$4);
+                                    my ($crsdom,$crsnum,$csec) =
+                                        ($extent =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$});
+                                    if (&Apache::lonnet::is_course($crsdom,$crsnum)) {
+                                        my ($rdummy,$roledef) =
+                                            &Apache::lonnet::get('roles',["rolesdef_$rolename"],$crudom,$cruname);
+                                        if (($rdummy ne 'con_lost') && ($roledef ne '')) {
+                                            unless ($gotroles{$requester}) {
+                                                &requester_roles($requname,$requdom,\%requesteractive);
+                                                $gotroles{$requester} = 1;
+                                            }
+                                            if (ref($requesteractive{$requester}) eq 'HASH') {
+                                                if (&requester_has_perm($crsdom,$crsnum,$mrole,$requesteractive{$requester})) {
+                                                    $result = &Apache::loncommon::commit_customrole($udom,$uname,$extent,$crudom,$cruname,
+                                                                                                    $rolename,$start,$end,$reqcontext);
+                                                }
+                                            }
+                                        }
+                                    }
+                                } else {
+                                    my $process;
+                                    foreach my $type ('course','domain') {
+                                        if (grep(/^\Q$role\E$/,@{$roles_by_context{$type}})) {
+                                            if ($type eq 'course') {
+                                                my ($crsdom,$crsnum,$csec) = ($extent =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$});
+                                                if (&Apache::lonnet::is_course($crsdom,$crsnum)) {
+                                                    my $typeok;
+                                                    if ($role eq 'cc') {
+                                                        my %info = &Apache::lonnet::coursedescription("$crsdom/$crsnum",{'one_time' => 1});
+                                                        if ($info{'type'} eq 'Course') {
+                                                            $typeok = 1;
+                                                        }
+                                                    } else {
+                                                        $typeok = 1;
+                                                    }
+                                                    if ($typeok) {
+                                                        unless ($gotroles{$requester}) {
+                                                            &requester_roles($requname,$requdom,\%requesteractive);
+                                                            $gotroles{$requester} = 1;
+                                                        }
+                                                        if (ref($requesteractive{$requester}) eq 'HASH') {
+                                                            if (&requester_has_perm($crsdom,$crsnum,$role,$requesteractive{$requester})) {
+                                                                $result =
+                                                                    &Apache::loncommon::commit_standardrole($udom,$uname,$extent,$role,$start,
+                                                                                                            $end,$crsdom,$crsnum,$csec,
+                                                                                                            $reqcontext,$credits);
+                                                            }
+                                                        }
+                                                    }
+                                                }
+                                            } else {
+                                                my ($domain)  = ($extent =~ m{^/($match_domain)/});
+                                                if (&Apache::lonnet::domain($domain) ne '') {
+                                                    unless ($gotroles{$requester}) {
+                                                        &requester_roles($requname,$requdom,\%requesteractive);
+                                                        $gotroles{$requester} = 1;
+                                                    }
+                                                    if (&requester_has_perm($domain,'',$role,$requesteractive{$requester})) {
+                                                        $result = &Apache::lonnet::assignrole($udom,$uname,$extent,$role,
+                                                                                              $end,$start,'','',$reqcontext);
+                                                    }
+                                                }
+                                            }
+                                            last;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                if ($result eq 'ok') {
+                    push(@completed,$item);
+                }
+            } else {
+                my ($num,$extent,$role) = split(/:/,$item);
+                if (exists($requesthash{$extent.':'.$role})) {
+                    if (ref($requesthash{$extent.':'.$role}) eq 'HASH') {
+#FIXME
+#check if extent is valid
+#check if role is valid
+#check requester privs
+                    }
+                }
+            }
         } else {
             my ($num,$cnum) = split(':',$item);
             if (ref($requesthash{$cnum.'_'.$queue}) eq 'HASH') {
@@ -1093,6 +1514,8 @@
         @changes = map {$_.'_approval'} (@changes);
     } elsif ($context eq 'requestusername') {
         @changes = map {&escape($_).'_approval'} (@changes);
+    } elsif (($context eq 'othdombydc') || ($context eq 'othdombyuser')) {
+        @changes = ();
     }
     if (@rejections) {
         foreach my $item (@rejections) {
@@ -1147,6 +1570,8 @@
                 if ($userresult ne 'ok') {
                     push(@warn_rejects,$uname);
                 }
+            } elsif (($context eq 'othdombydc') || ($context eq 'othdombyuser')) {
+#FIXME
             } else {
                 my $cnum = $item;
                 if (ref($requesthash{$cnum.'_'.$queue}) eq 'HASH') {
@@ -1329,6 +1754,8 @@
                                                  $now,'usernamemanagers',$sender,
                                                  $approvedlist,$rejectedlist);
                 }
+            } elsif (($context eq 'othdombydc') || ($context eq 'othdombyuser')) {
+#FIXME
             } else {
                 $chgmsg = "'Action was taken on the following course and community requests by [_1].',$namelink";
                 if (@completed) {
@@ -1594,6 +2021,43 @@
     return $output;
 }
 
+sub requester_roles {
+    my ($requname,$requdom,$activeroles) = @_;
+    if (ref($activeroles) eq 'HASH') {
+        my %roleshash = &Apache::lonnet::get_my_roles($requname,$requdom,'userroles');
+        $activeroles->{$requname.':'.$requdom} = \%roleshash;
+    }
+    return;
+}
+
+sub requester_has_perm {
+    my ($crsdom,$crsnum,$mrole,$requesterroles) = @_;
+    return unless (ref($requesterroles) eq 'HASH');
+    my $has_perm;
+    foreach my $key (keys(%{$requesterroles})) {
+        if ($crsnum eq '') {
+            next unless ($key =~ /^\Q:$crsdom:\E/);
+        } else {
+            next unless (($key =~ /^\Q$crsnum:$crsdom:\E/) || ($key =~ /^\Q:$crsdom:\E/));
+        }
+        my ($keycrs,$keydom,$keyrole) = split(/:/,$key);
+        if (($keycrs ne '') && ($crsnum ne '')) {
+            if ($keycrs eq $crsnum) {
+                if ($Apache::lonnet::pr{$keyrole.':c'} =~ /(^|:)c\Q$mrole\E(&|:)/) {
+                    $has_perm = 1;
+                    last;
+                }
+            }
+        } else {
+            if ($Apache::lonnet::pr{$keyrole.':d'} =~ /(^|:)c\Q$mrole\E(&|:)/) {
+                $has_perm = 1;
+                last;
+            }
+        }
+    }
+    return $has_perm;
+}
+
 sub get_student_counts {
     my ($cdom,$cnum) = @_;
     my (%idx,%stucounts);
Index: loncom/interface/loncreateuser.pm
diff -u loncom/interface/loncreateuser.pm:1.464 loncom/interface/loncreateuser.pm:1.465
--- loncom/interface/loncreateuser.pm:1.464	Thu Nov 17 19:07:21 2022
+++ loncom/interface/loncreateuser.pm	Wed Nov 23 02:55:37 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Create a user
 #
-# $Id: loncreateuser.pm,v 1.464 2022/11/17 19:07:21 raeburn Exp $
+# $Id: loncreateuser.pm,v 1.465 2022/11/23 02:55:37 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -3977,7 +3977,13 @@
     my ($r,$context,$showcredits) = @_;
     my $now=time;
     my @rolechanges;
-    my %disallowed;
+    my (%disallowed,%got_role_approvals,%got_instdoms,%process_by,%instdoms,
+        %pending,%reject,%notifydc);
+    $got_role_approvals{$context} = '';
+    $process_by{$context} = {};
+    my @domroles = &Apache::lonuserutils::domain_roles();
+    my @cstrroles = &Apache::lonuserutils::construction_space_roles();
+    my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
     $r->print('<h3>'.&mt('Modifying Roles').'</h3>');
     foreach my $key (keys(%env)) {
 	next if (! $env{$key});
@@ -4079,6 +4085,7 @@
 	    if ($key=~/^form\.ren\:([^\_]+)\_([^\_\.]+)$/) {
                 my $url = $1;
                 my $role = $2;
+                my $id = $url.'_'.$role;
                 my $logmsg;
                 my $output;
                 if ($role eq 'st') {
@@ -4086,10 +4093,16 @@
                         my ($cdom,$cnum,$csec) = ($1,$2,$3);
                         my $credits;
                         if ($showcredits) {
-                            my $defaultcredits = 
+                            my $defaultcredits =
                                 &Apache::lonuserutils::get_defaultcredits($cdom,$cnum);
                             $credits = &get_user_credits($defaultcredits,$cdom,$cnum);
                         }
+                        unless ($udom eq $cdom) {
+                            next if (&Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                         $uname,$role,$now,0,$cdom,$cnum,$csec,$credits,
+                                         \%process_by,\%instdoms,\%got_role_approvals,
+                                         \%got_instdoms,\%reject,\%pending,\%notifydc));
+                        }
                         my $result = &Apache::loncommon::commit_studentrole(\$logmsg,$udom,$uname,$url,$role,$now,0,$cdom,$cnum,$csec,$context,$credits);
                         if (($result =~ /^error/) || ($result eq 'not_in_class') || ($result eq 'unknown_course') || ($result eq 'refused')) {
                             if ($result eq 'refused' && $logmsg) {
@@ -4105,6 +4118,22 @@
                         }
                     }
                 } else {
+                    my ($cdom,$cnum,$csec);
+                    if (grep(/^\Q$role\E$/, at cstrroles)) {
+                        ($cdom,$cnum) = ($url =~ m{^/($match_domain)/($match_username)$});
+                    } elsif (grep(/^\Q$role\E$/, at domroles)) {
+                        ($cdom) = ($url =~ m{^/($match_domain)/$});
+                    } elsif ($url =~ m-^/($match_domain)/($match_courseid)/?(\w*)$-) {
+                        ($cdom,$cnum,$csec) = ($1,$2,$3);
+                    }
+                    if ($cdom ne '') {
+                        unless ($udom eq $cdom) {
+                            next if (&Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                         $uname,$role,$now,0,$cdom,$cnum,$csec,'',\%process_by,
+                                         \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                         \%pending,\%notifydc));
+                        }
+                    }
 		    my $result=&Apache::lonnet::assignrole($env{'form.ccdomain'},
                                $env{'form.ccuname'},$url,$role,0,$now,'','',
                                $context);
@@ -4123,6 +4152,17 @@
 # Re-enable custom role
 	    if ($key=~m{^form\.ren\:([^_]+)_cr\.cr/($match_domain)/($match_username)/(\w+)$}) {
                 my ($url,$rdom,$rnam,$rolename) = ($1,$2,$3,$4);
+                my $id = $url.'_cr'."/$rdom/$rnam/$rolename";
+                my $role = "cr/$rdom/$rnam/$rolename";
+                if ($url =~ m-^/($match_domain)/($match_courseid)/?(\w*)$-) {
+                    my ($cdom,$cnum,$csec) = ($1,$2,$3);
+                    unless ($udom eq $cdom) {
+                        next if (&Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                     $uname,$role,$now,0,$cdom,$cnum,$csec,'',\%process_by,
+                                     \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                     \%pending,\%notifydc));
+                    }
+                }
                 my $result = &Apache::lonnet::assigncustomrole(
                                $env{'form.ccdomain'}, $env{'form.ccuname'},
                                $url,$rdom,$rnam,$rolename,0,$now,undef,$context);
@@ -4144,6 +4184,8 @@
                 # Activate a custom role
 		my ($one,$two,$three,$four,$five)=($1,$2,$3,$4,$5);
 		my $url='/'.$one.'/'.$two;
+                my $id = $url.'_cr/'."$three/$four/$five";
+                my $role = "cr/$three/$four/$five";
 		my $full=$one.'_'.$two.'_cr_cr_'.$three.'_'.$four.'_'.$five;
 
                 my $start = ( $env{'form.start_'.$full} ?
@@ -4152,15 +4194,22 @@
                 my $end   = ( $env{'form.end_'.$full} ?
                               $env{'form.end_'.$full} :
                               0 );
-                                                                                     
+
                 # split multiple sections
                 my %sections = ();
                 my $num_sections = &build_roles($env{'form.sec_'.$full},\%sections,$five);
                 if ($num_sections == 0) {
+                    unless ($udom eq $one) {
+                        next if (&Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                     $uname,$role,$start,$end,$one,$two,'','',\%process_by,
+                                     \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                     \%pending,\%notifydc));
+                    }
                     $r->print(&Apache::loncommon::commit_customrole($udom,$uname,$url,$three,$four,$five,$start,$end,$context));
                 } else {
 		    my %curr_groups =
 			&Apache::longroup::coursegroups($one,$two);
+                    my ($restricted,$numchanges);
                     foreach my $sec (sort {$a cmp $b} keys(%sections)) {
                         if (($sec eq 'none') || ($sec eq 'all') || 
                             exists($curr_groups{$sec})) {
@@ -4168,8 +4217,18 @@
                             next;
                         }
                         my $securl = $url.'/'.$sec;
+                        my $secid = $securl.'_cr'."/$three/$four/$five";
+                        undef($restricted);
+                        unless ($udom eq $one) {
+                            next if (&Apache::lonuserutils::restricted_dom($context,$secid,$udom,
+                                         $uname,$role,$start,$end,$one,$two,$sec,'',\%process_by,
+                                         \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                         \%pending,\%notifydc));
+                        }
+                        $numchanges ++;
 		        $r->print(&Apache::loncommon::commit_customrole($udom,$uname,$securl,$three,$four,$five,$start,$end,$context));
                     }
+                    next unless ($numchanges);
                 }
                 if (!grep(/^cr$/, at rolechanges)) {
                     push(@rolechanges,'cr');
@@ -4185,11 +4244,12 @@
 			      $env{'form.end_'.$one.'_'.$two.'_'.$three} :
 			      0 );
 		my $url='/'.$one.'/'.$two;
+                my $id = $url.'_'.$three;
                 my $type = 'three';
                 # split multiple sections
                 my %sections = ();
                 my $num_sections = &build_roles($env{'form.sec_'.$one.'_'.$two.'_'.$three},\%sections,$three);
-                my $credits;
+                my ($credits,$numchanges);
                 if ($three eq 'st') {
                     if ($showcredits) {
                         my $defaultcredits = 
@@ -4202,11 +4262,19 @@
                     }
                 }
                 if ($num_sections == 0) {
+                    unless ($udom eq $one) {
+                        next if (&Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                     $uname,$three,$start,$end,$one,$two,'',$credits,\%process_by,
+                                     \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                     \%pending,\%notifydc));
+                    }
+                    $numchanges ++;
                     $r->print(&Apache::loncommon::commit_standardrole($udom,$uname,$url,$three,$start,$end,$one,$two,'',$context,$credits));
                 } else {
                     my %curr_groups = 
 			&Apache::longroup::coursegroups($one,$two);
                     my $emptysec = 0;
+                    my $restricted;
                     foreach my $sec (sort {$a cmp $b} keys(%sections)) {
                         $sec =~ s/\W//g;
                         if ($sec ne '') {
@@ -4216,14 +4284,34 @@
                                 next;
                             }
                             my $securl = $url.'/'.$sec;
+                            my $secid = $securl.'_'.$three;
+                            unless ($udom eq $one) {
+                                undef($restricted);
+                                $restricted = &Apache::lonuserutils::restricted_dom($context,$secid,$udom,
+                                                  $uname,$three,$start,$end,$one,$two,$sec,$credits,\%process_by,
+                                                  \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                                  \%pending,\%notifydc);
+                                next if ($restricted);
+                            }
+                            $numchanges ++;
                             $r->print(&Apache::loncommon::commit_standardrole($udom,$uname,$securl,$three,$start,$end,$one,$two,$sec,$context,$credits));
                         } else {
                             $emptysec = 1;
                         }
                     }
                     if ($emptysec) {
+                        unless ($udom eq $one) {
+                            undef($restricted);
+                            $restricted = &Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                              $uname,$three,$start,$end,$one,$two,'',$credits,\%process_by,
+                                              \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                              \%pending,\%notifydc);
+                            next if ($restricted);
+                        }
+                        $numchanges ++;
                         $r->print(&Apache::loncommon::commit_standardrole($udom,$uname,$url,$three,$start,$end,$one,$two,'',$context,$credits));
                     }
+                    next unless ($numchanges);
                 }
                 if (!grep(/^\Q$three\E$/, at rolechanges)) {
                     push(@rolechanges,$three);
@@ -4240,24 +4328,54 @@
                 my $one = $1;
                 my $two = $2;
 		my $url='/'.$one.'/';
+                my $id = $url.'_'.$two;
                 # split multiple sections
                 my %sections = ();
+                my ($restricted,$numchanges);
                 my $num_sections = &build_roles($env{'form.sec_'.$one.'_'.$two},\%sections,$two);
                 if ($num_sections == 0) {
+                    unless ($udom eq $one) {
+                        $restricted = &Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                          $uname,$two,$start,$end,$one,$two,'','',\%process_by,
+                                          \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                          \%pending,\%notifydc);
+                        next if ($restricted);
+                    }
+                    $numchanges ++;
                     $r->print(&Apache::loncommon::commit_standardrole($udom,$uname,$url,$two,$start,$end,$one,undef,'',$context));
                 } else {
                     my $emptysec = 0;
                     foreach my $sec (sort {$a cmp $b} keys(%sections)) {
                         if ($sec ne '') {
                             my $securl = $url.'/'.$sec;
+                            my $secid = $securl.'_'.$two;
+                            unless ($udom eq $one) {
+                                undef($restricted);
+                                $restricted = &Apache::lonuserutils::restricted_dom($context,$secid,$udom,
+                                                  $uname,$two,$start,$end,$one,$two,$sec,'',\%process_by,
+                                                  \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                                  \%pending,\%notifydc);
+                                next if ($restricted);
+                            }
+                            $numchanges ++;
                             $r->print(&Apache::loncommon::commit_standardrole($udom,$uname,$securl,$two,$start,$end,$one,undef,$sec,$context));
                         } else {
                             $emptysec = 1;
                         }
                     }
                     if ($emptysec) {
+                        unless ($udom eq $one) {
+                            undef($restricted);
+                            $restricted = &Apache::lonuserutils::restricted_dom($context,$id,$udom,
+                                              $uname,$two,$start,$end,$one,$two,'','',\%process_by,
+                                              \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject,
+                                              \%pending,\%notifydc);
+                            next if ($restricted);
+                        }
+                        $numchanges ++;
                         $r->print(&Apache::loncommon::commit_standardrole($udom,$uname,$url,$two,$start,$end,$one,undef,'',$context));
                     }
+                    next unless ($numchanges); 
                 }
                 if (!grep(/^\Q$two\E$/, at rolechanges)) {
                     push(@rolechanges,$two);
@@ -4281,6 +4399,12 @@
             }
 	}
     } # End of foreach (keys(%env))
+    if (keys(%reject)) {
+        $r->print(&Apache::lonuserutils::print_roles_rejected($context,\%reject));
+    }
+    if (keys(%pending)) {
+        $r->print(&Apache::lonuserutils::print_roles_queued($context,\%pending,\%notifydc));
+    }
 # Flush the course logs so reverse user roles immediately updated
     $r->register_cleanup(\&Apache::lonnet::flushcourselogs);
     if (@rolechanges == 0) {
@@ -4335,11 +4459,34 @@
             }
         }
     }
+    my ($startdate,$enddate) = &Apache::lonuserutils::get_dates_from_form();
+    my (%got_role_approvals,%got_instdoms,%process_by,%instdoms,%pending,%reject,%notifydc);
+    unless ($env{'form.ccdomain'} eq $env{'course.'.$env{'request.course.id'}.'.domain'}) {
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $csec = $env{'form.sections'};
+        my $id = "/$cdom/$cnum";
+        if ($csec ne '') {
+            $id .= "/$csec";
+        }
+        $id .= '_st';
+        if (&Apache::lonuserutils::restricted_dom($context,$id,$env{'form.ccdomain'},$env{'form.ccuname'},
+                                                  'st',$startdate,$enddate,$cdom,$cnum,$csec,$credits,
+                                                  \%process_by,\%instdoms,\%got_role_approvals,\%got_instdoms,
+                                                  \%reject,\%pending,\%notifydc)) {
+            if (keys(%reject)) {
+                $r->print(&Apache::lonuserutils::print_roles_rejected($context,\%reject));
+            }
+            if (keys(%pending)) {
+                $r->print(&Apache::lonuserutils::print_roles_queued($context,\%pending,\%notifydc));
+            }
+            return;
+        }
+    }
 
     # Clean out any old student roles the user has in this class.
     &Apache::lonuserutils::modifystudent($env{'form.ccdomain'},
          $env{'form.ccuname'},$env{'request.course.id'},undef,$uhome);
-    my ($startdate,$enddate) = &Apache::lonuserutils::get_dates_from_form();
     my $enroll_result =
         &Apache::lonnet::modify_student_enrollment($env{'form.ccdomain'},
             $env{'form.ccuname'},$env{'form.cid'},$env{'form.cfirstname'},
@@ -4701,6 +4848,31 @@
     );
 }
 
+sub show_role_requests {
+    my ($caller,$dom) = @_;
+    my $showrolereqs;
+    my %domconfig = &Apache::lonnet::get_dom('configuration',['privacy'],$dom);
+    if (ref($domconfig{'privacy'}) eq 'HASH') {
+        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}})) {
+                        foreach my $context ('domain','author','course','community') {
+                            if ($approvalconf{$key}{$context} eq $caller) {
+                                $showrolereqs = 1;
+                                last if ($showrolereqs);
+                            }
+                        }
+                    }
+                }
+                last if ($showrolereqs);
+            }
+        }
+    }
+    return $showrolereqs;
+}
+
 # ================================================================ Main Handler
 sub handler {
     my $r = shift;
@@ -5381,6 +5553,26 @@
             $r->print(&header(undef,{'no_nav_bar' => 1}).
                       '<span class="LC_error">'.&mt('You do not have permission to view helpdesk access').'</span>');
         }
+    } elsif ($env{'form.action'} eq 'rolerequests') {
+        if ($permission->{cusr} || $permission->{view}) {
+            &print_queued_roles($r,$context,$permission,$brcrum);
+        }
+    } elsif ($env{'form.action'} eq 'queuedroles') {
+        if (($permission->{cusr}) && ($context eq 'domain')) {
+            if (&show_role_requests($context,$env{'request.role.domain'})) {
+                if ($env{'form.state'} eq 'done') {
+                    &process_pendingroles($r,$context,$permission,$brcrum);
+                } else {
+                    &print_pendingroles($r,$context,$permission,$brcrum);
+                }
+            } else {
+                $r->print(&header(undef,{'no_nav_bar' => 1}).
+                          '<span class="LC_info">'.&mt('Domain coordinator approval of requests from other domains for assignment of roles to users from this domain not in use.').'</span>');
+            }
+        } else {
+            $r->print(&header(undef,{'no_nav_bar' => 1}).
+                     '<span class="LC_error">'.&mt('You do not have permission to view queued requests from other domains for assignment of roles to users from this domain.').'</span>');
+        }
     } else {
         $bread_crumbs_component = 'User Management';
         $args = { bread_crumbs           => $brcrum,
@@ -5632,6 +5824,7 @@
                             listusers  => 'Show and manage users in this community.',
                            },
                 );
+
   if ($linkcontext eq 'domain') {
       unless ($permission->{'cusr'}) {
           $links{'domain'}{'singleuser'} = 'View a User';
@@ -5877,6 +6070,28 @@
             },
         );
     }
+    push(@{ $menu[2]->{items} },
+        {
+         linktext => 'Role Requests (other domains)',
+         icon => 'edit-find.png',
+         #help => 'Role_Requests',
+         url => '/adm/createuser?action=rolerequests',
+         permission => $permission->{'cusr'},
+         linktitle => 'Role requests for users in other domains',
+        },
+    );
+    if (&show_role_requests($context,$env{'request.role.domain'})) {
+        push(@{ $menu[2]->{items} },
+            {
+             linktext => 'Queued Role Assignments (this domain)',
+             icon => 'edit-find.png',
+             #help => 'Role_Approvals',
+             url => '/adm/createuser?action=queuedroles',
+             permission => $permission->{'cusr'},
+             linktitle => "Role requests for this domain's users",
+            },
+        );
+    }
     return Apache::lonhtmlcommon::generate_menu(@menu);
 #               { text => 'View Log-in History',
 #                 help => 'Course_User_Logins',
@@ -7961,6 +8176,67 @@
     return;
 }
 
+sub print_queued_roles {
+    my ($r,$context,$permission,$brcrum) = @_;
+    push (@{$brcrum},
+             {href => '/adm/createuser?action=rolerequests',
+              text => 'Role Requests (other domains)',
+              help => ''});
+    my $bread_crumbs_component = 'Role Requests';
+    my $args = { bread_crumbs           => $brcrum,
+                 bread_crumbs_component => $bread_crumbs_component};
+    # print page header
+    $r->print(&header('',$args));
+    my ($dom,$cnum);
+    $dom = $env{'request.role.domain'};
+    if ($context eq 'course') {
+        if ($env{'request.course.id'}) {
+            if (&Apache::loncommon::course_type() eq 'Community') {
+                $context = 'community';
+            }
+            $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        }
+    } elsif ($context eq 'author') {
+        $cnum = $env{'user.name'};
+    }
+    $r->print(&Apache::loncoursequeueadmin::display_queued_requests('othdomqueue',$dom,$cnum,$context));
+    return;
+}
+
+sub print_pendingroles {
+    my ($r,$context,$permission,$brcrum) = @_;
+    push (@{$brcrum},
+             {href => '/adm/createuser?action=queuedroles',
+              text => 'Queued Role Assignments (users in this domain)',
+              help => ''});
+    my $bread_crumbs_component = 'Queued Role Assignments';
+    my $args = { bread_crumbs           => $brcrum,
+                 bread_crumbs_component => $bread_crumbs_component};
+    # print page header
+    $r->print(&header('',$args));
+    $r->print(&Apache::loncoursequeueadmin::display_queued_requests('othdomaction',$env{'request.role.domain'},'','domain'));
+    return;
+}
+
+sub process_pendingroles {
+    my ($r,$context,$permission,$brcrum) = @_;
+    push (@{$brcrum},
+             {href => '/adm/createuser?action=queuedroles',
+              text => 'Queued Role Assignments (users in this domain)',
+              help => ''},
+             {href => '/adm/createuser?action=processrolereq',
+              text => 'Process Queue',
+              help => ''});
+    my $bread_crumbs_component = 'Queued Role Assignments';
+    my $args = { bread_crumbs           => $brcrum,
+                 bread_crumbs_component => $bread_crumbs_component};
+    # print page header
+    $r->print(&header('',$args));
+    $r->print(&Apache::loncoursequeueadmin::update_request_queue('othdombydc',
+                                                                 $env{'request.role.domain'}));
+    return;
+}
+
 sub domain_adhoc_access {
     my ($roles,$domcurrent,$accesstypes,$usertypes,$othertitle) = @_;
     my %domusage;
Index: loncom/interface/lonuserutils.pm
diff -u loncom/interface/lonuserutils.pm:1.211 loncom/interface/lonuserutils.pm:1.212
--- loncom/interface/lonuserutils.pm:1.211	Thu Nov 17 19:07:21 2022
+++ loncom/interface/lonuserutils.pm	Wed Nov 23 02:55:37 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Utility functions for managing LON-CAPA user accounts
 #
-# $Id: lonuserutils.pm,v 1.211 2022/11/17 19:07:21 raeburn Exp $
+# $Id: lonuserutils.pm,v 1.212 2022/11/23 02:55:37 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -50,6 +50,7 @@
 use Apache::lonnet;
 use Apache::loncommon();
 use Apache::lonhtmlcommon;
+use Apache::loncoursequeueadmin;
 use Apache::lonlocal;
 use Apache::longroup;
 use HTML::Entities;
@@ -153,6 +154,357 @@
     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$/, at 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$/, at 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) = @_;
+    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'));
+    my (%approval, at 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$/, at inst)) {
+                if (exists($approval{'instdom'})) {
+                    my $rule = $approval{'instdom'};
+                    if ($rule eq 'none') {
+                        $reject->{$key} = {
+                                            cdom  => $cdom,
+                                            cnum  => $cnum,
+                                            csec  => $csec,
+                                            udom  => $udom,
+                                            uname => $uname,
+                                            role  => $role,
+                                          };
+                        $skip = 1;
+                    } elsif (($rule eq 'user') || ($rule eq 'domain')) {
+                        $pending->{$key} = {
+                                             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}->{'credits'} = $credits;
+                        }
+                        $skip = 1;
+                    }
+                }
+            } elsif (exists($approval{'extdom'})) {
+                my $rule = $approval{'extdom'};
+                if ($rule eq 'none') {
+                    $reject->{$key} = {
+                                        cdom  => $cdom,
+                                        cnum  => $cnum,
+                                        csec  => $csec,
+                                        udom  => $udom,
+                                        uname => $uname,
+                                        role  => $role,
+                                      };
+                    $skip = 1;
+                } elsif (($rule eq 'user') || ($rule eq 'domain')) {
+                    $pending->{$key} = {
+                                         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}->{'credits'} = $credits;
+                    }
+                    $skip = 1;
+                }
+            }
+        }
+    }
+    return $skip;
+}
+
+sub print_roles_rejected {
+    my ($context,$reject) = @_;
+    return unless (ref($reject) eq 'HASH');
+    my $output;
+    if (keys(%{$reject}) > 0) {
+        $output = '<p class="LC_warning">'.
+                  &mt("The following roles could not be assigned because the user is from another domain, and that domain's policies disallow it").'<ul>';
+        foreach my $key (sort(keys(%{$reject}))) {
+            if (ref($reject->{$key}) eq 'HASH') {
+                my ($crstype,$role,$cdom,$cnum,$title);
+                $role = $reject->{$key}{'role'};
+                $cdom = $reject->{$key}{'cdom'};
+                $cnum = $reject->{$key}{'cnum'};
+                if ($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);
+                    }
+                } elsif ($context eq 'course') {
+                    $crstype = &Apache::loncommon::course_type();
+                }
+                my $plainrole = &Apache::lonnet::plaintext($reject->{$key}{'role'},$crstype);
+                $output .= '<li>'.&mt('User: [_1]',$reject->{$key}{'uname'}).' | '.
+                                  &mt('Domain: [_1]',$reject->{$key}{'udom'}).' | '.
+                                  &mt('Role: [_1]',$plainrole);
+                if ($crstype) {
+                    $output .= ' | '.&mt('Section: [_1]',$reject->{$key}{'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 .= '</li>';
+            }
+        }
+        $output .= '</ul></p>';
+    }
+    return $output;
+}
+
+sub print_roles_queued {
+    my ($context,$pending,$notifydc) = @_;
+    return unless ((ref($pending) eq 'HASH') && (ref($notifydc) eq 'HASH'));
+    my $output;
+    if (keys(%{$pending}) > 0) {
+        my $now = time;
+        $output = '<p class="LC_warning">'.
+                  &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").'<ul>';
+        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') {
+                my $role = $pending->{$key}{'role'};
+                my $uname = $pending->{$key}{'uname'};
+                my $udom = $pending->{$key}{'udom'};
+                my $csec = $pending->{$key}{'csec'};
+                my $cdom = $pending->{$key}{'cdom'};
+                my $cnum = $pending->{$key}{'cnum'};
+                my $adj = $pending->{$key}{'adj'};
+                my $start = $pending->{$key}{'start'};
+                my $end = $pending->{$key}{'end'};
+                my $credits = $pending->{$key}{'credits'};
+                my $now = time;
+                my ($crstype,$title);
+                if ($context eq 'course') {
+                    $crstype = &Apache::loncommon::course_type();
+                } 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);
+                    }
+                }
+                my $plainrole = &Apache::lonnet::plaintext($role,$crstype);
+                my $extent = "/$cdom/$cnum";
+                $output .= '<li>'.&mt('User: [_1]',$uname).' | '.
+                                  &mt('Domain: [_1]',$udom).' | '.
+                                  &mt('Role: [_1]',$plainrole).' | ';
+                if ($crstype) {
+                    if ($csec ne '') {
+                        $output .= &mt('Section: [_1]',$csec).' | ';
+                        $extent .= "/$csec";
+                    }
+                }
+                if ($adj eq 'user') {
+                    $output .= '<br />'.&mt('Message sent to user for approval');
+                    $touser{$uname.':'.$udom}{$extent.':'.$role} = {
+                                                                     timestamp => $now,
+                                                                     requester => $requester,
+                                                                     start     => $start,
+                                                                     end       => $end,
+                                                                     credits   => $credits,
+                                                                     context   => $context,
+                                                                   };
+                } elsif ($adj eq 'domain') {
+                    $output .= '<br />'.&mt("Message sent to user's domain coordinator for approval");
+                    $todom{$udom}{$uname.':'.$extent.':'.$role} = {
+                                                                    timestamp => $now,
+                                                                    requester => $requester,
+                                                                    start     => $start,
+                                                                    end       => $end,
+                                                                    credits   => $credits,
+                                                                    context   => $context,
+                                                                  };
+                }
+                $output .= '</li>';
+                my $id = $uname.':'.$udom.':'.$role;
+                if (($context eq 'course') || ($crstype)) {
+                    $id .= ':'.$csec;
+                    $crsqueue{$cdom.'_'.$cnum}{$id} = {
+                                                        timestamp => $now,
+                                                        requester => $requester,
+                                                        adj       => $adj,
+                                                      };
+                } elsif (($context eq 'author') ||
+                         (($context eq 'domain') && (($role eq 'ca') || ($role eq 'aa')))) {
+                    $caqueue{$cnum.':'.$cdom}{$id} = {
+                                                        timestamp => $now,
+                                                        requester => $requester,
+                                                        adj       => $adj,
+                                                      };
+                } elsif ($context eq 'domain') {
+                    $domqueue{$id} = {
+                                        timestamp => $now,
+                                        requester => $requester,
+                                        adj       => $adj,
+                                     };
+                }
+            }
+        }
+        $output .= '</ul></p>';
+        if (keys(%touser)) {
+            foreach my $key (keys(%touser)) {
+                my ($uname,$udom) = split(/:/,$touser{$key});
+                if (&Apache::lonnet::put('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('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('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('othdomqueued',$caqueue{$key},$audom,$auname);
+                }
+            }
+        }
+        if (keys(%domqueue)) {
+            my $confname = &Apache::lonnet::get_domainconfiguser($env{'request.role.domain'});
+            &Apache::lonnet::put('othdomqueued',\%domqueue,$env{'request.role.domain'},$confname);
+        }
+    }
+    return $output;
+}
+
 sub propagate_id_change {
     my ($uname,$udom,$user) = @_;
     my (@types, at roles);
@@ -4168,7 +4520,7 @@
         $fieldstype{$field.'_choice'} = 'scalar';
     }
     &Apache::loncommon::store_course_settings('enrollment_upload',\%fieldstype);
-    my ($cid,$crstype,$setting,$crsdom);
+    my ($cid,$crstype,$setting,$crsdom,$crsnum);
     if ($context eq 'domain') {
         $setting = $env{'form.roleaction'};
     }
@@ -4176,11 +4528,13 @@
         $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'};
         }
     }
     my ($startdate,$enddate) = &get_dates_from_form();
@@ -4365,6 +4719,8 @@
             $r->print('<h3>'.&mt('Adding/Modifying Users')."</h3>\n<p>\n");
         }
         $r->rflush;
+        my (%got_role_approvals,%got_instdoms,%process_by,%instdoms,
+            %pending,%reject,%notifydc);
 
         my %counts = (
                        user => 0,
@@ -4897,6 +5253,17 @@
                                 $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));
+                        }
                         &modifystudent($userdomain,$username,$cid,$sec,
                                        $desiredhost,$context);
                         $roleresult =
@@ -4908,16 +5275,33 @@
                                  '',$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));
+                                            }
                                             ($userres{$sec},$authres{$sec},$roleres{$sec},$idres{$sec}) =
                                             &modifyuserrole($context,$setting,
                                                 $changeauth,$cid,$userdomain,$username,
@@ -4931,9 +5315,21 @@
                                         $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));
+                            }
                             ($userresult,$authresult,$roleresult,$idresult) = 
                                 &modifyuserrole($context,$setting,
                                                 $changeauth,$cid,$userdomain,$username, 
@@ -4983,6 +5379,12 @@
         }
         $r->print(&print_namespacing_alerts($domain,\%alerts,\%curr_rules));
         $r->print(&passwdrule_alerts($domain,\%showpasswdrules));
+        if (keys(%reject)) {
+            $r->print(&print_roles_rejected($context,\%reject));
+        }
+        if (keys(%pending)) {
+            $r->print(&print_roles_queued($context,\%pending,\%notifydc));
+        }
         #####################################
         # Display list of students to drop  #
         #####################################
@@ -5176,7 +5578,8 @@
     if ($context eq 'course') {
         $crstype = &Apache::loncommon::course_type();
     }
-    my @changelist;
+    my (@changelist,%got_role_approvals,%got_instdoms,%process_by,%instdoms,
+        %pending,%reject,%notifydc);
     if ($choice eq 'drop') {
         @changelist = &Apache::loncommon::get_env_multiple('form.droplist');
     } else {
@@ -5206,7 +5609,7 @@
     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';
@@ -5223,6 +5626,8 @@
                 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 '') {
@@ -5233,15 +5638,20 @@
         } 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;
@@ -5283,7 +5693,11 @@
                 $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));
                 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 {
@@ -5292,6 +5706,9 @@
                                                     $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));
                 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 {
@@ -5299,6 +5716,9 @@
                                             $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));
                 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 {
@@ -5508,6 +5928,12 @@
             $r->print(&make_dates_default($startdate,$enddate,$context,$crstype));
         }
     }
+    if (keys(%reject)) {
+        $r->print(&print_roles_rejected($context,\%reject));
+    }
+    if (keys(%pending)) {
+        $r->print(&print_roles_queued($context,\%pending,\%notifydc));
+    }
     my $linktext = &mt('Display User Lists');
     if ($choice eq 'drop') {
         $linktext = &mt('Display current class roster');
Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.1501 loncom/lonnet/perl/lonnet.pm:1.1502
--- loncom/lonnet/perl/lonnet.pm:1.1501	Sat Nov  5 06:00:07 2022
+++ loncom/lonnet/perl/lonnet.pm	Wed Nov 23 02:55:37 2022
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1501 2022/11/05 06:00:07 raeburn Exp $
+# $Id: lonnet.pm,v 1.1502 2022/11/23 02:55:37 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -10334,7 +10334,7 @@
     if ($role =~ /^cr\//) {
         my $cwosec=$url;
         $cwosec=~s/^\/($match_domain)\/($match_courseid)\/.*/$1\/$2/;
-	unless (&allowed('ccr',$cwosec)) {
+        if ((!&allowed('ccr',$cwosec)) && (!&allowed('ccr',$udom))) {
            my $refused = 1;
            if ($context eq 'requestcourses') {
                if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) {


More information about the LON-CAPA-cvs mailing list