[LON-CAPA-cvs] cvs: doc /loncapafiles loncapafiles.lpml loncom/homework grades.pm inputtags.pm lonhomework.pm structuretags.pm loncom/html/res/adm/pages viewuser.png loncom/interface loncommon.pm loncourseuser.pm lonmenu.pm

raeburn raeburn at source.lon-capa.org
Sat Apr 1 23:16:31 EDT 2023


raeburn		Sun Apr  2 03:16:31 2023 EDT

  Added files:                 
    /loncom/interface	loncourseuser.pm 
    /loncom/html/res/adm/pages	viewuser.png 

  Modified files:              
    /loncom/interface	lonmenu.pm loncommon.pm 
    /loncom/homework	inputtags.pm structuretags.pm lonhomework.pm 
                    	grades.pm 
    /doc/loncapafiles	loncapafiles.lpml 
  Log:
  - Bug 6979
    - Shortcut to student's view of problem etc. from 'View As' item in Functions
    menu above problem.
    - Part of code in &submission() subroutine in grades.pm moved to separate
    subroutine -- &show_last_submission() which can be called by &view_as_user(),
    which in turn is called from lonhomework.pm 
  
  
-------------- next part --------------
Index: loncom/interface/lonmenu.pm
diff -u loncom/interface/lonmenu.pm:1.529 loncom/interface/lonmenu.pm:1.530
--- loncom/interface/lonmenu.pm:1.529	Sat Oct 29 14:47:00 2022
+++ loncom/interface/lonmenu.pm	Sun Apr  2 03:16:26 2023
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Routines to control the menu
 #
-# $Id: lonmenu.pm,v 1.529 2022/10/29 14:47:00 raeburn Exp $
+# $Id: lonmenu.pm,v 1.530 2023/04/02 03:16:26 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1062,6 +1062,69 @@
                     'Folder/Page Content');
         }
 # End modifiable folder/page container check
+
+#
+# Determine whether to show View As button for shortcut to display problem, answer, and submissions
+#
+
+        if (($env{'request.symb'} ne '') &&
+            ($env{'request.filename'}=~/$LONCAPA::assess_re/) &&
+            (($perms{'mgr'}) || ($perms{'vgr'}))) {
+            my ($viewas,$text,$change,$visibility,$vuname,$vudom,$vid,$leftvis,$defdom,$righticon);
+            my %lt = &Apache::lonlocal::texthash(
+                                                 view => 'View',
+                                                 upda => 'Update',
+            );
+            if ($env{'request.user_in_effect'} =~ /^($match_username):($match_domain)$/) {
+                ($vuname,$vudom) = ($1,$2);
+                $vid = (&Apache::lonnet::idrget($vudom,$vuname))[1];
+                $viewas = $env{'request.user_in_effect'};
+                $text = $lt{'upda'};
+                $change = 'off';
+                $visibility = 'inline';
+                $leftvis = 'none';
+                $defdom = $vudom;
+                $righticon = '✖';
+            } else {
+                $text = $lt{'view'};
+                $change = 'on';
+                $visibility = 'none';
+                $leftvis = 'inline';
+                $defdom = $cdom;
+            }
+            my $sellink = &Apache::loncommon::selectstudent_link('userview','vuname','vudom');
+            my $selscript=&Apache::loncommon::studentbrowser_javascript();
+            my $shownsymb = &HTML::Entities::encode(&Apache::lonenc::check_encrypt($env{'request.symb'}),'<>&"');
+            my $input = &mt('User: [_1] or ID: [_2] at: [_3]',
+                            '<input name="vuname" type="text" size="8" value="'.$vuname.'" />',
+                            '<input name="vid" type="text" size="8" value="'.$vid.'" />',
+                            &Apache::loncommon::select_dom_form($defdom,'vudom')).
+                            '<input name="LC_viewas" type="hidden" value="'.$viewas.'" />',
+                            '<input name="symb" type="hidden" value="'.$shownsymb.'" />';
+            my $chooser = <<END;
+$selscript
+<a href="javascript:toggleViewAsUser('$change');" class="LC_menubuttons_link">
+<span id="usexpand" class="LC_menubuttons_inline_text" style="display:$leftvis">► </span>
+</a>
+<fieldset id="LC_selectuser" style="display:$visibility">
+<form name="userview" action="" method="post" onsubmit="event.preventDefault(); return validCourseUser(this,'$change');">
+<span class="LC_menubuttons_inline_text LC_nobreak">
+$input
+$sellink
+</span>
+ <input type="submit" value="$text" />
+</form>
+</fieldset>
+<a href="javascript:toggleViewAsUser('$change');" class="LC_menubuttons_link">
+<span id="uscollapse" class="LC_menubuttons_inline_text">$righticon</span>
+</a>
+END
+            &switch('','',7,5,'viewuser.png','View As','user[_1]',
+                    'toggleViewAsUser('."'$change'".')',
+                    'View As','','',$chooser);
+        }
+# End view as user check
+
     }
 # End course context
 
@@ -1641,7 +1704,7 @@
                 'advtools', @funcs[61,73,74,71,72]);
         } else {
             &Apache::lonhtmlcommon::add_breadcrumb_tool(
-                'advtools', @funcs[61,71,72,73,74,92]);
+                'advtools', @funcs[61,71,72,73,74,75,92]);
         }
     } elsif ($env{'request.noversionuri'} eq '/adm/viewclasslist') {
         &Apache::lonhtmlcommon::add_breadcrumb_tool(
@@ -1663,7 +1726,7 @@
 # The javascript is usually similar to "go('/adm/roles')" or "cstrgo(..)".
 
 sub switch {
-    my ($uname,$udom,$row,$col,$img,$top,$bot,$act,$desc,$cat,$nobreak)=@_;
+    my ($uname,$udom,$row,$col,$img,$top,$bot,$act,$desc,$cat,$nobreak,$form)=@_;
     $act=~s/\$uname/$uname/g;
     $act=~s/\$udom/$udom/g;
     $top=&mt($top);
@@ -1714,7 +1777,7 @@
         } else {
             $inlineremote[$idx] =
        '<a title="'.$desc.'" class="LC_menubuttons_link" href="javascript:'.$act.';">'.$pic.
-       '<span class="LC_menubuttons_inline_text">'.$top.' </span></a>';
+       '<span class="LC_menubuttons_inline_text">'.$top.' </span></a>'.$form;
         }
     }
     return '';
@@ -2255,6 +2318,79 @@
     }
 }
 
