--- loncom/interface/lonmenu.pm	2022/06/11 04:30:48	1.523
+++ loncom/interface/lonmenu.pm	2023/07/14 00:54:13	1.534
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Routines to control the menu
 #
-# $Id: lonmenu.pm,v 1.523 2022/06/11 04:30:48 raeburn Exp $
+# $Id: lonmenu.pm,v 1.534 2023/07/14 00:54:13 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -210,7 +210,6 @@ use Apache::lonenc();
 use Apache::lonlocal;
 use Apache::lonmsg();
 use LONCAPA qw(:DEFAULT :match);
-use LONCAPA::ltiutils;
 use HTML::Entities();
 use Apache::lonwishlist();
 
@@ -846,7 +845,8 @@ sub build_submenu {
 }
 
 sub innerregister {
-    my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname,$ltiscope,$ltiuri) = @_;
+    my ($forcereg,$bread_crumbs,$group,$pagebuttonshide,$hostname,
+        $ltiscope,$ltiuri,$showncrumbsref) = @_;
     my $const_space = ($env{'request.state'} eq 'construct');
     my $is_const_dir = 0;
 
@@ -934,27 +934,39 @@ sub innerregister {
                 if ($env{'form.title'}) {
                     $title = $env{'form.title'};
                 }
-                my $trail;
+                my ($trail,$cnum,$cdom);
+                if ($env{'form.folderpath'}) {
+                    $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                    $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                    &Apache::loncommon::validate_folderpath(1,'',$cnum,$cdom);
+                }
                 if ($env{'form.folderpath'}) {
                     &prepare_functions($resurl,$forcereg,$group,undef,undef,1,$hostname);
+                    $title = &HTML::Entities::encode($title,'\'"<>&');
                     ($trail) =
-                        &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1);
+                        &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1);
                 } else {
                     &Apache::lonhtmlcommon::add_breadcrumb(
                     {text  => "Supplemental $crstype Content",
                      href  => "javascript:gopost('/adm/supplemental','')"});
-                    $title = &mt('View Resource');
+                    $title = &HTML::Entities::encode(&mt('View Resource'),'\'"<>&');
                     ($trail) = 
-                        &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1);
+                        &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1);
+                }
+                if (ref($showncrumbsref)) {
+                    $$showncrumbsref = 1;
                 }
                 return $trail;
             } elsif ($resurl =~ m{^\Q/uploaded$courseurl/portfolio/syllabus/}) {
                 &Apache::lonhtmlcommon::clear_breadcrumbs();
                 &prepare_functions('/public'.$courseurl."/syllabus",
                                    $forcereg,$group,undef,undef,1,$hostname);
-                $title = &mt('Syllabus File');
+                $title = &HTML::Entities::encode(&mt('Syllabus File'),'\'"<>&');
                 my ($trail) =
-                    &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,$hostname);
+                    &Apache::lonhtmlcommon::docs_breadcrumbs(undef,$crstype,undef,$title,1,1);
+                if (ref($showncrumbsref)) {
+                    $$showncrumbsref = 1;
+                }
                 return $trail;
             }
             unless ($env{'request.state'} eq 'construct') {
@@ -1049,6 +1061,71 @@ sub innerregister {
                     '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);
+                unless (&Apache::lonnet::is_advanced_user($vudom,$vuname)) {
+                    $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 = '&#10006;';
+            } 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">&#9658;&nbsp;</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>
+&nbsp;<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
 
@@ -1062,16 +1139,25 @@ sub innerregister {
             my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
 	    my ($udom,$uname,$thisdisfn) =
 		($env{'request.filename'}=~m{^\Q$londocroot/priv/\E([^/]+)/([^/]+)/(.*)$});
+            my $crsauthor;
+            if (($env{'request.course.id'}) &&
+                ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) &&
+                ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) {
+                $crsauthor = 1;
+            }
             my $currdir = '/priv/'.$udom.'/'.$uname.'/'.$thisdisfn;
             if ($currdir =~ m-/$-) {
                 $is_const_dir = 1;
-                if ($thisdisfn eq '') {
-                    unless (($env{'request.course.id'}) && 
-                            ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) &&
-                            ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) { 
-                        $is_const_dir = 2;
-                    }
+                if (($thisdisfn eq '') && ($crsauthor)) {
+                    $is_const_dir = 2;
                 }
+                my $esc_currdir = &Apache::loncommon::escape_single($currdir);
+                $menuitems=(<<ENDMENUITEMS);
+s&6&3&pub.png&Publish&dir[_2]&gocstr('/adm/publish','$esc_currdir')&Publish this Directory
+s&7&4&docs-22x22.png&Edit Metadata&defaults[_1]&gopost('${esc_currdir}default.meta','')&Edit metadata for this Directory
+s&7&2&prt.png&Print&printout[_1]&gocstr('/adm/printout','$esc_currdir')&Print contents of directory
+s&7&1&del.png&Delete&dir[_3]&gocstr('/adm/cfile?action=delete','$esc_currdir')&Delete this Directory
+ENDMENUITEMS
             } else {
                 $currdir =~ s|[^/]+$||;
 		my $cleandisfn = &Apache::loncommon::escape_single($thisdisfn);
@@ -1086,12 +1172,19 @@ sub innerregister {
 #
 # Probably should be in mydesk.tab
 #
-                $menuitems=(<<ENDMENUITEMS);
+                if (($crsauthor) && ($pubfile eq "/res/$udom/$uname/default.rights")) {
+                    $menuitems=(<<ENDMENUITEMS);
+s&6&1&list.png&Directory&dir[_1]&golist('$esc_currdir')&List current directory
+s&6&3&pub.png&Publish&resource[_3]&gocstr('/adm/publish','/priv/$udom/$uname/$cleandisfn')&Publish this resource
+ENDMENUITEMS
+                } else {
+                    $menuitems=(<<ENDMENUITEMS);
 s&6&1&list.png&Directory&dir[_1]&golist('$esc_currdir')&List current directory
 s&6&2&rtrv.png&Retrieve&version[_1]&gocstr('/adm/retrieve','/priv/$udom/$uname/$cleandisfn')&Retrieve old version
 s&6&3&pub.png&Publish&resource[_3]&gocstr('/adm/publish','/priv/$udom/$uname/$cleandisfn')&Publish this resource
 s&7&3&copy.png&Copy&resource[_4]&gocstr('/adm/cfile?action=copy','/priv/$udom/$uname/$cleandisfn')&Copy this resource
 ENDMENUITEMS
+                }
 #
 # Rename and Delete only available if obsolete or unpublished
 #
@@ -1261,10 +1354,13 @@ ENDMENUITEMS
                 }
             }
         }
-        my $showprogress;
+        my ($showprogress,$linkprotout);
         if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) {
             $showprogress = &placement_progress();
         }
