--- loncom/homework/structuretags.pm 2011/12/12 11:19:53 1.497.2.1 +++ loncom/homework/structuretags.pm 2023/04/02 03:16:28 1.574 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # definition of tags that give a structure to a document # -# $Id: structuretags.pm,v 1.497.2.1 2011/12/12 11:19:53 foxr Exp $ +# $Id: structuretags.pm,v 1.574 2023/04/02 03:16:28 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -61,14 +61,115 @@ use Apache::lonlocal; use Apache::lonxml; use Apache::londefdef; use Apache::lonenc(); +use Apache::loncommon(); use Time::HiRes qw( gettimeofday tv_interval ); +use HTML::Entities(); use lib '/home/httpd/lib/perl/'; use LONCAPA; BEGIN { - &Apache::lonxml::register('Apache::structuretags',('block','languageblock','translated','instructorcomment','while','randomlist','problem','library','web','tex','part','preduedate','postanswerdate','solved','notsolved','problemtype','startpartmarker','startouttext','endpartmarker','endouttext','simpleeditbutton','definetag')); + &Apache::lonxml::register('Apache::structuretags',('block','languageblock','translated','instructorcomment','while','randomlist','problem','library','web','print','tex','part','preduedate','postanswerdate','solved','notsolved','problemtype','startpartmarker','startouttext','endpartmarker','endouttext','simpleeditbutton','definetag')); } + +#--------------------------------------------------------------------------------- +# +# This section of code deals with hyphenation management. +# We must do three things: +# - keep track fo the desired languages to alter the header. +# - provide hyphenation selection as needed by each language that appears in the +# text. +# - Provide the header text needed to make available the desired hyphenations. +# +# + +# Hash whose keys are the languages encountered in the document/resource. +# + +my %languages_required; +## +# Given a language selection as input returns a chunk of LaTeX that +# selects the required hyphenator. +# +# @param language - the language being selected. +# @return string +# @retval The LaTeX needed to select the hyphenation appropriate to the language. +# +sub select_hyphenation { + my $language = shift; + + $language = &Apache::loncommon::latexlanguage($language); # Translate -> latex language. + + # If there is no latex language there's not much we can do: + + if ($language) { + &require_language($language); + my $babel_hyphenation = "\\selectlanguage{$language}"; + + return $babel_hyphenation; + } else { + return ''; + } +} +## +# Selects hyphenation based on the current problem metadata. +# This requires that +# - There is a language metadata item set for the problem. +# - The language has a latex/babel hyphenation. +# +# @note: Uses &Apache::lonxml::request to locate the Uri associated with +# this problem. +# @return string (possibly empty). +# @retval If not empty an appropriate \selectlanguage{} directive. +# +sub select_metadata_hyphenation { + my $uri = $Apache::lonxml::request->uri; + my $language = &Apache::lonnet::metadata($uri, 'language'); + my $latex_language = &Apache::loncommon::latexhyphenation($language); + if ($latex_language) { + return '\selectlanguage{'.$latex_language."}\n"; + } + return ''; # no latex hyphenation or no lang metadata. +} + + +## +# Clears the set of languages required by the document being rendered. +# +sub clear_required_languages { + %languages_required = (); +} +## +# Allows an external client of this module to register a need for a language: +# +# @param LaTeX language required: +# +sub require_language { + my $language = shift; + $languages_required{$language} = 1; +} + +## +# Provides the header for babel that indicates the languages +# the document requires. +# @return string +# @retval \usepackage[lang1,lang2...]{babel} +# @retval '' if there are no languages_required. +sub languages_header { + my $header =''; + my @languages = (keys(%languages_required)); + + # Only generate the header if there are languages: + + if (scalar @languages) { + my $language_list = join(',', (@languages)); + $header = '\usepackage['.$language_list."]{babel}\n"; + } + return $header; +} + +#---------------------------------------------------------------------------------- + sub start_web { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; if ($target ne 'edit' && $target ne 'modified') { @@ -92,6 +193,28 @@ sub end_web { return ''; } +sub start_print { + my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; + if ($target ne 'edit' && $target ne 'modified') { + if ($target ne 'tex') { + my $skip = &Apache::lonxml::get_all_text("/print",$parser,$style); + &Apache::lonxml::debug("skipping ahead :$skip: $$parser[-1]"); + } + } elsif ($target eq "edit") { + my $bodytext = &Apache::lonxml::get_all_text_unbalanced("/print",$parser); + my $result = &Apache::edit::tag_start($target,$token); + $result .= &Apache::edit::editfield($token->[1],$bodytext,'',80,1); + return $result; + } elsif ($target eq "modified") { + return $token->[4].&Apache::edit::modifiedfield("/print",$parser); + } + return ''; +} + +sub end_print { + return ''; +} + sub start_tex { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result=''; @@ -124,9 +247,57 @@ sub end_tex { } sub homework_js { + my ($postsubmit,$timeout); + if (($env{'request.course.id'}) && ($env{'request.state'} ne 'construct')) { + my $crstype = &Apache::loncommon::course_type(); + if ($crstype eq 'Community') { + $crstype = 'community'; + } elsif ($crstype eq 'Placement') { + $crstype = 'placement'; + } else { + if ($env{'course.'.$env{'request.course.id'}.'.internal.coursecode'}) { + $crstype = 'official'; + } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.textbook'}) { + $crstype = 'textbook'; + } else { + $crstype = 'unofficial'; + } + } + $postsubmit = $env{'course.'.$env{'request.course.id'}.'.internal.postsubmit'}; + if ($postsubmit eq '') { + my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); + $postsubmit = $domdefs{'postsubmit'}; + unless ($postsubmit eq 'off') { + $timeout = $domdefs{$crstype.'postsubtimeout'}; + } + } elsif ($postsubmit eq '0') { + $postsubmit = 'off'; + } elsif ($postsubmit eq '1') { + $postsubmit = 'on'; + $timeout = $env{'course.'.$env{'request.course.id'}.'.internal.postsubtimeout'}; + if ($timeout eq '') { + my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); + $timeout = $domdefs{$crstype.'postsubtimeout'}; + } + } + if ($timeout eq '') { + $timeout = 60; + } + } else { + my %domdefs = &Apache::lonnet::get_domain_defaults($env{'request.role.domain'}); + $postsubmit = $domdefs{'postsubmit'}; + unless ($postsubmit eq 'off') { + $timeout = 60; + } + } + my $jstimeout = 0; + if ($timeout) { + $jstimeout = 1000 * $timeout; + } return &Apache::loncommon::resize_textarea_js(). + &Apache::loncommon::colorfuleditor_js(). &setmode_javascript(). - <<'JS'; + <<"JS"; JS @@ -164,8 +413,10 @@ sub setmode_javascript { @@ -191,13 +442,17 @@ sub page_start { $extra_head .= &homework_js(). &Apache::lonhtmlcommon::dragmath_js("EditMathPopup"); if (&Apache::lonhtmlcommon::htmlareabrowser()) { - my %textarea_args = ( + my %textarea_args; + if (($env{'request.state'} ne 'construct') || + ($env{'environment.nocodemirror'})) { + %textarea_args = ( dragmath => 'math', ); + } $extra_head .= &Apache::lonhtmlcommon::htmlareaselectactive(\%textarea_args); } my $is_task = ($env{'request.uri'} =~ /\.task$/); - my $needs_upload; + my ($needs_upload,$partlist); my ($symb)= &Apache::lonnet::whichuser(); my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb); if ($is_task) { @@ -215,6 +470,12 @@ sub page_start { unless ($is_page) { $needs_upload = 1; } + if ((ref($tagstack) eq 'ARRAY') && ($tagstack->[-1] eq 'problem')) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + $partlist = $res->parts(); + } + } } } } else { @@ -226,10 +487,17 @@ sub page_start { if (ref($mapres)) { $is_page = $mapres->is_page(); } - unless ($is_page) { + if ($is_page) { + if ((ref($tagstack) eq 'ARRAY') && ($tagstack->[-1] eq 'problem')) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + $partlist = $res->parts(); + } + } + } else { my $res = $navmap->getBySymb($symb); if (ref($res)) { - my $partlist = $res->parts(); + $partlist = $res->parts(); if (ref($partlist) eq 'ARRAY') { foreach my $part (@{$partlist}) { my @types = $res->responseType($part); @@ -251,7 +519,9 @@ sub page_start { } } if ($needs_upload) { - $extra_head .= &Apache::lonhtmlcommon::file_submissionchk_js(); + $extra_head .= &Apache::lonhtmlcommon::file_submissionchk_js() + .''; } } @@ -270,6 +540,9 @@ sub page_start { "if (typeof swmenu != 'undefined') {swmenu.currentURL=null;}\n". &Apache::loncommon::browser_and_searcher_javascript(). "\n\n"; + if ($target eq 'edit') { + $extra_head .= &Apache::edit::js_update_linknum(); + } } } @@ -279,23 +552,25 @@ sub page_start { } elsif (!defined($found{'body'}) && $env{'request.state'} eq 'construct') { if ($target eq 'web' || $target eq 'edit') { - # Breadcrumbs for Construction Space - &Apache::lonhtmlcommon::clear_breadcrumbs(); - &Apache::lonhtmlcommon::add_breadcrumb({ - 'text' => 'Construction Space', - 'href' => &Apache::loncommon::authorspace($env{'request.uri'}), - }); - # breadcrumbs (and tools) will be created - # in start_page->bodytag->innerregister + unless ($env{'form.inhibitmenu'} eq 'yes') { + # Breadcrumbs for Authoring Space + &Apache::lonhtmlcommon::clear_breadcrumbs(); + &Apache::lonhtmlcommon::add_breadcrumb({ + 'text' => 'Authoring Space', + 'href' => &Apache::loncommon::authorspace($env{'request.uri'}), + }); + # breadcrumbs (and tools) will be created + # in start_page->bodytag->innerregister # FIXME Where are we? -# &Apache::lonhtmlcommon::add_breadcrumb({ -# 'text' => 'Problem Editing', # 'Problem Testing' -# 'href' => '', -# }); - $pageheader =&Apache::loncommon::head_subbox( - &Apache::loncommon::CSTR_pageheader()); - } +# &Apache::lonhtmlcommon::add_breadcrumb({ +# 'text' => 'Problem Editing', # 'Problem Testing' +# 'href' => '', +# }); + $pageheader = &Apache::loncommon::head_subbox( + &Apache::loncommon::CSTR_pageheader()); + } + } } elsif (!defined($found{'body'})) { my %add_entries; my $background=&Apache::lonxml::get_param('background',$parstack, @@ -314,6 +589,8 @@ sub page_start { $body_args{'add_entries'} = \%add_entries; if ( $env{'request.state'} eq 'construct') { $body_args{'only_body'} = 1; + } elsif ($target eq 'web') { + $body_args{'print_suppress'} = 1; } } $body_args{'no_auto_mt_title'} = 1; @@ -348,12 +625,13 @@ sub page_start { 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"; - my $symb=&Apache::lonnet::symbread(); if ($symb =~ /\S/) { $symb= &HTML::Entities::encode(&Apache::lonenc::check_encrypt($symb)); @@ -361,7 +639,7 @@ sub page_start { "\t".''."\n"; } } - return ($page_start,$form_tag_start); + return ($page_start,$form_tag_start,$partlist); } #use Time::HiRes(); @@ -392,19 +670,45 @@ sub get_resource_name { } sub setup_rndseed { - my ($safeeval,$target)=@_; + my ($safeeval,$target,$probpartlist,$prevparttype)=@_; my ($symb)=&Apache::lonnet::whichuser(); - my ($questiontype,$set_safespace,$rndseed); + my ($questiontype,$set_safespace,$rndseed,$numtries,$reqtries); if ($target eq 'analyze') { $questiontype = $env{'form.grade_questiontype'}; } unless (defined($questiontype)) { $questiontype = $Apache::lonhomework::type; } - if ($env{'request.state'} eq "construct" - || $symb eq '' - || $Apache::lonhomework::type eq 'practice' - || $Apache::lonhomework::history{'resource.CODE'}) { + if ($Apache::lonhomework::type eq 'randomizetry') { + my $partfortries = $Apache::inputtags::part; +# +# Where question type is "randomizetry" for a problem containing +# a single part (and unless type is explicitly set to not be +# "randomizetry" for that part), the number of tries used to +# determine randomization will be for that part, and randomization +# from calls to &random() in a perl script block before the part tag, +# will change based on the number of tries, and value of the +# "randomizeontries" parameter in effect for the single part. +# + if (ref($probpartlist) eq 'ARRAY') { + if ((@{$probpartlist} == 1) && ($probpartlist->[0] ne $partfortries)) { + if (&Apache::lonnet::EXT('resource.'.$probpartlist->[0].'.type') eq 'randomizetry') { + $partfortries = $probpartlist->[0]; + } else { + $partfortries = ''; + } + } + } + if ($partfortries ne '') { + $numtries = $Apache::lonhomework::history{"resource.$partfortries.tries"}; + $reqtries = &Apache::lonnet::EXT("resource.$partfortries.randomizeontries"); + } + } + if (($env{'request.state'} eq "construct") + || ($symb eq '') + || ($Apache::lonhomework::type eq 'practice') + || ($Apache::lonhomework::history{'resource.CODE'}) + || (($env{'form.code_for_randomlist'}) && ($target eq 'analyze'))) { &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['rndseed']); $rndseed=$env{'form.rndseed'}; @@ -413,17 +717,23 @@ sub setup_rndseed { if (!$rndseed) { $rndseed=time; } - $env{'form.rndseed'}=$rndseed; + unless ($env{'form.code_for_randomlist'}) { + $env{'form.rndseed'}=$rndseed; + } } - if (($env{'request.state'} eq "construct") && + if ((($env{'request.state'} eq "construct") || ($symb eq '')) && ($Apache::lonhomework::type eq 'randomizetry')) { - my $tries = $Apache::lonhomework::history{"resource.$Apache::inputtags::part.tries"}; - if ($tries) { - $rndseed += $tries; + if ($numtries) { + if (($reqtries =~ /^\d+$/) && ($reqtries > 1)) { + my $inc = int($numtries/$reqtries); + $rndseed += $inc; + } else { + $rndseed += $numtries; + } } $env{'form.'.$Apache::inputtags::part.'.rndseed'}=$rndseed; } - if ( ($env{'form.resetdata'} eq &mt('New Problem Variation') + if ( ($env{'form.resetdata'} eq 'new_problem_variation' && $env{'form.submitted'} eq 'yes') || $env{'form.newrandomization'} eq &mt('New Randomization')) { srand(time); @@ -441,7 +751,11 @@ sub setup_rndseed { $rndseed=join(':',&Apache::lonnet::digest($rndseed)); } } - if ($Apache::lonhomework::history{'resource.CODE'}) { + if (($env{'form.code_for_randomlist'}) && ($target eq 'analyze')) { + $env{'form.CODE'} = $env{'form.code_for_randomlist'}; + $rndseed=&Apache::lonnet::rndseed(); + undef($env{'form.CODE'}); + } elsif ($Apache::lonhomework::history{'resource.CODE'}) { $rndseed=&Apache::lonnet::rndseed(); } $set_safespace = 1; @@ -453,7 +767,7 @@ sub setup_rndseed { } unless (($target eq 'analyze') && (defined($rndseed))) { $rndseed=&Apache::lonnet::rndseed(); - my $curr_try = $Apache::lonhomework::history{"resource.$Apache::inputtags::part.tries"}; + my $curr_try = $numtries; if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { $curr_try ++; } @@ -461,7 +775,6 @@ sub setup_rndseed { $rndseed = $1; } if ($curr_try) { - my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); if (($reqtries =~ /^\d+$/) && ($reqtries > 1)) { my $inc = int(($curr_try-1)/$reqtries); $rndseed += $inc; @@ -471,11 +784,23 @@ sub setup_rndseed { } } $set_safespace = 1; + if ($target eq 'grade') { + $Apache::lonhomework::rawrndseed = $rndseed; + } + } elsif ($prevparttype eq 'randomizetry') { + if ($env{'form.0.rndseed'} ne '') { + $set_safespace = 1; + $rndseed = $env{'form.0.rndseed'}; + } } if ($set_safespace) { if ($safeeval) { &Apache::lonxml::debug("Setting rndseed to $rndseed"); &Apache::run::run('$external::randomseed="'.$rndseed.'";',$safeeval); + if (($Apache::lonhomework::type eq 'randomizetry') || ($prevparttype eq 'randomizetry')) { + &Apache::lonxml::debug("Setting randomizetrypart to $Apache::inputtags::part"); + &Apache::run::run('$external::randomizetrypart="'.$Apache::inputtags::part.'";',$safeeval); + } } } unless (($env{'request.state'} eq "construct") || ($symb eq '')) { @@ -500,53 +825,81 @@ sub problem_edit_action_button { sub problem_edit_buttons { my ($mode)=@_; +# Buttons that save + my $result = '
';
+
+ ' . &Apache::lonxml::message_location();
+ $return .= '
+ ';
+
+ $return .= '
+
+
+ '.&mt('Problem Editing').$mode.&Apache::loncommon::help_open_menu('Problem Editing','Problem_Editor_XML_Index',5,'Authoring').'
+
'. "\n\n".&Apache::loncommon::end_page(); @@ -611,7 +964,7 @@ sub problem_web_to_edit_header { ".&mt("Problem Type:")." |