+sub view_as_js {
+    my ($url,$symb) = @_;
+    my %lt = &Apache::lonlocal::texthash(
+                ente => 'Enter a username or a student/employee ID',
+                info => 'Information you entered does not match a valid course user',
+    );
+    &js_escape(\%lt);
+    return <<"END";
+
+function toggleViewAsUser(change) {
+    var seluserid = document.getElementById('LC_selectuser');
+    var currstyle = seluserid.style.display;
+    if (change == 'off') {
+        document.userview.elements['LC_viewas'].value = '';
+        document.userview.elements['vuname'].value = '';
+        document.userview.elements['vid'].value = '';
+        document.userview.submit();
+        return;
+    }
+    if (currstyle == 'inline') {
+        seluserid.style.display = 'none';
+        document.getElementById('usexpand').innerHTML='► ';
+        document.getElementById('uscollapse').innerHTML='';
+    } else {
+        seluserid.style.display = 'inline';
+        document.getElementById('usexpand').innerHTML='';
+        document.getElementById('uscollapse').innerHTML='◄ ';
+    }
+    return;
+}
+
+function validCourseUser(form,change) {
+    var possuname = form.elements['vuname'].value;
+    var possuid = form.elements['vid'].value;
+    var possudom = form.elements['vudom'].options[form.elements['vudom'].selectedIndex].value;
+    if ((possuname == '') && (possuid == '')) {
+        if (change == 'off') {
+            form.elements['LC_viewas'].value = '';
+            form.submit();
+        } else {
+            alert("$lt{'ente'}");
+        }
+        return;
+    }
+    var http = new XMLHttpRequest();
+    var url = "/adm/courseuser";
+    var params = "uname="+possuname+"&uid="+possuid+"&udom="+possudom;
+    http.open("POST", url, true);
+    http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+    http.onreadystatechange = function() {
+        if (http.readyState == 4 && http.status == 200) {
+            var data = JSON.parse(http.responseText);
+            if (Array.isArray(data.match)) {
+               var len = data.match.length;
+               if (len == 2) {
+                   if (data.match[0] != '' && data.match[1] != '') {
+                       form.elements['LC_viewas'].value = data.match[0]+':'+data.match[1];
+                       form.submit(); 
+                   }
+               } else {
+                   alert("$lt{'info'}");
+               }
+            }
+        }
+        return;
+    }
+    http.send(params);
+    return false;
+}
+
+END
+}
+
 sub utilityfunctions {
     my ($httphost) = @_;
     my $currenturl=&Apache::lonnet::clutter(&Apache::lonnet::fixversion((split(/\?/,$env{'request.noversionuri'}))[0]));
@@ -2296,6 +2432,24 @@
 
     my $countdown = &countdown_toggle_js();
 
+    my $viewuser;
+    if (($env{'request.course.id'}) &&
+        ($env{'request.symb'} ne '') &&
+        ($env{'request.filename'}=~/$LONCAPA::assess_re/)) {
+        my $canview;
+        foreach my $priv ('msg','vgr') {
+            $canview = &Apache::lonnet::allowed($priv,$env{'request.course.id'});
+            if (!$canview && $env{'request.course.sec'} ne '') {
+                $canview =
+                    &Apache::lonnet::allowed($priv,"$env{'request.course.id'}/$env{'request.course.sec'}");
+            }
+            last if ($canview);
+        }
+        if ($canview) {
+            $viewuser = &view_as_js($esc_url,$esc_symb);
+        }
+    }
+
     my ($ltitarget,$deeplinktarget);
     if ($env{'request.lti.login'}) {
         $ltitarget = $env{'request.lti.target'};
@@ -2512,6 +2666,8 @@
 
 $countdown
 
+$viewuser
+
 ENDUTILITY
 }
 
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.1403 loncom/interface/loncommon.pm:1.1404
--- loncom/interface/loncommon.pm:1.1403	Wed Mar 29 16:01:13 2023
+++ loncom/interface/loncommon.pm	Sun Apr  2 03:16:27 2023
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1403 2023/03/29 16:01:13 raeburn Exp $
+# $Id: loncommon.pm,v 1.1404 2023/04/02 03:16:27 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -8160,6 +8160,11 @@
   /* overflow: hidden; */
 }
 
+fieldset#LC_selectuser {
+    margin: 0;
+    padding: 0;
+}
+
 article.geogebraweb div {
     margin: 0;
 }
Index: loncom/homework/inputtags.pm
diff -u loncom/homework/inputtags.pm:1.357 loncom/homework/inputtags.pm:1.358
--- loncom/homework/inputtags.pm:1.357	Thu Mar 30 23:25:47 2023
+++ loncom/homework/inputtags.pm	Sun Apr  2 03:16:28 2023
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # input  definitons
 #