+        if ($env{'request.deeplink.login'}) {
+            $linkprotout = &linkprot_exit();
+        }
 
 	my $addremote=0;
 	foreach (@inlineremote) { if ($_ ne '') { $addremote=1; last;} }
@@ -1290,6 +1386,9 @@ ENDMENUITEMS
             if ($countdown) {
                 &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$countdown);
             }
+            if ($linkprotout) {
+                &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout);
+            }
             if ($showprogress) {
                 &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$showprogress);
             }
@@ -1298,6 +1397,9 @@ ENDMENUITEMS
             if ($countdown) {
                 unshift(@tools,$countdown);
             }
+            if ($linkprotout) {
+                unshift(@tools,$linkprotout);
+            }
             &Apache::lonhtmlcommon::add_breadcrumb_tool(
                 'tools',@tools);
 
@@ -1315,6 +1417,9 @@ ENDMENUITEMS
         if ($showprogress) {
             &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$showprogress);
         }
+        if ($linkprotout) {
+            &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout);
+        }
     }
     my ($topic_help,$topic_help_text);
     if ($is_const_dir == 2) {
@@ -1326,6 +1431,9 @@ ENDMENUITEMS
             $topic_help_text = 'About WebDAV access';
         }
     }
+    if (ref($showncrumbsref)) {
+        $$showncrumbsref = 1;
+    }
     return   &Apache::lonhtmlcommon::scripttag('', 'start')
            . &Apache::lonhtmlcommon::breadcrumbs(undef,undef,0,'','','','',$topic_help,$topic_help_text)
            . &Apache::lonhtmlcommon::scripttag('', 'end');
