--- loncom/interface/lonmenu.pm	2021/11/15 23:29:20	1.512
+++ loncom/interface/lonmenu.pm	2022/05/29 03:19:00	1.521
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Routines to control the menu
 #
-# $Id: lonmenu.pm,v 1.512 2021/11/15 23:29:20 raeburn Exp $
+# $Id: lonmenu.pm,v 1.521 2022/05/29 03:19:00 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -99,7 +99,7 @@ It gets filled in the BEGIN block of thi
 
 =over
 
-=item prep_menuitems(\@menuitem,$ltitarget)
+=item prep_menuitems(\@menuitem,$target,$listclass,$linkattr)
 
 This routine wraps a menuitem in proper HTML. It is used by primary_menu() and 
 secondary_menu().
@@ -220,9 +220,9 @@ use vars qw(@desklines %category_names %
 my @inlineremote;
 
 sub prep_menuitem {
-    my ($menuitem,$ltitarget,$listclass,$linkattr) = @_;
+    my ($menuitem,$target,$listclass,$linkattr) = @_;
     return '' unless(ref($menuitem) eq 'ARRAY');
-    my $link;
+    my ($link,$targetattr);
     if ($$menuitem[1]) { # graphical Link
         $link = "<img class=\"LC_noBorder\""
               . " src=\"" . &Apache::loncommon::lonhttpdurl($$menuitem[1]) . "\"" 
@@ -230,14 +230,13 @@ sub prep_menuitem {
     } else {             # textual Link
         $link = &mt($$menuitem[3]);
     }
-    my $target = ' target="_top"';
-    if ($ltitarget eq 'iframe') {
-        $target ='';
+    if ($target ne '') {
+        $targetattr = ' target="'.$target.'"';
     }
     return ($listclass?'<li class="'.$listclass.'">':'<li>').'<a'
            # highlighting for new messages
            . ( $$menuitem[4] eq 'newmsg' ? ' class="LC_new_message"' : '') 
-           . qq| href="$$menuitem[0]"$target $linkattr>$link</a></li>|;
+           . qq| href="$$menuitem[0]"$targetattr $linkattr>$link</a></li>|;
 }
 
 # primary_menu() evaluates @primary_menu and returns a two item array,
@@ -247,7 +246,7 @@ sub prep_menuitem {
 # @primary_menu is filled within the BEGIN block of this module with 
 # entries from mydesk.tab
 sub primary_menu {
-    my ($crstype,$ltimenu,$menucoll,$menuref,$links_disabled) = @_;
+    my ($crstype,$ltimenu,$menucoll,$menuref,$links_disabled,$links_target) = @_;
     my (%menu,%ltiexc,%menuopts);
     # each element of @primary contains following array:
     # (link url, icon path, alt text, link text, condition, position)
@@ -265,10 +264,9 @@ sub primary_menu {
         my %roles_in_env;
         $rolecount = &Apache::lonroles::roles_from_env(\%roles_in_env,$update);
     }
-    my ($lti,$ltitarget);
+    my $lti;
     if ($env{'request.lti.login'}) {
         $lti = 1;
-        $ltitarget = $env{'request.lti.target'};
         if (ref($ltimenu) eq 'HASH') {
             foreach my $item ('fullname','logout') {
                 unless ($ltimenu->{$item}) {
@@ -277,6 +275,27 @@ sub primary_menu {
             }
         }
     }
+    my ($listclass,$linkattr,$target);
+    if ($links_disabled) {
+        $listclass = 'LCisDisabled';
+        $linkattr = 'aria-disabled="true"';
+    }
+    if ($links_target ne '') {
+        $target = $links_target;
+    } else {
+        my ($ltitarget,$deeplinktarget);
+        if ($env{'request.lti.login'}) {
+             $ltitarget = $env{'request.lti.target'};
+        }
+        if ($env{'request.deeplink.login'}) {
+            $deeplinktarget = $env{'request.deeplink.target'};
+        }
+        if (($ltitarget eq 'iframe') || ($deeplinktarget eq '_self')) {
+            $target = '_self';
+        } else {
+            $target = '_top';
+        }
+    }
     if (($menucoll) && (ref($menuref) eq 'HASH')) {
         %menuopts = %{$menuref};
     }
@@ -332,18 +351,10 @@ sub primary_menu {
                 }
             }
         }
-        my ($listclass,$linkattr);
-        if ($links_disabled) {
-            $listclass = 'LCisDisabled';
-            $linkattr = 'aria-disabled="true"';
-        }
         if (defined($primary_submenu{$title})) {
-            my ($link,$target);
+            my $link;
             if ($menuitem->[0] ne '') {
                 $link = $menuitem->[0];
-                unless ($ltitarget eq 'iframe') {
-                    $target = '_top';
-                }
             } else {
                 $link = '#';
             }
@@ -391,7 +402,7 @@ sub primary_menu {
                                                                   'helpdeskmail',
                                                                   $defdom,$origmail);
                 if ($to ne '') {
-                    $menu{$position} .= &prep_menuitem($menuitem,$ltitarget,$listclass,$linkattr); 
+                    $menu{$position} .= &prep_menuitem($menuitem,$target,$listclass,$linkattr); 
                 }
             } else {
                 $menu{$position} .= ($listclass?'<li class="'.$listclass.'">':'<li>').
@@ -404,9 +415,9 @@ sub primary_menu {
                     $$menuitem[0] = '/adm/login';
                 }
             }
-            $menu{$position} .= prep_menuitem($menuitem,$ltitarget,$listclass,$linkattr);
+            $menu{$position} .= prep_menuitem($menuitem,$target,$listclass,$linkattr);
         } else {
-            $menu{$position} .= prep_menuitem($menuitem,$ltitarget,$listclass,$linkattr);
+            $menu{$position} .= prep_menuitem($menuitem,$target,$listclass,$linkattr);
         }
     }
     my @output = ('','');
@@ -445,7 +456,8 @@ sub getauthor{
 }
 
 sub secondary_menu {
-    my ($httphost,$ltiscope,$ltimenu,$noprimary,$menucoll,$menuref,$links_disabled) = @_;
+    my ($httphost,$ltiscope,$ltimenu,$noprimary,$menucoll,$menuref,
+        $links_disabled,$links_target) = @_;
     my $menu;
 
     my $crstype = &Apache::loncommon::course_type();
@@ -522,7 +534,7 @@ sub secondary_menu {
         %menuopts = %{$menuref};
     }
 
-    my ($listclass,$linkattr);
+    my ($listclass,$linkattr,$target);
     if ($links_disabled) {
         $listclass = 'LCisDisabled';
         $linkattr = 'aria-disabled="true"';
@@ -537,9 +549,22 @@ sub secondary_menu {
         }
     }
 
-    my ($roleswitcher_js,$roleswitcher_form,$ltitarget);
-    if ($env{'request.lti.login'}) {
-        $ltitarget = $env{'request.lti.target'};
+    my ($roleswitcher_js,$roleswitcher_form);
+    if ($links_target ne '') {
+        $target = $links_target;
+    } else {
+        my ($ltitarget,$deeplinktarget);
+        if ($env{'request.lti.login'}) {
+            $ltitarget = $env{'request.lti.target'};
+        }
+        if ($env{'request.deeplink.login'}) {
+            $deeplinktarget = $env{'request.deeplink.target'};
+        }
+        if (($ltitarget eq 'iframe') || ($deeplinktarget eq '_self')) {
+            $target = '_self';
+        } else {
+            $target = '_top';
+        }
     }
 
     foreach my $menuitem (@secondary_menu) {
@@ -595,12 +620,9 @@ sub secondary_menu {
             }
         }
         if (defined($secondary_submenu{$title})) {
-            my ($link,$target);
+            my $link;
             if ($menuitem->[0] ne '') {
                 $link = $menuitem->[0];
-                unless ($ltitarget eq 'iframe') {
-                    $target = '_top';
-                }
             } else {
                 $link = '#';
             }
@@ -649,7 +671,7 @@ sub secondary_menu {
                 &roles_selector(
                     $env{'course.' . $env{'request.course.id'} . '.domain'},
                     $env{'course.' . $env{'request.course.id'} . '.num'},
-                    $httphost,$ltitarget
+                    $httphost,$target,$menucoll,$menuref
                 );
             if (($$menuitem[5]) && (!$menuopts{$$menuitem[5]})) {
                 next unless ($has_opa_priv);
@@ -683,7 +705,7 @@ sub secondary_menu {
                 }
                 $$menuitem[0] = &HTML::Entities::encode($$menuitem[0],'&<>"');
             }
-            $menu .= &prep_menuitem(\@$menuitem,$ltitarget,$listclass,$linkattr);
+            $menu .= &prep_menuitem(\@$menuitem,$target,$listclass,$linkattr);
         }
     }
     if ($menu =~ /\[url\].*\[symb\]/) {
@@ -723,12 +745,12 @@ sub secondary_menu {
 sub create_submenu {
     my ($link,$target,$title,$submenu,$translate,$addclass,$listclass,$linkattr) = @_;
     return unless (ref($submenu) eq 'ARRAY');
-    my $disptarget;
-    if ($target ne '') {
-        $disptarget = ' target="'.$target.'"';
+    my $targetattr;
+    if (($target ne '') && ($link ne '#')) {
+        $targetattr = ' target="'.$target.'"';
     }
     my $menu = '<li class="LC_hoverable '.$addclass.'">'.
-               '<a href="'.$link.'"'.$disptarget.'>'.
+               '<a href="'.$link.'"'.$targetattr.'>'.
                '<span class="LC_nobreak">'.$title.
                '<span class="LC_fontsize_small" style="font-weight:normal;">'.
                ' &#9660;</span></span></a>'.
@@ -805,16 +827,17 @@ sub build_submenu {
                     }
                     $href =~ s/\[returnurl\]/$returnurl/;
                 }
+                my $targetattr;
                 unless (($href eq '') || ($href =~ /^\#/)) {
-                    if ($target eq '_top') {
-                        $target = ' target="_top"';
+                    if ($target ne '') {
+                        $targetattr = ' target="'.$target.'"';
                     }
                 }
 
-                $menu .= '<li '.
+                $menu .= '<li ';
                 $menu .= ($listclass?'class="'.$listclass.'" ':'');
                 $menu .= 'style="margin:0;padding:0;'. $bordertop . $borderbot .'">';
-                $menu .= '<a href="'.$href.'"'.$target.' '.$linkattr.'>' .  $title . '</a>';
+                $menu .= '<a href="'.$href.'"'.$targetattr.' '.$linkattr.'>' .  $title . '</a>';
                 $menu .= '</li>';
             }
         }
@@ -1056,6 +1079,13 @@ sub innerregister {
                 $currdir =~ s|[^/]+$||;
 		my $cleandisfn = &Apache::loncommon::escape_single($thisdisfn);
 		my $esc_currdir = &Apache::loncommon::escape_single($currdir);
+                my $pubfile = "/res/$udom/$uname/$thisdisfn";
+                my $candelete = 1;
+                if (-e $londocroot.$pubfile) {
+                    unless (&Apache::lonnet::metadata($pubfile,'obsolete')) {
+                        undef($candelete);
+                    }
+                }
 #
 # Probably should be in mydesk.tab
 #
@@ -1063,7 +1093,18 @@ sub innerregister {
 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
+#
+                if ($candelete) {
+                    $menuitems .= (<<ENDMENUITEMS);
+s&7&4&rename.png&Rename&resource[_5]&gocstr('/adm/cfile?action=rename','/priv/$udom/$uname/$cleandisfn')&Rename this resource
 s&7&1&del.png&Delete&resource[_2]&gocstr('/adm/cfile?action=delete','/priv/$udom/$uname/$cleandisfn')&Delete this resource
+ENDMENUITEMS
+                }
+                $menuitems .= (<<ENDMENUITEMS);
 s&7&2&prt.png&Print&printout[_1]&gocstr('/adm/printout','/priv/$udom/$uname/$cleandisfn')&Prepare a printable document
 ENDMENUITEMS
             }
@@ -1348,11 +1389,12 @@ sub prepare_functions {
 
     my $editbutton = '';
     my $viewsrcbutton = '';
+    my $clientip = &Apache::lonnet::get_requestor_ip();
 #
 # Determine whether or not to display 'Edit' or 'View Source' icon/button
 #
     if ($resurl =~ m{^/?adm/($match_domain)/($match_username)/aboutme$}) {
-        my $blocked = &Apache::loncommon::blocking_status('about',$2,$1);
+        my $blocked = &Apache::loncommon::blocking_status('about',$clientip,$2,$1);
         my $file=&Apache::lonnet::declutter($env{'request.filename'});
         ($cfile,$home,$switchserver,$forceedit,$forceview) =
             &Apache::lonnet::can_edit_resource($file,$cnum,$cdom,
@@ -1470,7 +1512,7 @@ sub prepare_functions {
         if ($resurl =~ m{^/?adm/($match_domain)/($match_username)/aboutme$}) {
             my ($sdom,$sname) = ($1,$2);
             unless (&Apache::lonnet::is_course($sdom,$sname)) {
-                my $blocked = &Apache::loncommon::blocking_status('about',$sname,$sdom);
+                my $blocked = &Apache::loncommon::blocking_status('about',$clientip,$sname,$sdom);
                 unless ($blocked) {
                     &switch('','',6,4,'mail-message-new-22x22.png','Message to user',
                             '',
@@ -1565,8 +1607,13 @@ sub advtools_crumbs {
         &Apache::lonhtmlcommon::add_breadcrumb_tool(
             'advtools', @funcs[61,64,65,66,67,74]);
     } elsif ($env{'request.noversionuri'} !~ m{^/adm/(navmaps|viewclasslist)(\?|$)}) {
-        &Apache::lonhtmlcommon::add_breadcrumb_tool(
-            'advtools', @funcs[61,71,72,73,74,92]);
+        if ($env{'request.state'} eq 'construct') {
+            &Apache::lonhtmlcommon::add_breadcrumb_tool(
+                'advtools', @funcs[61,73,74,71,72]);
+        } else {
+            &Apache::lonhtmlcommon::add_breadcrumb_tool(
+                'advtools', @funcs[61,71,72,73,74,92]);
+        }
     } elsif ($env{'request.noversionuri'} eq '/adm/viewclasslist') {
         &Apache::lonhtmlcommon::add_breadcrumb_tool(
             'advtools', $funcs[61]);
@@ -2207,13 +2254,17 @@ sub utilityfunctions {
 
     my $esc_url=&escape($currenturl);
     my $esc_symb=&escape($currentsymb);
+    my $newname = &mt('New Name');
 
     my $countdown = &countdown_toggle_js();
 
-    my $ltitarget;
+    my ($ltitarget,$deeplinktarget);
     if ($env{'request.lti.login'}) {
         $ltitarget = $env{'request.lti.target'};
     }
+    if ($env{'request.deeplink.login'}) {
+        $deeplinktarget = $env{'request.deeplink.target'};
+    }
 
     my $annotateurl = '/adm/annotation';
     if ($httphost) {
@@ -2277,6 +2328,22 @@ function gocstr(url,filename) {
         this.document.cstrdelete.submit();
         return;
     }
+    if ((url == '/adm/cfile?action=copy') || (url == '/adm/cfile?action=rename')) {
+        this.document.cstrcopy.filename.value = filename;
+        var oldname = filename.substring(filename.lastIndexOf("/") + 1);
+        var newname=prompt('$newname',oldname);
+        if (newname == "" || !newname || newname == oldname)  {
+            return;
+        }
+        if (url == '/adm/cfile?action=rename') {
+            this.document.cstrcopy.action.value = 'rename';
+        } else {
+            this.document.cstrcopy.action.value = 'copy';
+        }
+        this.document.cstrcopy.newfilename.value = newname;
+        this.document.cstrcopy.submit();
+        return;
+    }
     if (url == '/adm/printout') {
         this.document.cstrprint.postdata.value = filename
         this.document.cstrprint.curseed.value = 0;
@@ -2316,7 +2383,8 @@ function golist(url) {
        currentSymb= null;
        var lcHostname = setLCHost();
        var ltitarget = '$ltitarget';
-       if (ltitarget == 'iframe') {
+       var deeplinktarget = '$deeplinktarget';
+       if ((ltitarget == 'iframe') || (deeplinktarget == '_self')) {
            document.location.href=lcHostname+url;
        } else {
            top.location.href=lcHostname+url;
@@ -2414,6 +2482,9 @@ sub serverform {
     unless (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
         $target = ' target="_top"';
     }
+    if (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self')) {
+        $target = ' target="_self"';
+    }
     return(<<ENDSERVERFORM);
 <form name="server" action="/adm/logout" method="post"$target>
 <input type="hidden" name="postdata" value="none" />
@@ -2425,10 +2496,17 @@ ENDSERVERFORM
 }
 
 sub constspaceform {
+    my ($frameset) = @_;
     my ($target,$printtarget);
-    unless (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
-        $target = ' target="_top"';
+    if ($frameset) {
+        $target = ' target="_parent"';
         $printtarget = ' target="_parent"';
+    } else {
+        unless ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) ||
+                (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) {
+            $target = ' target="_top"';
+            $printtarget = ' target="_top"';
+        }
     }
     return(<<ENDCONSTSPACEFORM);
 <form name="constspace" action="/adm/logout" method="post"$target>
@@ -2443,6 +2521,11 @@ sub constspaceform {
 <input type="hidden" name="curseed" value="" />
 <input type="hidden" name="problemtype" value="" />
 </form>
+<form name="cstrcopy" action="/adm/cfile" method="post"$target>
+<input type="hidden" name="action" value="copy" />
+<input type="hidden" name="filename" value="" />
+<input type="hidden" name="newfilename" value="" />
+</form>
 
 ENDCONSTSPACEFORM
 }
@@ -2458,7 +2541,7 @@ sub hidden_button_check {
 }
 
 sub roles_selector {
-    my ($cdom,$cnum,$httphost,$ltitarget) = @_;
+    my ($cdom,$cnum,$httphost,$target,$menucoll,$menuref) = @_;
     my $crstype = &Apache::loncommon::course_type();
     my $now = time;
     my (%courseroles,%seccount,%courseprivs,%roledesc);
@@ -2564,10 +2647,15 @@ sub roles_selector {
         }
     }
     if ((keys(%seccount) > 1) || ($numdiffsec > 1)) {
+        my $targetattr;
+        if ($target ne '') {
+            $targetattr = ' target="'.$target.'"';
+        }
         my @submenu;
-        $js = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles,\%courseprivs,\%roledesc,$privref);
+        $js = &jump_to_role($cdom,$cnum,\%seccount,\%courseroles,\%courseprivs,
+                            \%roledesc,$privref,$menucoll,$menuref);
         $form = 
-            '<form name="rolechooser" method="post" action="'.$httphost.'/adm/roles">'."\n".
+            '<form name="rolechooser" method="post" action="'.$httphost.'/adm/roles"'.$targetattr.'>'."\n".
             '  <input type="hidden" name="destinationurl" value="'.
             &HTML::Entities::encode($destinationurl).'" />'."\n".
             '  <input type="hidden" name="gotorole" value="1" />'."\n".
@@ -2630,7 +2718,7 @@ sub roles_selector {
             }
         }
         if (@submenu > 0) {
-            $switcher = &create_submenu('','',&mt('Switch role'),\@submenu,'','',$ltitarget);
+            $switcher = &create_submenu('#',$target,&mt('Switch role'),\@submenu);
         }
     }
     return ($js,$form,$switcher,$has_opa_priv);
@@ -2800,7 +2888,8 @@ sub get_customadhoc_roles {
 }
 
 sub jump_to_role {
-    my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$roledesc,$privref) = @_;
+    my ($cdom,$cnum,$seccount,$courseroles,$courseprivs,$roledesc,$privref,
+        $menucoll,$menuref) = @_;
     my %lt = &Apache::lonlocal::texthash(
                 this => 'This role has section(s) associated with it.',
                 ente => 'Enter a specific section.',
@@ -2811,6 +2900,7 @@ sub jump_to_role {
                 role => 'The role you selected is not permitted to view the current page.',
                 swit => 'Switch role, but display Main Menu page instead?',
     );
+    &js_escape(\%lt);
     my $js;
     if (ref($courseroles) eq 'HASH') {
         $js = '    var secpick = new Array("'.$lt{'ente'}.'","'.$lt{'orlb'}.'");'."\n". 
@@ -2833,6 +2923,8 @@ sub jump_to_role {
         }
     }
     my $checkroles = 0;
+    my $fallback = '/adm/menu';
+    my $displaymsg = $lt{'swit'};
     if ((ref($privref) eq 'ARRAY') && (@{$privref} > 0) && (ref($courseprivs) eq 'HASH')) {
         my %disallowed;
         foreach my $role (sort(keys(%{$courseprivs}))) {
@@ -2854,8 +2946,22 @@ sub jump_to_role {
             $checkroles = 1;
             $js .= "    var disallow = new Array('".join("','",keys(%disallowed))."');\n".
                    "    var rolecheck = 1;\n";
+            if ($menucoll) {
+                if (ref($menuref) eq 'HASH') {
+                    if ($menuref->{'main'} eq 'n') {
+                        $fallback = '/adm/navmaps';
+                        if (&Apache::loncommon::course_type() eq 'Community') {
+                            $displaymsg = &mt('Switch role, but display Community Contents page instead?');
+                        } else {
+                            $displaymsg = &mt('Switch role, but display Course Contents page instead?');
+                        }
+                        &js_escape(\$displaymsg);
+                    }
+                }
+            }
         }
     }
+    &js_escape(\$fallback);
     if (!$checkroles) {
         $js .=  "    var disallow = new Array();\n".
                 "    rolecheck = 0;\n";
@@ -2878,8 +2984,8 @@ function adhocRole(newrole) {
     if (rolecheck > 0) {
         for (var i=0; i<disallow.length; i++) {
             if (disallow[i] == newrole) {
-                if (confirm("$lt{'role'}\\n$lt{'swit'}")) {
-                    document.rolechooser.destinationurl.value = '/adm/menu';
+                if (confirm("$lt{'role'}\\n$displaymsg")) {
+                    document.rolechooser.destinationurl.value = '$fallback';
                 } else {
                     return;
                 }