-# $Id: inputtags.pm,v 1.357 2023/03/30 23:25:47 raeburn Exp $
+# $Id: inputtags.pm,v 1.358 2023/04/02 03:16:28 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1787,12 +1787,17 @@
 	}
 	if ( $showbutton ) {
 	    if ($target ne 'tex') {
-		$button = 
+                if ($env{'form.disable_submit'}) {
+                    $button = '<input type="submit" name="submit_'.$id.'" id="submit_'.$id.'" class="LC_hwk_submit" value="'.&mt('Submit Answer').'" disabled="disabled" /> '.
+                               '<div id="msg_submit_'.$id.'" style="display:none"></div>';
+                } else {
+		    $button =
             '<input onmouseup="javascript:setSubmittedPart(\''.$id.'\');this.form.action+=\'#'.&escape($id).'\';"
                     type="submit" name="submit_'.$id.'" id="submit_'.$id.'" class="LC_hwk_submit"
                     value="'.&mt('Submit Answer').'" /> '.
                     '<div id="msg_submit_'.$id.'" style="display:none">'.
                     &mt('Processing your submission ...').'</div>';
+                }
 	    }
 	}
 
Index: loncom/homework/structuretags.pm
diff -u loncom/homework/structuretags.pm:1.573 loncom/homework/structuretags.pm:1.574
--- loncom/homework/structuretags.pm:1.573	Fri Feb  3 16:52:57 2023
+++ loncom/homework/structuretags.pm	Sun Apr  2 03:16:28 2023
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA 
 # definition of tags that give a structure to a document
 #
-# $Id: structuretags.pm,v 1.573 2023/02/03 16:52:57 raeburn Exp $
+# $Id: structuretags.pm,v 1.574 2023/04/02 03:16:28 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -625,7 +625,9 @@
         my ($symb,$courseid,$udom,$uname)=&Apache::lonnet::whichuser();
         my ($path,$multiresp) = 
             &Apache::loncommon::get_turnedin_filepath($symb,$uname,$udom);
-        if (($is_task) || ($needs_upload)) {
+        if ($env{'request.user_in_effect'}) {
+            $form_tag_start .= ' onsubmit="preventDefault();"';
+        } elsif (($is_task) || ($needs_upload)) {
             $form_tag_start .= ' onsubmit="return file_submission_check(this,'."'$path','$multiresp'".');"';
         }
 	$form_tag_start.='>'."\n";
@@ -1845,6 +1847,11 @@
         }
     }
 
+    if (($target eq 'web') && ($env{'request.user_in_effect'})) {
+        &Apache::lonxml::get_all_text("/problem",$parser,$style);
+        return $result;
+    }
+
     if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' ||
         $target eq 'tex') {
 
@@ -2129,6 +2136,12 @@
 	    }
 	}
 	$result =~ s/INSERTTEXFRONTMATTERHERE/$frontmatter/;
+    } elsif ($target eq 'web') {
+        if ($env{'request.user_in_effect'}) {
+            &reset_problem_globals('problem');
+            $result .= &Apache::lonhtmlcommon::set_compute_end_time();
+            return $result;
+        }
     }
 
     my $status=$Apache::inputtags::status['-1'];
Index: loncom/homework/lonhomework.pm
diff -u loncom/homework/lonhomework.pm:1.374 loncom/homework/lonhomework.pm:1.375
--- loncom/homework/lonhomework.pm:1.374	Thu Sep 20 14:16:51 2018
+++ loncom/homework/lonhomework.pm	Sun Apr  2 03:16:28 2023
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Homework handler
 #
-# $Id: lonhomework.pm,v 1.374 2018/09/20 14:16:51 raeburn Exp $
+# $Id: lonhomework.pm,v 1.375 2023/04/02 03:16:28 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -50,6 +50,8 @@
 use Apache::functionplotresponse();
 use Apache::drawimage();
 use Apache::loncapamath();
+use Apache::loncourseuser();
+use Apache::grades();
 use Apache::Constants qw(:common);
 use Apache::loncommon();
 use Apache::lonparmset();
@@ -795,6 +797,9 @@
 	$env{'request.course.sec'} !~ /^\s*$/) {
 	$viewgrades = &Apache::lonnet::allowed('vgr',$env{'request.course.id'}.
                                                '/'.$env{'request.course.sec'});
+        if ($viewgrades) {
+            $Apache::lonhomework::viewgradessec = $env{'request.course.sec'};
+        }
     }
     $Apache::lonhomework::viewgrades = $viewgrades;
 
@@ -812,6 +817,9 @@
 	$modifygrades = 
 	    &Apache::lonnet::allowed('mgr',$env{'request.course.id'}.
 				     '/'.$env{'request.course.sec'});
+        if ($modifygrades) {
+            $Apache::lonhomework::modifygradessec = $env{'request.course.sec'};
+        }
     }
     $Apache::lonhomework::modifygrades = $modifygrades;
 
@@ -830,7 +838,9 @@
 sub unset_permissions {
     undef($Apache::lonhomework::queuegrade);
     undef($Apache::lonhomework::modifygrades);
+    undef($Apache::lonhomework::modifygradessec);
     undef($Apache::lonhomework::viewgrades);
+    undef($Apache::lonhomework::viewgradessec);
     undef($Apache::lonhomework::browse);
 }
 
@@ -1278,7 +1288,7 @@
 #    Render the page in whatever target desired.
 #
 sub renderpage {
-    my ($request,$file,$targets,$return_string,$donebuttonmsg) = @_;
+    my ($request,$file,$targets,$return_string,$donebuttonmsg,$viewasuser,$symb) = @_;
 
     my @targets = @{$targets || [&get_target()]};
     &Apache::lonhomework::showhashsubset(\%env,'form.');
@@ -1319,6 +1329,10 @@
 	if ($target eq 'answer') { &showhash(%Apache::lonhomework::history); }
 	if ($target eq 'web') {&Apache::lonhomework::showhashsubset(\%env,'^form');}
 
+        if (($target eq 'web') && ($viewasuser ne '') && ($symb ne '')) {
+            $env{'request.user_in_effect'} = $viewasuser;
+        }
+
 	&Apache::lonxml::debug("Should be parsing now");
 	$result .= &Apache::lonxml::xmlparse($request, $target, $problem,
 					     &setup_vars($target),%mystyle);
@@ -1344,15 +1358,23 @@
 	    #}
 #	    $request->print($result);
 	    $overall_result.=$result;
+            if (($target eq 'web') && ($viewasuser ne '') && ($symb ne '')) {
+                my ($vuname,$vudom) = split(/:/,$viewasuser);
+                $overall_result .= &Apache::grades::view_as_user($symb,$vuname,$vudom).
+                                   '</body></html>';
+            }
 #	    $request->rflush();
 	}