@@ -1349,8 +1457,12 @@ sub get_editbutton {
         if ($env{'form.folderpath'}) {
             $suppanchor = $env{'form.anchor'};
         }
+        my $shownsymb;
+        if ($env{'request.symb'}) {
+            $shownsymb = &Apache::lonenc::check_encrypt($env{'request.symb'});
+        }
         $jscall = &Apache::lonhtmlcommon::jump_to_editres($cfile,$home,$switchserver,
-                                                $forceedit,$forcereg,$env{'request.symb'},
+                                                $forceedit,$forcereg,$env{'request.symb'},$shownsymb,
                                                 &escape($env{'form.folderpath'}),
                                                 &escape($env{'form.title'}),$hostname,
                                                 $env{'form.idx'},&escape($env{'form.suppurl'}),
@@ -1609,7 +1721,7 @@ sub advtools_crumbs {
                 '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(
@@ -1631,7 +1743,7 @@ sub clear {
 # 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);
@@ -1682,7 +1794,7 @@ sub switch {
         } else {
             $inlineremote[$idx] =
        '<a title="'.$desc.'" class="LC_menubuttons_link" href="javascript:'.$act.';">'.$pic.
-       '<span class="LC_menubuttons_inline_text">'.$top.'&nbsp;</span></a>';
+       '<span class="LC_menubuttons_inline_text">'.$top.'&nbsp;</span></a>'.$form;
         }
     }
     return '';
@@ -2223,6 +2335,79 @@ END
     }
 }
 
+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='&#9658;&nbsp;';
+        document.getElementById('uscollapse').innerHTML='';
+    } else {
+        seluserid.style.display = 'inline';
+        document.getElementById('usexpand').innerHTML='';
+        document.getElementById('uscollapse').innerHTML='&#9668;&nbsp;';
+    }
+    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]));
@@ -2264,6 +2449,24 @@ sub utilityfunctions {
 
     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'};
@@ -2480,6 +2683,8 @@ function open_aboutLC() {
 
 $countdown
 
+$viewuser
+
 ENDUTILITY
 }
 
@@ -3177,6 +3382,94 @@ sub placement_progress {
            &mt('Test is [_1]% complete',$complete).'</span>';
 }
 
+sub linkprot_exit {
+    if (($env{'request.course.id'}) && ($env{'request.deeplink.login'})) {
+        my ($deeplink_symb,$deeplink);
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        if (($cnum ne '') && ($cdom ne '')) {
+            $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom);
+            if ($deeplink_symb) {
+                if ($deeplink_symb =~ /\.(page|sequence)$/) {
+                    my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]);
+                    my $navmap = Apache::lonnavmaps::navmap->new();
+                    if (ref($navmap)) {
+                        $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink');
+                    }
+                } else {
+                    $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb);
+                }
+                if ($deeplink ne '') {
+                    my ($state,$others,$listed,$scope,$protect,$display,$target,$exit) = split(/,/,$deeplink);
+                    my %lt = &Apache::lonlocal::texthash(
+                        title    => 'Exit Tool',
+                        okdone   => 'Click "OK" to exit embedded tool',
+                        cancel   => 'Click "Cancel" to continue working.',
+                        ok       => 'OK',
+                        exit     => 'Cancel',
+                    );
+                    if ($exit) {
+                        my ($show,$text) = split(/:/,$exit);
+                        unless ($show eq 'no') { 
+                            my $height = 250;
+                            my $width = 300;
+                            my $exitbuttontext;
+                            if ($text eq '') { 
+                                $exitbuttontext = &mt('Exit Tool');
+                            } else {
+                                $exitbuttontext = $text;
+                            }
+                            return <<END;
+<form method="post" name="LCexitButton" action="/adm/linkexit">
+    <input type="hidden" name="LC_deeplink_exit" value="" />
+    <button id="LC_exit-confirm-opener" type="button">$exitbuttontext</button>
+</form>
+
+<div id="LC_exit-confirm" title="$lt{'title'}">
+    <p>$lt{'okdone'} $lt{'cancel'}</p>
+</div>
+
+<script type="text/javascript">
+// <![CDATA[
+\$( "#LC_exit-confirm" ).dialog({ autoOpen: false });
+\$( "#LC_exit-confirm-opener" ).click(function() {
+    \$( "#LC_exit-confirm" ).dialog( "open" );
+    \$( "#LC_exit-confirm" ).dialog({
+      resizable: false,
+      height: $height,
+      width: $width,
+      modal: true,
+      buttons: [
+                 {
+                    text: "$lt{'ok'}",
+                    click: function() {
+                        \$( this ).dialog( "close" );
+                        \$( '[name="LC_deeplink_exit"]' )[0].value = 'true';
+                        \$( '[name="LCexitButton"]' )[0].submit();
+                    },
+                 },
+                 {
+                     text: "$lt{'exit'}",
+                     click: function() {
+                         \$( this ).dialog( "close" );
+                     },
+                  },
+               ],
+       });
+});
+// ]]>
+</script>
+
+END
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
 # ================================================================ Main Program
 
 BEGIN {