--- loncom/interface/loncommon.pm 2023/10/06 13:40:53 1.1075.2.161.2.20 +++ loncom/interface/loncommon.pm 2025/01/16 13:50:34 1.1075.2.161.2.32 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.1075.2.161.2.20 2023/10/06 13:40:53 raeburn Exp $ +# $Id: loncommon.pm,v 1.1075.2.161.2.32 2025/01/16 13:50:34 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -1728,8 +1728,6 @@ the id of the element to resize, second surrounds everything that comes after the textarea, this routine needs to be attached to the for the onload and onresize events. -=back - =cut sub resize_textarea_js { @@ -2016,6 +2014,107 @@ sub insert_folding_button { value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">"; } +=pod + +=item * &iframe_wrapper_headjs() + +emits javascript containing two global vars to facilitate handling of resizing +by code in iframe_wrapper_resizejs() used when an iframe is present in a page +with standard LON-CAPA menus. + +=cut + +# +# Where iframe is in use, if window.onload() executes before the custom resize function +# has been defined (jQuery), two global javascript vars (LCnotready and LCresizedef) +# are used to ensure document.ready() triggers a call to resize, so the iframe contents +# do not obscure the Functions menu. +# + +sub iframe_wrapper_headjs { + return <<"ENDJS"; + + +ENDJS + +} + +=pod + +=item * &iframe_wrapper_resizejs() + +emits javascript used to handle resizing for a page containing +an iframe, to ensure that the iframe does not obscure any +standard LON-CAPA menu items. + +=back + +=cut + +# +# jQuery to use when iframe is in use and a page resize occurs. +# This script will ensure that the iframe does not obscure any +# standard LON-CAPA inline menus (primary, secondary, and/or +# breadcrumbs and Functions menus. Expects javascript from +# &iframe_wrapper_headjs() to be in head portion of the web page, +# e.g., by inclusion in second arg passed to &start_page(). +# + +sub iframe_wrapper_resizejs { + my $offset = 5; + &get_unprocessed_cgi($ENV{'QUERY_STRING'},['inhibitmenu']); + if (($env{'form.inhibitmenu'} eq 'yes') || ($env{'form.only_body'})) { + $offset = 0; + } + return &Apache::lonhtmlcommon::scripttag(<'.&mt('Authoring Space:').' ' + .''.$title.' ' .'
' .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef); @@ -5935,6 +6055,147 @@ sub CSTR_pageheader { return $output; } +############################################## +=pod + +=item * &nocodemirror() + +Input: None + +Returns: 1 if CodeMirror is deactivated based on + user's preference, or domain default, + if user indicated use of default. + +=cut + +sub nocodemirror { + my $nocodem = $env{'environment.nocodemirror'}; + unless ($nocodem) { + my %domdefs = &Apache::lonnet::get_domain_defaults($env{'user.domain'}); + if ($domdefs{'nocodemirror'}) { + $nocodem = 'yes'; + } + } + if ($nocodem eq 'yes') { + return 1; + } + return; +} + +############################################## +=pod + +=item * &permitted_editors() + +Input: $uri (optional) + +Returns: %editors hash in which keys are editors + permitted in current Authoring Space, + or in current course for web pages + created in a course. + + Value for each key is 1. Possible keys + are: edit, xml, and daxe. + + For a regular Authoring Space, if no specific + set of editors has been set for the Author + who owns the Authoring Space, then the + domain default will be used. If no domain + default has been set, then the keys will be + edit and xml. + + For a course author, or for web pages created + in a course, if no specific set of editors has + been set for the course, then the domain + course default will be used. If no domain + course default has been set, then the keys + will be edit and xml. + +=cut + +sub permitted_editors { + my ($uri) = @_; + my ($is_author,$is_coauthor,$is_course,$auname,$audom,%editors); + if ($env{'request.role'} =~ m{^au\./}) { + $is_author = 1; + } elsif ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)}) { + ($audom,$auname) = ($1,$2); + if (($audom ne '') && ($auname ne '')) { + if (($env{'user.domain'} eq $audom) && + ($env{'user.name'} eq $auname)) { + $is_author = 1; + } else { + $is_coauthor = 1; + } + } + } elsif ($env{'request.course.id'}) { + my ($cdom,$cnum); + $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + if (($env{'request.editurl'} =~ m{^/priv/\Q$cdom/$cnum\E/}) || + ($env{'request.editurl'} =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}) || + ($uri =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/})) { + $is_course = 1; + } elsif ($env{'request.editurl'} =~ m{^/priv/($match_domain)/($match_username)/}) { + ($audom,$auname) = ($1,$2); + } elsif ($env{'request.uri'} =~ m{^/priv/($match_domain)/($match_username)/}) { + ($audom,$auname) = ($1,$2); + } elsif (($uri eq '/daxesave') && + (($env{'form.path'} =~ m{^/daxeopen/priv/\Q$cdom/$cnum\E/}) || + ($env{'form.path'} =~ m{^/daxeopen/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}))) { + $is_course = 1; + } elsif (($uri eq '/daxesave') && + ($env{'form.path'} =~ m{^/daxeopen/priv/($match_domain)/($match_username)/})) { + ($audom,$auname) = ($1,$2); + } + unless ($is_course) { + if (($audom ne '') && ($auname ne '')) { + if (($env{'user.domain'} eq $audom) && + ($env{'user.name'} eq $auname)) { + $is_author = 1; + } else { + $is_coauthor = 1; + } + } + } + } + if ($is_author) { + if (exists($env{'environment.editors'})) { + map { $editors{$_} = 1; } split(/,/,$env{'environment.editors'}); + } else { + %editors = ( edit => 1, + xml => 1, + ); + } + } elsif ($is_coauthor) { + if (exists($env{"environment.internal.editors./$audom/$auname"})) { + map { $editors{$_} = 1; } split(/,/,$env{"environment.internal.editors./$audom/$auname"}); + } else { + %editors = ( edit => 1, + xml => 1, + ); + } + } elsif ($is_course) { + if (exists($env{'course.'.$env{'request.course.id'}.'.internal.crseditors'})) { + map { $editors{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.internal.crseditors'}); + } else { + my %domdefaults = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); + if (exists($domdefaults{'crseditors'})) { + map { $editors{$_} = 1; } split(/,/,$domdefaults{'crseditors'}); + } else { + %editors = ( edit => 1, + xml => 1, + ); + } + } + } else { + %editors = ( edit => 1, + xml => 1, + ); + } + return %editors; +} + ############################################### ############################################### @@ -6158,15 +6419,18 @@ sub bodytag { my $funclist; if (($env{'environment.remote'} eq 'on') && ($env{'request.state'} ne 'construct')) { - $bodytag .= Apache::lonhtmlcommon::scripttag(Apache::lonmenu::utilityfunctions($httphost), 'start')."\n". - Apache::lonmenu::serverform(); - my $forbodytag; - &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, - $forcereg,$args->{'group'}, - $args->{'bread_crumbs'}, - $advtoolsref,'','',\$forbodytag); - unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') { - $funclist = $forbodytag; + unless ($args->{'switchserver'}) { + $bodytag .= Apache::lonhtmlcommon::scripttag(Apache::lonmenu::utilityfunctions($httphost), 'start')."\n". + Apache::lonmenu::serverform(); + my $forbodytag; + &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, + $forcereg,$args->{'group'}, + $args->{'bread_crumbs'}, + $advtoolsref,'','',\$forbodytag); + unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') { + $funclist = $forbodytag; + } + $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); } } else { @@ -6174,19 +6438,53 @@ sub bodytag { # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls # } - $bodytag .= Apache::lonhtmlcommon::scripttag( - Apache::lonmenu::utilityfunctions($httphost), 'start'); + my $need_endlcint; + unless ($args->{'switchserver'}) { + $bodytag .= Apache::lonhtmlcommon::scripttag( + Apache::lonmenu::utilityfunctions($httphost), 'start'); + $need_endlcint = 1; + } - unless ($args->{'no_primary_menu'}) { + my $collapsible; + if ($args->{'collapsible_header'} ne '') { + $collapsible = 1; + my ($menustate,$tiptext,$divclass); + if ($args->{'start_collapsed'}) { + $menustate = 'collapsed'; + $tiptext = 'display'; + $divclass = 'hidden'; + } else { + $menustate = 'expanded'; + $tiptext = 'hide'; + $divclass = 'shown'; + } + my $alttext = &mt('menu state: '.$menustate); + my $tooltip = &mt($tiptext.' standard menus'); + $bodytag .= <<"END"; +
+
+$alttext
+
+END + } + if (($env{'environment.remote'} eq 'on') && ($env{'request.state'} eq 'construct')) { + unless ($env{'form.inhibitmenu'}) { + $bodytag .= &inline_for_remote($public,$role,$realm,$dc_info,$no_inline_link); + } + } elsif (!$args->{'no_primary_menu'}) { my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref, $args->{'links_disabled'}, - $args->{'links_target'}); + $args->{'links_target'}, + $collapsible); if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) { if ($dc_info) { $dc_info = qq|$dc_info|; } $bodytag .= qq|
$left $role
$realm $dc_info
|; + if ($need_endlcint) { + $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); + } return $bodytag; } @@ -6204,11 +6502,16 @@ sub bodytag { #if directed to not display the secondary menu, don't. if ($args->{'no_secondary_menu'}) { + if ($need_endlcint) { + $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); + } return $bodytag; } #don't show menus for public users if (!$public){ - unless ($args->{'no_inline_menu'}) { + unless (($args->{'no_inline_menu'}) || + (($env{'environment.remote'} eq 'on') && + ($env{'request.state'} eq 'construct'))) { $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu, $args->{'no_primary_menu'}, $menucoll,$menuref, @@ -6216,7 +6519,9 @@ sub bodytag { $args->{'links_target'}); } $bodytag .= Apache::lonmenu::serverform(); - $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); + if ($need_endlcint) { + $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); + } if ($env{'request.state'} eq 'construct') { $bodytag .= &Apache::lonmenu::innerregister($forcereg, $args->{'bread_crumbs'},'','',$hostname, @@ -6236,13 +6541,19 @@ sub bodytag { $bodytag .= $forbodytag; } } - }else{ - # this is to seperate menu from content when there's no secondary + } else { + # this is to separate menu from content when there's no secondary # menu. Especially needed for public accessible ressources. $bodytag .= '
'; - $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); + if ($need_endlcint) { + $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); + } + } + if ($args->{'collapsible_header'} ne '') { + $bodytag .= $args->{'collapsible_header'}. + '
'. + '
'; } - return $bodytag; } @@ -6250,18 +6561,17 @@ sub bodytag { # Top frame rendering, Remote is up # - my $imgsrc = $img; - if ($img =~ /^\/adm/) { - $imgsrc = &lonhttpdurl($img); + my $linkattr; + if ($args->{'links_disabled'}) { + $linkattr = 'class="LCisDisabled" aria-disabled="true"'; } - my $upperleft=''.$function.''; my $help=($no_inline_link?'' - :&Apache::loncommon::top_nav_help('Help')); + :&top_nav_help('Help',$linkattr)); # Explicit link to get inline menu my $menu= ($no_inline_link?'' - :''.&mt('Switch to Inline Menu Mode').''); + :''.&mt('Switch to Inline Menu Mode').''); if ($dc_info) { $dc_info = qq|($dc_info)|; @@ -6269,31 +6579,46 @@ sub bodytag { my $name = &plainname($env{'user.name'},$env{'user.domain'}); unless ($public) { + my $class = 'LC_menubuttons_link'; + if ($args->{'links_disabled'}) { + $class .= ' LCisDisabled'; + } $name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'}, - undef,'LC_menubuttons_link'); + undef,$class); } unless ($env{'form.inhibitmenu'}) { - $bodytag .= qq|
$name $role
-
    -
  1. $help
  2. -
  3. $menu
  4. -
$realm $dc_info
|; - } - if ($env{'request.state'} eq 'construct') { - if (!$public){ - if ($env{'request.state'} eq 'construct') { - $funclist = &Apache::lonhtmlcommon::scripttag( - &Apache::lonmenu::utilityfunctions($httphost), 'start'). - &Apache::lonhtmlcommon::scripttag('','end'). - &Apache::lonmenu::innerregister($forcereg, - $args->{'bread_crumbs'}); - } - } + $bodytag .= &inline_for_remote($public,$role,$realm,$dc_info,$no_inline_link); } return $bodytag."\n".$funclist; } +sub inline_for_remote { + my ($public,$role,$realm,$dc_info,$no_inline_link) = @_; + my $help=($no_inline_link?'' + :&Apache::loncommon::top_nav_help('Help')); + + # Explicit link to get inline menu + my $menu= ($no_inline_link?'' + :''.&mt('Switch to Inline Menu Mode').''); + + if ($dc_info) { + $dc_info = qq|($dc_info)|; + } + + my $name = &plainname($env{'user.name'},$env{'user.domain'}); + unless ($public) { + $name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'}, + undef,'LC_menubuttons_link'); + } + + return qq|
$name $role
+
    +
  1. $help
  2. +
  3. $menu
  4. +
$realm $dc_info
|; +} + sub dc_courseid_toggle { my ($dc_info) = @_; return ' '. @@ -6479,6 +6804,7 @@ body { line-height:130%; font-size:0.83em; color:$font; + background-color: $pgbg_or_bgcolor; } a:focus, @@ -6490,6 +6816,14 @@ form, .inline { display: inline; } +.LC_menus_content.shown{ + display: block; +} + +.LC_menus_content.hidden { + display: none; +} + .LC_right { text-align:right; } @@ -6510,6 +6844,12 @@ form, .inline { width:400px; } +#LC_collapsible_separator { + border: 1px solid black; + width: 99.9%; + height: 0px; +} + .LC_iframecontainer { width: 98%; margin: 0; @@ -8395,6 +8735,16 @@ pre.LC_wordwrap { } /* + styles used for response display +*/ +div.LC_radiofoil, div.LC_rankfoil { + margin: .5em 0em .5em 0em; +} +table.LC_itemgroup { + margin-top: 1em; +} + +/* styles used by TTH when "Default set of options to pass to tth/m when converting TeX" in course settings has been set @@ -8415,6 +8765,87 @@ span.roman {font-family: serif; font-sty span.overacc2 {position: relative; left: .8em; top: -1.2ex;} span.overacc1 {position: relative; left: .6em; top: -1.2ex;} +/* + sections with roles, for content only +*/ +section[class^="role-"] { + padding-left: 10px; + padding-right: 5px; + margin-top: 8px; + margin-bottom: 8px; + border: 1px solid #2A4; + border-radius: 5px; + box-shadow: 0px 1px 1px #BBB; +} +section[class^="role-"]>h1 { + position: relative; + margin: 0px; + padding-top: 10px; + padding-left: 40px; +} +section[class^="role-"]>h1:before { + position: absolute; + left: -5px; + top: 5px; +} +section.role-activity>h1:before { + content:url('/adm/daxe/images/section_icons/activity.png'); +} +section.role-advice>h1:before { + content:url('/adm/daxe/images/section_icons/advice.png'); +} +section.role-bibliography>h1:before { + content:url('/adm/daxe/images/section_icons/bibliography.png'); +} +section.role-citation>h1:before { + content:url('/adm/daxe/images/section_icons/citation.png'); +} +section.role-conclusion>h1:before { + content:url('/adm/daxe/images/section_icons/conclusion.png'); +} +section.role-definition>h1:before { + content:url('/adm/daxe/images/section_icons/definition.png'); +} +section.role-demonstration>h1:before { + content:url('/adm/daxe/images/section_icons/demonstration.png'); +} +section.role-example>h1:before { + content:url('/adm/daxe/images/section_icons/example.png'); +} +section.role-explanation>h1:before { + content:url('/adm/daxe/images/section_icons/explanation.png'); +} +section.role-introduction>h1:before { + content:url('/adm/daxe/images/section_icons/introduction.png'); +} +section.role-method>h1:before { + content:url('/adm/daxe/images/section_icons/method.png'); +} +section.role-more_information>h1:before { + content:url('/adm/daxe/images/section_icons/more_information.png'); +} +section.role-objectives>h1:before { + content:url('/adm/daxe/images/section_icons/objectives.png'); +} +section.role-prerequisites>h1:before { + content:url('/adm/daxe/images/section_icons/prerequisites.png'); +} +section.role-remark>h1:before { + content:url('/adm/daxe/images/section_icons/remark.png'); +} +section.role-reminder>h1:before { + content:url('/adm/daxe/images/section_icons/reminder.png'); +} +section.role-summary>h1:before { + content:url('/adm/daxe/images/section_icons/summary.png'); +} +section.role-syntax>h1:before { + content:url('/adm/daxe/images/section_icons/syntax.png'); +} +section.role-warning>h1:before { + content:url('/adm/daxe/images/section_icons/warning.png'); +} + #LC_minitab_header { float:left; width:100%; @@ -8508,7 +8939,7 @@ sub headtag { $inhibitprint = &print_suppression(); } - if (!$args->{'frameset'}) { + if (!$args->{'frameset'} && !$args->{'switchserver'}) { $result .= &Apache::lonhtmlcommon::htmlareaheaders(); } if ($args->{'force_register'}) { @@ -8516,7 +8947,8 @@ sub headtag { } if (!$args->{'no_nav_bar'} && !$args->{'only_body'} - && !$args->{'frameset'}) { + && !$args->{'frameset'} + && !$args->{'switchserver'}) { $result .= &help_menu_js($httphost); $result.=&modal_window(); $result.=&togglebox_script(); @@ -8712,8 +9144,12 @@ OFFLOAD $title = 'The LearningOnline Network with CAPA'; } if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); } - $result .= ' LON-CAPA '.$title.'' - .''; + } else { + $result .= ' LON-CAPA '.$title.''; + } + $result .= "\n".'{'frameset'}) { $result .= ' /'; } @@ -8914,6 +9350,11 @@ $args - additional optional args support no_auto_mt_title -> prevent &mt()ing the title arg bread_crumbs -> Array containing breadcrumbs bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs + bread_crumbs_style -> breadcrumbs are contained within
, + and &standard_css() contains CSS for #LC_breadcrumbs, if you want + to override those values, or add to them, specify the value to + include in the style attribute to include in the div tag by using + bread_crumbs_style (e.g., overflow: visible) bread_crumbs_nomenu -> if true will pass false as the value of $menulink to lonhtmlcommon::breadcrumbs group -> includes the current group, if page is for a @@ -9079,9 +9520,12 @@ sub start_page { } #if bread_crumbs_component exists show it as headline else show only the breadcrumbs if(exists($args->{'bread_crumbs_component'})){ - $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink); + $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'}, + '',$menulink,'', + $args->{'bread_crumbs_style'}); } else { - $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink); + $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink,'', + $args->{'bread_crumbs_style'}); } } } elsif (($env{'environment.remote'} eq 'on') && @@ -16775,7 +17219,8 @@ sub init_user_environment { # See if old ID present, if so, remove - my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv); + my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv, + $coauthorenv); my $now=time; if ($public) { @@ -16840,7 +17285,7 @@ sub init_user_environment { # Initialize roles - ($userroles,$firstaccenv,$timerintenv) = + ($userroles,$firstaccenv,$timerintenv,$coauthorenv) = &Apache::lonnet::rolesinit($domain,$username,$authhost); } # ------------------------------------ Check browser type and MathML capability @@ -16919,8 +17364,8 @@ sub init_user_environment { my %is_adv = ( is_adv => $env{'user.adv'} ); my %domdef = &Apache::lonnet::get_domain_defaults($domain); - foreach my $tool ('aboutme','blog','webdav','portfolio','timezone') { - $userenv{'availabletools.'.$tool} = + foreach my $tool ('aboutme','blog','webdav','portfolio','portaccess','timezone') { + $userenv{'availabletools.'.$tool} = &Apache::lonnet::usertools_access($username,$domain,$tool,'reload', undef,\%userenv,\%domdef,\%is_adv); } @@ -16932,6 +17377,23 @@ sub init_user_environment { \%userenv,\%domdef,\%is_adv); } + if ((ref($userroles) eq 'HASH') && ($userroles->{'user.author'}) && + (exists($userroles->{"user.role.au./$domain/"}))) { + if ($userenv{'authoreditors'}) { + $userenv{'editors'} = $userenv{'authoreditors'}; + } elsif ($domdef{'editors'} ne '') { + $userenv{'editors'} = $domdef{'editors'}; + } else { + $userenv{'editors'} = 'edit,xml'; + } + if ($userenv{'authorarchive'}) { + $userenv{'canarchive'} = 1; + } elsif (($userenv{'authorarchive'} eq '') && + ($domdef{'archive'})) { + $userenv{'canarchive'} = 1; + } + } + $userenv{'canrequest.author'} = &Apache::lonnet::usertools_access($username,$domain,'requestauthor', 'reload','requestauthor', @@ -16960,6 +17422,11 @@ sub init_user_environment { if (ref($timerintenv) eq 'HASH') { &_add_to_env(\%disk_env,$timerintenv); } + if (ref($coauthorenv) eq 'HASH') { + if (keys(%{$coauthorenv})) { + &_add_to_env(\%disk_env,$coauthorenv); + } + } if (ref($args->{'extra_env'})) { &_add_to_env(\%disk_env,$args->{'extra_env'}); }