+        if (($target eq 'web') && ($viewasuser ne '') && ($symb ne '')) {
+            undef($env{'request.user_in_effect'});
+        }
 	#$request->print(":Result ends");
 	#my $td=&tv_interval($t0);
     }
     if (!$return_string) {
 	&Apache::lonxml::add_messages(\$overall_result);
 	$request->print($overall_result);   
-	$request->rflush();   
+	$request->rflush();
     } else {
 	return $overall_result;
     }
@@ -1674,16 +1696,40 @@
     } else {
         # Set the event timer to zero if the "done button" was clicked.  The button is
         # part of the doneButton form created in lonmenu.pm
-        my ($donebuttonresult,$donemsg);
+        my ($donebuttonresult,$donemsg,$viewasuser);
         if ($symb && $env{'form.LC_interval_done'} eq 'true') {  
             ($donebuttonresult,$donemsg) = &zero_timer($symb);
             undef($env{'form.LC_interval_done'});
             undef($env{'form.LC_interval_done_proctorpass'});
         }
+        if (($env{'form.LC_viewas'} ne '') && $symb && $env{'request.course.id'} &&
+            ($Apache::lonhomework::viewgrades || $Apache::lonhomework::modifygrades)) {
+            if ($env{'form.LC_viewas'} =~ /^($match_username):($match_domain)$/) {
+                my ($possuname,$possudom) = ($1,$2);
+                my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                my ($canview,$posssec);
+                if ($env{'request.course.sec'} ne '') {
+                    if ($Apache::lonhomework::modifygradessec eq $env{'request.course.sec'}) {
+                        $canview = 'section';
+                        $posssec = $env{'request.course.sec'};
+                    } elsif ($Apache::lonhomework::viewgradessec eq $env{'request.course.sec'}) {
+                        $canview = 'section';
+                        $posssec = $env{'request.course.sec'};
+                    }
+                }
+                my $crstype = &Apache::loncommon::course_type();
+                if (&Apache::loncourseuser::is_course_user($possudom,$possuname,$cdom,$cnum,
+                                                           $canview,$crstype,$posssec)) {
+                    $viewasuser = $possuname.':'.$possudom;
+                }
+            }
+            undef($env{'form.LC_viewas'});
+        }
 	# just render the page normally outside of construction space
 	&Apache::lonxml::debug("not construct");
         undef(@Apache::lonhomework::ltipassback);
-	&renderpage($request,$file,undef,undef,$donemsg);
+	&renderpage($request,$file,undef,undef,$donemsg,$viewasuser,$symb);
         if (@Apache::lonhomework::ltipassback) {
             unless ($registered_cleanup) {
                 my $handlers = $request->get_handlers('PerlCleanupHandler');
Index: loncom/homework/grades.pm
diff -u loncom/homework/grades.pm:1.792 loncom/homework/grades.pm:1.793
--- loncom/homework/grades.pm:1.792	Sun Feb 12 21:01:30 2023
+++ loncom/homework/grades.pm	Sun Apr  2 03:16:28 2023
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Grading handler
 #
-# $Id: grades.pm,v 1.792 2023/02/12 21:01:30 raeburn Exp $
+# $Id: grades.pm,v 1.793 2023/04/02 03:16:28 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -2558,182 +2558,12 @@
     #             (3) All transactions (by date)
     #             (4) The whole record (with detailed information for all transactions)
 
-    my ($string,$timestamp,$lastgradetime,$lastsubmittime) =
-        &get_last_submission(\%record,$is_tool);
-
-    my $lastsubonly;
-
-    if ($timestamp eq '') {
-        $lastsubonly.='<div class="LC_grade_submissions_body">'.$string->[0].'</div>'; 
-    } elsif ($is_tool) {
-        $lastsubonly =
-            '<div class="LC_grade_submissions_body">'
-           .'<b>'.&mt('Date Grade Passed Back:').'</b> '.$timestamp."</div>\n";
-    } else {
-        my ($shownsubmdate,$showngradedate);
-        if ($lastsubmittime && $lastgradetime) {
-            $shownsubmdate = &Apache::lonlocal::locallocaltime($lastsubmittime);
-            if ($lastgradetime > $lastsubmittime) {
-                 $showngradedate = &Apache::lonlocal::locallocaltime($lastgradetime);
-             }
-        } else {
-            $shownsubmdate = $timestamp;
-        }
-        $lastsubonly =
-            '<div class="LC_grade_submissions_body">'
-           .'<b>'.&mt('Date Submitted:').'</b> '.$shownsubmdate."\n";
-        if ($showngradedate) {
-            $lastsubonly .= '<br /><b>'.&mt('Date Graded:').'</b> '.$showngradedate."\n";
-        }
-
-	my %seenparts;
-	my @part_response_id = &flatten_responseType($responseType);
-	foreach my $part (@part_response_id) {
-	    my ($partid,$respid) = @{ $part };
-	    my $display_part=&get_display_part($partid,$symb);
-	    if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
-		if (exists($seenparts{$partid})) { next; }
-		$seenparts{$partid}=1;
-                $request->print(
-                    '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
-                    ' <b>'.&mt('Collaborative submission by: [_1]',
-                               '<a href="javascript:viewSubmitter(\''.
-                               $env{"form.$uname:$udom:$partid:submitted_by"}.
-                               '\');" target="_self">'.
-                               $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
-                    '<br />');
-		next;
-	    }
-	    my $responsetype = $responseType->{$partid}->{$respid};
-	    if (!exists($record{"resource.$partid.$respid.submission"})) {
-                $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
-                    '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
-                    ' <span class="LC_internal_info">'.
-                    '('.&mt('Response ID: [_1]',$respid).')'.
-                    '</span>   '.
-	       	    '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
-		next;
-	    }
-	    foreach my $submission (@$string) {
-		my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
-		if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
-		my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
-		# Similarity check
-                my $similar='';
-                my ($type,$trial,$rndseed);
-                if ($hide eq 'rand') {
-                    $type = 'randomizetry';
-                    $trial = $record{"resource.$partid.tries"};
-                    $rndseed = $record{"resource.$partid.rndseed"};
-                }
-	        if ($env{'form.checkPlag'}) {
-		    my ($oname,$odom,$ocrsid,$oessay,$osim)=
-		    &most_similar($uname,$udom,$symb,$subval);
-		    if ($osim) {
-			$osim=int($osim*100.0);
-                        if ($hide eq 'anon') {
-                            $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.
-                                     &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';
-                        } else {
-			    $similar='<hr />';
-                            if ($essayurl eq 'lib/templates/simpleproblem.problem') {
-                                $similar .= '<h3><span class="LC_warning">'.
-                                            &mt('Essay is [_1]% similar to an essay by [_2]',
-                                                $osim,
-                                                &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
-                                            '</span></h3>';
-                            } else {
-                                my %old_course_desc;
-                                if ($ocrsid ne '') {
-                                    if (ref($coursedesc_by_cid{$ocrsid}) eq 'HASH') {
-                                        %old_course_desc = %{$coursedesc_by_cid{$ocrsid}};
-                                    } else {
-                                        my $args;
-                                        if ($ocrsid ne $env{'request.course.id'}) {
-                                            $args = {'one_time' => 1};
-                                        }
-                                        %old_course_desc =
-                                            &Apache::lonnet::coursedescription($ocrsid,$args);
-                                        $coursedesc_by_cid{$ocrsid} = \%old_course_desc;
-                                    }
-                                    $similar .=
-                                        '<h3><span class="LC_warning">'.
-                                        &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
-                                            $osim,
-                                            &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
-                                            $old_course_desc{'description'},
-                                            $old_course_desc{'num'},
-                                            $old_course_desc{'domain'}).
-                                        '</span></h3>';
-                                } else {
-                                    $similar .=
-                                        '<h3><span class="LC_warning">'.
-                                        &mt('Essay is [_1]% similar to an essay by [_2] in an unknown course',
-                                            $osim,
-                                            &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
-                                        '</span></h3>';
-                                }
-                            }
-                            $similar .= '<blockquote><i>'.
-                                        &keywords_highlight($oessay).
-                                        '</i></blockquote><hr />';
-                        }
-	            }
-		}
-		my $order=&get_order($partid,$respid,$symb,$uname,$udom,
-                                     undef,$type,$trial,$rndseed);
-                if (($env{'form.lastSub'} eq 'lastonly') ||
-                    ($env{'form.lastSub'} eq 'datesub')  ||
-                    ($env{'form.lastSub'} =~ /^(last|all)$/)) {
-		    my $display_part=&get_display_part($partid,$symb);
-                    $lastsubonly.='<div class="LC_grade_submission_part">'.
-                        '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
-                        ' <span class="LC_internal_info">'.
-                        '('.&mt('Response ID: [_1]',$respid).')'.
-                        '</span>   ';
-		    my $files=&get_submitted_files($udom,$uname,$partid,$respid,\%record);
-		    if (@$files) {
-                        if ($hide eq 'anon') {
-                            $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
-                        } else {
-                            $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'
-                                        .'<br /><span class="LC_warning">';
-                            if(@$files == 1) {
-                                $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
-                            } else {
-                                $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
-                            }
-                            $lastsubonly .= '</span>';
-                            foreach my $file (@$files) {
-                                &Apache::lonnet::allowuploaded('/adm/grades',$file);
-                                $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
-                            }
-                        }
-			$lastsubonly.='<br />';
-                    }
-                    if ($hide eq 'anon') {
-                        $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>'; 
-                    } else {
-                        $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
-                        if ($draft) {
-                            $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
-                        }
-                        $subval =
-			    &cleanRecord($subval,$responsetype,$symb,$partid,
-					 $respid,\%record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
-                        if ($responsetype eq 'essay') {
-                            $subval =~ s{\n}{<br />}g;
-                        }
-                        $lastsubonly.=$subval."\n";
-                    }
-                    if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
-		    $lastsubonly.='</div>';
-		}
-            }
-	}
-	$lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
-    }
+    my ($lastsubonly,$partinfo) =
+        &show_last_submission($uname,$udom,$symb,$essayurl,$responseType,$env{'form.lastSub'},
+                              $is_tool,$fullname,\%record,\%coursedesc_by_cid);
+    $request->print($partinfo);
     $request->print($lastsubonly);
+
     if ($env{'form.lastSub'} eq 'datesub') {
         my ($parts,$handgrade,$responseType) = &response_type($symb,\$res_error);
 	$request->print(&displaySubByDates($symb,\%record,$parts,$responseType,$checkIcon,$uname,$udom));
@@ -2857,6 +2687,186 @@
     return '';
 }
 
+sub show_last_submission {
+    my ($uname,$udom,$symb,$essayurl,$responseType,$viewtype,$is_tool,$fullname,
+        $record,$coursedesc_by_cid) = @_;
+    my ($string,$timestamp,$lastgradetime,$lastsubmittime) =
+        &get_last_submission($record,$is_tool);
+
+    my ($lastsubonly,$partinfo);
+    if ($timestamp eq '') {
+        $lastsubonly.='<div class="LC_grade_submissions_body">'.$string->[0].'</div>';
+    } elsif ($is_tool) {
+        $lastsubonly =
+            '<div class="LC_grade_submissions_body">'
+           .'<b>'.&mt('Date Grade Passed Back:').'</b> '.$timestamp."</div>\n";
+    } else {
+        my ($shownsubmdate,$showngradedate);
+        if ($lastsubmittime && $lastgradetime) {
+            $shownsubmdate = &Apache::lonlocal::locallocaltime($lastsubmittime);
+            if ($lastgradetime > $lastsubmittime) {
+                 $showngradedate = &Apache::lonlocal::locallocaltime($lastgradetime);
+             }
+        } else {
+            $shownsubmdate = $timestamp;
+        }
+        $lastsubonly =
+            '<div class="LC_grade_submissions_body">'
+           .'<b>'.&mt('Date Submitted:').'</b> '.$shownsubmdate."\n";
+        if ($showngradedate) {
+            $lastsubonly .= '<br /><b>'.&mt('Date Graded:').'</b> '.$showngradedate."\n";
+        }
+
+        my %seenparts;
+        my @part_response_id = &flatten_responseType($responseType);
+        foreach my $part (@part_response_id) {
+            my ($partid,$respid) = @{ $part };
+            my $display_part=&get_display_part($partid,$symb);
+            if ($env{"form.$uname:$udom:$partid:submitted_by"}) {
+                if (exists($seenparts{$partid})) { next; }
+                $seenparts{$partid}=1;
+                $partinfo .=
+                    '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
+                    ' <b>'.&mt('Collaborative submission by: [_1]',
+                               '<a href="javascript:viewSubmitter(\''.
+                               $env{"form.$uname:$udom:$partid:submitted_by"}.
+                               '\');" target="_self">'.
+                               $$fullname{$env{"form.$uname:$udom:$partid:submitted_by"}}.'</a>').
+                    '<br />';
+                next;
+            }
+            my $responsetype = $responseType->{$partid}->{$respid};
+            if (!exists($record->{"resource.$partid.$respid.submission"})) {
+                $lastsubonly.="\n".'<div class="LC_grade_submission_part">'.
+                    '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
+                    ' <span class="LC_internal_info">'.
+                    '('.&mt('Response ID: [_1]',$respid).')'.
+                    '</span>   '.
+                    '<span class="LC_warning">'.&mt('Nothing submitted - no attempts.').'</span><br /><br /></div>';
+                next;
+            }
+            foreach my $submission (@$string) {
+                my ($partid,$respid) = ($submission =~ /^resource\.([^\.]*)\.([^\.]*)\.submission/);
+                if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; }
+                my ($ressub,$hide,$draft,$subval) = split(/:/,$submission,4);
+                # Similarity check
+                my $similar='';
+                my ($type,$trial,$rndseed);
+                if ($hide eq 'rand') {
+                    $type = 'randomizetry';
+                    $trial = $record->{"resource.$partid.tries"};
+                    $rndseed = $record->{"resource.$partid.rndseed"};
+                }
+                if ($env{'form.checkPlag'}) {
+                    my ($oname,$odom,$ocrsid,$oessay,$osim)=
+                    &most_similar($uname,$udom,$symb,$subval);
+                    if ($osim) {
+                        $osim=int($osim*100.0);
+                        if ($hide eq 'anon') {
+                            $similar='<hr /><span class="LC_warning">'.&mt("Essay was found to be similar to another essay submitted for this assignment.").'<br />'.
+                                     &mt('As the current submission is for an anonymous survey, no other details are available.').'</span><hr />';
+                        } else {
+                            $similar='<hr />';
+                            if ($essayurl eq 'lib/templates/simpleproblem.problem') {
+                                $similar .= '<h3><span class="LC_warning">'.
+                                            &mt('Essay is [_1]% similar to an essay by [_2]',
+                                                $osim,
+                                                &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
+                                            '</span></h3>';
+                            } else {
+                                my %old_course_desc;
+                                if ($ocrsid ne '') {
+                                    if (ref($coursedesc_by_cid->{$ocrsid}) eq 'HASH') {
+                                        %old_course_desc = %{$coursedesc_by_cid->{$ocrsid}};
+                                    } else {
+                                        my $args;
+                                        if ($ocrsid ne $env{'request.course.id'}) {
+                                            $args = {'one_time' => 1};
+                                        }
+                                        %old_course_desc =
+                                            &Apache::lonnet::coursedescription($ocrsid,$args);
+                                        $coursedesc_by_cid->{$ocrsid} = \%old_course_desc;
+                                    }
+                                    $similar .=
+                                        '<h3><span class="LC_warning">'.
+                                        &mt('Essay is [_1]% similar to an essay by [_2] in course [_3] (course id [_4]:[_5])',
+                                            $osim,
+                                            &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')',
+                                            $old_course_desc{'description'},
+                                            $old_course_desc{'num'},
+                                            $old_course_desc{'domain'}).
+                                        '</span></h3>';
+                                } else {
+                                    $similar .=
+                                        '<h3><span class="LC_warning">'.
+                                        &mt('Essay is [_1]% similar to an essay by [_2] in an unknown course',
+                                            $osim,
+                                            &Apache::loncommon::plainname($oname,$odom).' ('.$oname.':'.$odom.')').
+                                        '</span></h3>';
+                                }
+                            }
+                            $similar .= '<blockquote><i>'.
+                                        &keywords_highlight($oessay).
+                                        '</i></blockquote><hr />';
+                        }
+                    }
+                }
+                my $order=&get_order($partid,$respid,$symb,$uname,$udom,
+                                     undef,$type,$trial,$rndseed);
+                if (($viewtype eq 'lastonly') ||
+                    ($viewtype eq 'datesub')  ||
+                    ($viewtype =~ /^(last|all)$/)) {
+                    my $display_part=&get_display_part($partid,$symb);
+                    $lastsubonly.='<div class="LC_grade_submission_part">'.
+                        '<b>'.&mt('Part: [_1]',$display_part).'</b>'.
+                        ' <span class="LC_internal_info">'.
+                        '('.&mt('Response ID: [_1]',$respid).')'.
+                        '</span>   ';
+                    my $files=&get_submitted_files($udom,$uname,$partid,$respid,$record);
+                    if (@$files) {
+                        if ($hide eq 'anon') {
+                            $lastsubonly.='<br />'.&mt('[quant,_1,file] uploaded to this anonymous survey',scalar(@{$files}));
+                        } else {
+                            $lastsubonly.='<br /><br />'.'<b>'.&mt('Submitted Files:').'</b>'
+                                        .'<br /><span class="LC_warning">';
+                            if(@$files == 1) {
+                                $lastsubonly .= &mt('Like all files provided by users, this file may contain viruses!');
+                            } else {
+                                $lastsubonly .= &mt('Like all files provided by users, these files may contain viruses!');
+                            }
+                            $lastsubonly .= '</span>';
+                            foreach my $file (@$files) {
+                                &Apache::lonnet::allowuploaded('/adm/grades',$file);
+                                $lastsubonly.='<br /><a href="'.$file.'?rawmode=1" target="lonGRDs"><img src="'.&Apache::loncommon::icon($file).'" border="0" alt="" /> '.$file.'</a>';
+                            }
+                        }
+                        $lastsubonly.='<br />';
+                    }
+                    if ($hide eq 'anon') {
+                        $lastsubonly.='<br /><b>'.&mt('Anonymous Survey').'</b>';
+                    } else {
+                        $lastsubonly.='<br /><b>'.&mt('Submitted Answer:').' </b>';
+                        if ($draft) {
+                            $lastsubonly.= ' <span class="LC_warning">'.&mt('Draft Copy').'</span>';
+                        }
+                        $subval =
+                            &cleanRecord($subval,$responsetype,$symb,$partid,
+                                         $respid,$record,$order,undef,$uname,$udom,$type,$trial,$rndseed);
+                        if ($responsetype eq 'essay') {
+                            $subval =~ s{\n}{<br />}g;
+                        }
+                        $lastsubonly.=$subval."\n";
+                    }
+                    if ($similar) {$lastsubonly.="<br /><br />$similar\n";}
+                    $lastsubonly.='</div>';
+                }
+            }
+        }
+        $lastsubonly.='</div>'."\n"; # End: LC_grade_submissions_body
+    }
+    return ($lastsubonly,$partinfo);
+}
+
 sub check_collaborators {
     my ($symb,$uname,$udom,$record,$handgrade,$counter) = @_;
     my ($result, at col_fullnames);
@@ -11555,6 +11565,77 @@
     $r->print('<input type="submit" value="'.&mt('Next').' →" /></form>');
 }
 
+#----- display problem, answer, and submissions for a single student (no grading)
+
+sub view_as_user {
+    my ($symb,$vuname,$vudom,$hasperm) = @_;
+    my $plainname = &Apache::loncommon::plainname($vuname,$vudom,'lastname');
+    my $displayname = &nameUserString('',$plainname,$vuname,$vudom);
+    my $output = &Apache::loncommon::get_student_view($symb,$vuname,$vudom,
+                                                      $env{'request.course.id'},
+                                                      undef,{'disable_submit' => 1}).
+                 "\n\n".
+                 '<div class="LC_grade_show_user">'.
+                 '<h2>'.$displayname.'</h2>'.
+                 "\n".
+                 &Apache::loncommon::track_student_link('View recent activity',
+                                                        $vuname,$vudom,'check').' '.
+                 "\n";
+    if (&Apache::lonnet::allowed('opa',$env{'request.course.id'}) ||
+        (($env{'request.course.sec'} ne '') &&
+         &Apache::lonnet::allowed('opa',$env{'request.course.id'}.'/'.$env{'request.course.sec'}))) {
+        $output .= &Apache::loncommon::pprmlink(&mt('Set/Change parameters'),
+                                               $vuname,$vudom,$symb,'check');
+    }
+    $output .= "\n";
+    my $companswer = &Apache::loncommon::get_student_answers($symb,$vuname,$vudom,
+                                                             $env{'request.course.id'});
+    $companswer=~s|<form(.*?)>||g;
+    $companswer=~s|</form>||g;
+    $companswer=~s|name="submit"|name="would_have_been_submit"|g;
+    $output .= '<div class="LC_Box">'.
+               '<h3 class="LC_hcell">'.&mt('Correct answer for[_1]',$displayname).'</h3>'.
+               $companswer.
+               '</div>'."\n";
+    my $is_tool = ($symb =~ /ext\.tool$/);
+    my ($essayurl,%coursedesc_by_cid);
+    (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb);
+    my %record = &Apache::lonnet::restore($symb,$env{'request.course.id'},$vudom,$vuname);
+    my $res_error;
+    my ($partlist,$handgrade,$responseType,$numresp,$numessay) =
+        &response_type($symb,\$res_error);
+    my $fullname;
+    my $collabinfo;
+    if ($numessay) {
+        unless ($hasperm) {
+            &init_perm();
+        }
+        ($collabinfo,$fullname)=
+            &check_collaborators($symb,$vuname,$vudom,\%record,$handgrade,0);
+        unless ($hasperm) {
+            &reset_perm();
+        }
+    }
+    my $checkIcon = '<img alt="'.&mt('Check Mark').
+                    '" src="'.$Apache::lonnet::perlvar{'lonIconsURL'}.
+                    '/check.gif" height="16" border="0" />';
+    my ($lastsubonly,$partinfo) =
+        &show_last_submission($vuname,$vudom,$symb,$essayurl,$responseType,'datesub',
+                              '',$fullname,\%record,\%coursedesc_by_cid);
+    $output .= '<div class="LC_Box">'.
+               '<h3 class="LC_hcell">'.&mt('Submissions').'</h3>'."\n".$collabinfo."\n";
+    if (($numresp > $numessay) & !$is_tool) {
+        $output .='<p class="LC_info">'.
+                  &mt('Part(s) graded correct by the computer is marked with a [_1] symbol.',$checkIcon).
+                  "</p>\n";
+    }
+    $output .= $partinfo;
+    $output .= $lastsubonly;
+    $output .= &displaySubByDates($symb,\%record,$partlist,$responseType,$checkIcon,$vuname,$vudom);
+    $output .= '</div></div>'."\n";
+    return $output;
+}
+
 sub handler {
     my $request=$_[0];
     &reset_caches();
Index: doc/loncapafiles/loncapafiles.lpml
diff -u doc/loncapafiles/loncapafiles.lpml:1.1035 doc/loncapafiles/loncapafiles.lpml:1.1036
--- doc/loncapafiles/loncapafiles.lpml:1.1035	Sat Dec 31 14:09:02 2022
+++ doc/loncapafiles/loncapafiles.lpml	Sun Apr  2 03:16:31 2023
@@ -2,7 +2,7 @@
  "http://lpml.sourceforge.net/DTD/lpml.dtd">
 <!-- loncapafiles.lpml -->
 
-<!-- $Id: loncapafiles.lpml,v 1.1035 2022/12/31 14:09:02 raeburn Exp $ -->
+<!-- $Id: loncapafiles.lpml,v 1.1036 2023/04/02 03:16:31 raeburn Exp $ -->
 
 <!--
 
@@ -2744,6 +2744,15 @@
 <status>works/unverified</status>
 </file>
 <file>
+<source>loncom/interface/loncourseuser.pm</source>
+<target dist='default'>home/httpd/lib/perl/Apache/loncourseuser.pm</target>
+<categoryname>handler</categoryname>
+<description>
+Handler to check if a user has a role (active, future or previous) in a course.
+</description>
+<status>works/unverified</status>
+</file>
+<file>
 <source>loncom/interface/loncourserespicker.pm</source>
 <target dist='default'>home/httpd/lib/perl/Apache/loncourserespicker.pm</target>
 <categoryname>handler</categoryname>
@@ -8579,6 +8588,7 @@
 uploadscores.png;
 verify.png;
 view-mode-22x22.png;
+viewuser.png;
 wishlist.png;
 wishlist-link.png;
 wishlist-link-lighter.png;

Index: loncom/interface/loncourseuser.pm
+++ loncom/interface/loncourseuser.pm
# The LearningOnline Network
# Verify user is in course
#
# $Id: loncourseuser.pm,v 1.1 2023/04/02 03:16:27 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package Apache::loncourseuser;

use strict;
use Apache::Constants qw(:common :http);
use Apache::lonnet;
use Apache::loncommon;
use Apache::lonuserutils;
use JSON::DWIW;
use LONCAPA qw(:DEFAULT :match);

sub handler {
    my $r = shift;
    &Apache::loncommon::content_type($r,'application/json');
    $r->send_http_header;
    my @userinfo;
    if ($env{'request.course.id'}) {
        my $canview;
        foreach my $priv ('mgr','vgr') {
            $canview = &Apache::lonnet::allowed($priv,$env{'request.course.id'});
            if (!$canview && $env{'request.course.sec'} ne '') {
                if (&Apache::lonnet::allowed($priv,"$env{'request.course.id'}/$env{'request.course.sec'}")) {
                    $canview = 'section';
                }
            }
            last if ($canview);
        }
        if ($canview) {
            my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
            my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
            my $crstype = &Apache::loncommon::course_type();
            if (($cdom) && ($cnum)) {
                my $possudom = $env{'form.udom'};
                if (&Apache::lonnet::domain($possudom) ne '') { 
                    my $possuname = $env{'form.uname'};
                    my $possuid = $env{'form.uid'};
                    if (($possuname eq '') && ($possuid ne '')) {
                        $possuname=(&Apache::lonnet::idget($possudom,[$possuid],'ids'))[1];
                    }
                    if ($possuname ne '') { 
                        if (&is_course_user($possudom,$possuname,$cdom,$cnum,$canview,
                                            $crstype,$env{'request.course.sec'})) {
                            @userinfo = ($possuname,$possudom);
                        }
                    }
                }
            }
        }
    }
    $r->print(JSON::DWIW->to_json({match => \@userinfo}));
    return OK;
}

sub is_course_user {
    my ($possudom,$possuname,$cdom,$cnum,$canview,$crstype,$section) = @_;
    my $found;
    my @types = ('active','future','previous');
    my @roles = &Apache::lonuserutils::roles_by_context('course',1,$crstype);
    my %userhash = &Apache::lonnet::get_my_roles($possuname,$possudom,'userroles',\@types,\@roles,[$cdom],1);
    if (keys(%userhash)) {
        foreach my $key (keys(%userhash)) {
            next unless ($key =~ /^\Q$cnum:$cdom:\E/);
            if ($canview eq 'section') {
                my $usec = (split(/:/,$key))[-1];
                if ($usec eq $section) {
                   $found = 1;
                }
            } else {
                $found = 1;
            }
            last if ($found);
        }
    }
    return $found;
}

1;



More information about the LON-CAPA-cvs mailing list