--- loncom/homework/structuretags.pm 2011/01/07 05:14:57 1.465.2.12 +++ loncom/homework/structuretags.pm 2015/12/10 16:26:43 1.543 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # definition of tags that give a structure to a document # -# $Id: structuretags.pm,v 1.465.2.12 2011/01/07 05:14:57 raeburn Exp $ +# $Id: structuretags.pm,v 1.543 2015/12/10 16:26:43 damieng Exp $ # # Copyright Michigan State University Board of Trustees # @@ -61,6 +61,7 @@ use Apache::lonlocal; use Apache::lonxml; use Apache::londefdef; use Apache::lonenc(); +use Apache::loncommon(); use Time::HiRes qw( gettimeofday tv_interval ); use lib '/home/httpd/lib/perl/'; use LONCAPA; @@ -69,6 +70,105 @@ 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')); } + +#--------------------------------------------------------------------------------- +# +# 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') { @@ -124,13 +224,78 @@ sub end_tex { } sub homework_js { + my ($postsubmit,$timeout); + if (($env{'request.course.id'}) && ($env{'request.state'} ne 'construct')) { + my $crstype; + if (&Apache::loncommon::course_type() eq 'Community') { + $crstype = 'community'; + } 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 @@ -154,52 +388,11 @@ JS sub setmode_javascript { return <<"ENDSCRIPT"; -ENDSCRIPT -} - -sub file_delchk_js { - my $delfilewarn = &mt('You have indicated you wish to remove some files previously included in your submission.').'\\n'. - &mt('Continue submission with these files removed?'); - return <<"ENDSCRIPT"; - ENDSCRIPT @@ -230,8 +423,77 @@ sub page_start { $extra_head .= &Apache::lonhtmlcommon::htmlareaselectactive(\%textarea_args); } my $is_task = ($env{'request.uri'} =~ /\.task$/); + my ($needs_upload,$partlist); + my ($symb)= &Apache::lonnet::whichuser(); + my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb); if ($is_task) { - $extra_head .= &file_delchk_js(); + $extra_head .= &Apache::lonhtmlcommon::file_submissionchk_js(); + } else { + if (&Apache::lonnet::EXT("resource.$Apache::inputtags::part.uploadedfiletypes") ne '') { + unless ($env{'request.state'} eq 'construct') { + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + my $mapres = $navmap->getResourceByUrl($map); + my $is_page; + if (ref($mapres)) { + $is_page = $mapres->is_page(); + } + 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 { + unless ($env{'request.state'} eq 'construct') { + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + my $mapres = $navmap->getResourceByUrl($map); + my $is_page; + if (ref($mapres)) { + $is_page = $mapres->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)) { + $partlist = $res->parts(); + if (ref($partlist) eq 'ARRAY') { + foreach my $part (@{$partlist}) { + my @types = $res->responseType($part); + my @ids = $res->responseIds($part); + for (my $i=0; $i < scalar(@ids); $i++) { + if ($types[$i] eq 'essay') { + my $partid = $part.'_'.$ids[$i]; + if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') { + $needs_upload = 1; + last; + } + } + } + } + } + } + } + } + } + } + if ($needs_upload) { + $extra_head .= &Apache::lonhtmlcommon::file_submissionchk_js() + .''; + } } my %body_args; @@ -249,6 +511,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(); + } } } @@ -258,26 +523,25 @@ sub page_start { } elsif (!defined($found{'body'}) && $env{'request.state'} eq 'construct') { if ($target eq 'web' || $target eq 'edit') { - if ($env{'environment.remote'} ne 'off') { - $body_args{'only_body'} = 1; - } - # Breadcrumbs for Construction Space - &Apache::lonhtmlcommon::clear_breadcrumbs(); - &Apache::lonhtmlcommon::add_breadcrumb({ - 'text' => 'Construction Space', - 'href' => &Apache::loncommon::authorspace(), - }); - # 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, @@ -294,9 +558,10 @@ sub page_start { # $body_args{'no_title'} = 1; $body_args{'force_register'} = 1; $body_args{'add_entries'} = \%add_entries; - if ($env{'environment.remote'} eq 'off' - && $env{'request.state'} eq 'construct') { + 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; @@ -318,7 +583,6 @@ sub page_start { if (!defined($found{'body'}) && $env{'request.state'} ne 'construct') { $page_start .= &Apache::lonxml::message_location(); } - my $form_tag_start; if (!defined($found{'form'})) { $form_tag_start='
'."\n"; } } - return ($page_start,$form_tag_start); + return ($page_start,$form_tag_start,$partlist); } #use Time::HiRes(); @@ -373,7 +639,7 @@ sub get_resource_name { } sub setup_rndseed { - my ($safeeval,$target)=@_; + my ($safeeval,$target,$probpartlist)=@_; my ($symb)=&Apache::lonnet::whichuser(); my ($questiontype,$set_safespace,$rndseed); if ($target eq 'analyze') { @@ -396,7 +662,7 @@ sub setup_rndseed { } $env{'form.rndseed'}=$rndseed; } - if (($env{'request.state'} eq "construct") && + if (($env{'request.state'} eq "construct") && ($Apache::lonhomework::type eq 'randomizetry')) { my $tries = $Apache::lonhomework::history{"resource.$Apache::inputtags::part.tries"}; if ($tries) { @@ -413,8 +679,14 @@ sub setup_rndseed { delete($env{'form.resetdata'}); delete($env{'form.newrandomization'}); } - if (defined($rndseed) && $rndseed ne int($rndseed)) { - $rndseed=join(':',&Apache::lonnet::digest($rndseed)); + $rndseed=~s/\,/\:/g; + $rndseed=~s/[^\w\d\:\-]//g; + if (defined($rndseed)) { + my ($c1,$c2)=split(/\:/,$rndseed); + unless ($c2) { $c2=0; } + unless (($c1==int($c1)) && ($c2==int($c2))) { + $rndseed=join(':',&Apache::lonnet::digest($rndseed)); + } } if ($Apache::lonhomework::history{'resource.CODE'}) { $rndseed=&Apache::lonnet::rndseed(); @@ -428,7 +700,13 @@ 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 $partfortries = $Apache::inputtags::part; + if (ref($probpartlist) eq 'ARRAY') { + if ((@{$probpartlist} == 1) && ($probpartlist->[0] ne $Apache::inputtags::part)) { + $partfortries = $probpartlist->[0]; + } + } + my $curr_try = $Apache::lonhomework::history{"resource.$partfortries.tries"}; if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { $curr_try ++; } @@ -436,7 +714,7 @@ sub setup_rndseed { $rndseed = $1; } if ($curr_try) { - my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); + my $reqtries = &Apache::lonnet::EXT("resource.$partfortries.randomizeontries"); if (($reqtries =~ /^\d+$/) && ($reqtries > 1)) { my $inc = int(($curr_try-1)/$reqtries); $rndseed += $inc; @@ -446,6 +724,9 @@ sub setup_rndseed { } } $set_safespace = 1; + if ($target eq 'grade') { + $Apache::lonhomework::rawrndseed = $rndseed; + } } if ($set_safespace) { if ($safeeval) { @@ -466,41 +747,88 @@ sub remember_problem_state { '; } +sub problem_edit_action_button { + my ($name,$action,$accesskey,$text,$flag)=@_; + my $actionscript="setmode(this.form,'$action')"; + return "\n"; +} + sub problem_edit_buttons { - return ' -
- - - -
-
- - -
'; + my ($mode)=@_; +# Buttons that save + my $result = '
'; + if ($mode eq 'editxml') { + $result.=&problem_edit_action_button('subsaveedit','saveeditxml','s','Save and EditXML'); + $result.=&problem_edit_action_button('subsaveview','saveviewxml','v','Save and View'); + } else { + $result.=&problem_edit_action_button('subsaveedit','saveedit','s','Save and Edit'); + $result.=&problem_edit_action_button('subsaveview','saveview','v','Save and View'); + } + $result.="\n
\n"; +# Buttons that do not save + $result .= '
'. + &problem_edit_action_button('subdiscview','discard','d','Discard Edits and View',1); + if ($mode eq 'editxml') { + $result.=&problem_edit_action_button('subedit','edit','e','Edit',1); + $result.=&problem_edit_action_button('subundo','undoxml','u','Undo',1); + $result.=&Apache::lonhtmlcommon::dragmath_button("LC_editxmltext",1); + } else { + $result.=&problem_edit_action_button('subeditxml','editxml','x','EditXML',1); + $result.=&problem_edit_action_button('subundo','undo','u','Undo',1); + } + $result.="\n
"; + return $result; } sub problem_edit_header { - return ''. - &Apache::structuretags::remember_problem_state().' -
-
-'.&mt('Problem Editing').&Apache::loncommon::help_open_menu('Problem Editing','Problem_Editor_XML_Index',5,'Authoring').' -
'. -&problem_edit_buttons().' -
-'.&Apache::lonxml::message_location().' -
-'. - ''; $result.=&Apache::edit::end_table(); - } + } return $result; } @@ -1883,6 +2627,10 @@ sub end_endpartmarker { return @result; } + + + + sub start_part { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; if (!$Apache::lonxml::metamode) { @@ -1893,6 +2641,7 @@ sub start_part { my $id= &Apache::lonxml::get_id($parstack,$safeeval); $Apache::inputtags::part=$id; push(@Apache::inputtags::partlist,$id); + $Apache::inputtags::leniency=''; @Apache::inputtags::response=(); @Apache::inputtags::previous=(); @Apache::inputtags::previous_version=(); @@ -1902,6 +2651,14 @@ sub start_part { my $hidden=&Apache::loncommon::check_if_partid_hidden($Apache::inputtags::part); my $newtype=&Apache::lonnet::EXT("resource.$id.type"); if ($newtype) { $Apache::lonhomework::type=$newtype; } + if ($Apache::lonhomework::type eq 'randomizetry') { + my $rndseed=&setup_rndseed($safeeval,$target); + if (($target eq 'grade') && &Apache::response::submitted()) { + $Apache::lonhomework::results{"resource.$id.rndseed"}=$rndseed; + } + } elsif (($target eq 'grade') && &Apache::response::submitted()) { + $Apache::lonhomework::results{"resource.$id.rndseed"}=$Apache::lonhomework::rawrndseed; + } my $in_order_show=&ordered_show_check(); my $expression='$external::part=\''.$Apache::inputtags::part.'\';'; $expression.='$external::type=\''.$Apache::lonhomework::type.'\';'; @@ -1952,7 +2709,7 @@ sub start_part { } my $weight = &Apache::lonnet::EXT("resource.$id.weight"); my $allkeys=&Apache::lonnet::metadata($env{'request.uri'},'packages'); - my @allkeys = split /,/,$allkeys; + my @allkeys = split(/,/,$allkeys); my $allow_print_points = 0; foreach my $partial_key (@allkeys) { if ($partial_key=~m/^part_(.*)$/) { @@ -1968,12 +2725,12 @@ sub start_part { $allow_print_points=0; } if (($Apache::lonhomework::type eq 'exam') && ($allow_print_points)) { - $result .= '\vskip 10mm\fbox{\textit{'.$weight.' pt}}'; + $result .= '\vskip 10mm\fbox{\textit{'.&mt('[quant,_1,pt,pt]',$weight ).'}}'; } } elsif ($target eq 'web') { if ($status eq 'CAN_ANSWER') { - my $problemstatus = &get_problem_status($Apache::inputtags::part); + my $problemstatus = &get_problem_status($Apache::inputtags::part); my $probrandomize = &Apache::lonnet::EXT("resource.$Apache::inputtags::partlist[0].type"); my $probrandtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::partlist[0].randomizeontries"); my $num = scalar(@Apache::inputtags::partlist)-1; @@ -1991,7 +2748,7 @@ sub start_part { $result .= &randomizetry_part_header($problemstatus,$reqtries,$num); } } - $result.=''; + $result.=''; } } } @@ -2037,7 +2794,8 @@ sub end_part { !$hidden && $in_order_show) { my $gradestatus=&Apache::inputtags::gradestatus($Apache::inputtags::part, $target); - if ($Apache::lonhomework::type eq 'exam' && $target eq 'tex') { + if (($Apache::lonhomework::type eq 'exam' && $target eq 'tex') || + ($env{'form.grade_imsexport'})) { $gradestatus=''; } $result.=$gradestatus; @@ -2049,6 +2807,7 @@ sub end_part { } pop @Apache::inputtags::status; $Apache::inputtags::part=''; + $Apache::inputtags::leniency=''; $Apache::lonhomework::type = $Apache::lonhomework::default_type; return $result; } @@ -2156,7 +2915,7 @@ sub start_problemtype { ['hide','Hide']] ,$token); $result .=&Apache::edit::checked_arg('When used as type(s):','for', - [ ['exam','Exam/Quiz Problem'], + [ ['exam','Bubblesheet Exam/Quiz Problem'], ['survey','Survey'], ['surveycred','Survey (with credit)'], ['anonsurvey','Anonymous Survey'], @@ -2199,17 +2958,18 @@ sub end_startouttext { if ($target eq 'edit') { my $areaid = 'homework_edit_'.$Apache::lonxml::curdepth; $text=&Apache::lonxml::get_all_text("endouttext",$parser,$style); - $result.=&Apache::edit::start_table($token)."" + $result.=&Apache::edit::start_table($token)."" .'' - .'' + .'' .'' - .'
'; + my ($mode)=@_; + my $return = ''. + &remember_problem_state('edit').' +
+
+ '.&mt('Problem Editing').$mode.&Apache::loncommon::help_open_menu('Problem Editing','Problem_Editor_XML_Index',5,'Authoring').' +
'. + ''. + &problem_edit_buttons(); + + $return .= '
' . &Apache::lonxml::message_location(); + $return .= ' + '; + + $return .= ' + +
'; + return $return; } sub problem_edit_footer { + my $resource = $env{'request.ambiguous'}; return '

'. "\n\n".&Apache::loncommon::end_page(); @@ -565,7 +893,7 @@ sub problem_web_to_edit_header { ".&mt("Problem Type:")." ".&option('yes','problemstatus').&mt("Show Feedback")." - ".&option('no', 'problemstatus').&mt("Don't Show Incorect/Correct Feedback")." + ".&option('no', 'problemstatus').&mt("Don't Show Incorrect/Correct Feedback")." ".&option('no_feedback_ever', 'problemstatus').&mt("Don't Show Any Feedback")." @@ -623,7 +951,7 @@ $show_all
- '; @@ -650,6 +978,12 @@ $show_all 'onclick="javascript:setmode(this.form,'."'edit'".')" />'; $result .= ''; + if ($env{'browser.type'} ne 'explorer' || $env{'browser.version'} > 9) { + my $uri = $env{'request.uri'}; + my $daxeurl = '/daxepage'.$uri; + $result .= ''; + } $result.='

@@ -685,7 +1019,7 @@ sub initialize_storage { } %Apache::lonhomework::history= &Apache::lonnet::tmprestore($namespace,'',$domain,$name); - my ($temp)=keys %Apache::lonhomework::history ; + my ($temp)=keys(%Apache::lonhomework::history) ; &Apache::lonxml::debug("Return message of $temp"); } else { %Apache::lonhomework::history= @@ -693,7 +1027,7 @@ sub initialize_storage { } #ignore error conditions - my ($temp)=keys %Apache::lonhomework::history ; + my ($temp)=keys(%Apache::lonhomework::history); if ($temp =~ m/^error:.*/) { %Apache::lonhomework::history=(); } } @@ -701,12 +1035,37 @@ sub initialize_storage { =item finalize_storage() - Stores away the result has to a student's environment - checks form.grade_ for specific values, other wises stores - to the running users environment - Will increment totals for attempts, students, and corrects - if running user has student role. - + Stores away the result hash to a student's environment; + checks form.grade_ for specific values, otherwise stores + to the running user's environment. + + &check_correctness_changes() is called in two circumstances + in which the results hash is to be stored permanently, for + grading triggered by a student's submission, where feedback on + correctness is to be provided to the student. + + 1. Immediately prior to storing the results hash + + To handle the case where a student's submission (and award) were + stored after history was retrieved in &initialize_storage(), e.g., + if a student submitted answers in quick succession (e.g., from + multiple tabs). &Apache::inputtags::hidealldata() is called for + any parts with out-of-order storage (i.e., correct then incorrect, + where awarded >= 1 when correct). + + 2. Immediately after storing the results hash + + To handle the case where lond on the student's homeserver returns + delay:N -- where N is the number of transactions between the last + retrieved in &initialize_storage() and the last stored immediately + before permanent storage of the current transaction via + lond::store_handler(). &Apache::grades::makehidden() is called + for any parts with out-of-order storage (i.e., correct then incorrect, + where awarded >= 1 when correct). + + Will call &store_aggregates() to increment totals for attempts, + students, and corrects, if running user has student role. + =cut @@ -727,8 +1086,92 @@ sub finalize_storage { $namespace,'',$domain,$name); &Apache::lonxml::debug('Construct Store return message:'.$result); } else { + my ($laststore,$checkedparts,@parts,%postcorrect); + if (($env{'user.name'} eq $name) && ($env{'user.domain'} eq $domain) && + (!$Apache::lonhomework::scantronmode) && (!defined($env{'form.grade_symb'})) && + (!defined($env{'form.grade_courseid'}))) { + if ($Apache::lonhomework::history{'version'}) { + $laststore = $Apache::lonhomework::history{'version'}.'='. + $Apache::lonhomework::history{'timestamp'}; + } else { + $laststore = '0=0'; + } + my %record = &Apache::lonnet::restore($symb,$courseid,$domain,$name); + if ($record{'version'}) { + my ($newversion,$oldversion,$oldtimestamp); + if ($Apache::lonhomework::history{'version'}) { + $oldversion = $Apache::lonhomework::history{'version'}; + $oldtimestamp = $Apache::lonhomework::history{'timestamp'}; + } else { + $oldversion = 0; + $oldtimestamp = 0; + } + if ($record{'version'} > $oldversion) { + if ($record{'timestamp'} >= $oldtimestamp) { + $laststore = $record{'version'}.'='.$record{'timestamp'}; + $newversion = $record{'version'} + 1; + $checkedparts = 1; + foreach my $key (keys(%Apache::lonhomework::results)) { + if ($key =~ /^resource\.([^\.]+)\.solved$/) { + my $part = $1; + if ($Apache::lonhomework::results{$key} eq 'incorrect_attempted') { + push(@parts,$part); + } + } + } + if (@parts) { + my @parts_to_hide = &check_correctness_changes($symb,$courseid,$domain,$name, + \%record,\@parts,$newversion, + $oldversion); + if (@parts_to_hide) { + foreach my $part (@parts_to_hide) { + $postcorrect{$part} = 1; + &Apache::inputtags::hidealldata($part); + } + } + } + } + } + } + } $result=&Apache::lonnet::cstore(\%Apache::lonhomework::results, - $symb,$courseid,$domain,$name); + $symb,$courseid,$domain,$name,$laststore); + if ($result =~ /^delay\:(\d+)$/) { + my $numtrans = $1; + my ($oldversion) = split(/=/,$laststore); + if ($numtrans) { + my $newversion = $oldversion + 1 + $numtrans; + my @possparts; + if ($checkedparts) { + foreach my $part (@parts) { + unless ($postcorrect{$part}) { + push(@possparts,$part); + } + } + } else { + foreach my $key (keys(%Apache::lonhomework::results)) { + if ($key =~ /^resource\.([^\.]+)\.solved$/) { + my $part = $1; + unless ($postcorrect{$part}) { + if ($Apache::lonhomework::results{$key} eq 'incorrect_attempted') { + push(@possparts,$part); + } + } + } + } + } + if (@possparts) { + my %newrecord = &Apache::lonnet::restore($symb,$courseid,$domain,$name); + my @parts_to_hide = &check_correctness_changes($symb,$courseid,$domain,$name, + \%newrecord,\@possparts,$newversion, + $oldversion); + if (@parts_to_hide) { + my $partslist = join(',',@parts_to_hide); + &Apache::grades::makehidden($newversion,$partslist,\%newrecord,$symb,$domain,$name,1); + } + } + } + } &Apache::lonxml::debug('Store return message:'.$result); &store_aggregates($symb,$courseid); } @@ -740,6 +1183,62 @@ sub finalize_storage { =pod +=item check_correctness_changes() + + For all parts for which current results contain a solved status + of "incorrect_attempted", check if there was a transaction in which + solved was set to "correct_by_student" in the time since the last + transaction (retrieved when &initialize_storage() was called i.e., + when &start_problem() was called), unless: + (a) questiontype parameter is set to survey or anonymous survey (+/- credit) + (b) problemstatus is set to no or no_feedback_ever + If such a transaction exists, and did not occur after "reset status" + by a user with grading privileges, then the current transaction is an + example of an out-of-order transaction (i.e., incorrect occurring after + correct). Accordingly, the current transaction should be hidden. + +=cut + + +sub check_correctness_changes { + my ($symb,$courseid,$domain,$name,$record,$parts,$newversion,$oldversion) = @_; + my @parts_to_hide; + unless ((ref($record) eq 'HASH') && (ref($parts) eq 'ARRAY')) { + return @parts_to_hide; + } + if (@{$parts}) { + my $usec; + if (($env{'user.name'} eq $name) && ($env{'user.domain'} eq $domain) && + ($env{'request.course.id'} eq $courseid)) { + $usec = $env{'request.course.sec'}; + } else { + $usec = &Apache::lonnet::getsection($domain,$name,$courseid); + } + foreach my $id (@{$parts}) { + next if (($Apache::lonhomework::results{'resource.'.$id.'.type'} =~ /survey/) || + (&Apache::lonnet::EXT("resource.$id.problemstatus",$symb, + $domain,$name,$usec,undef,$courseid) =~ /^no/)); + my $reset; + for (my $i=$newversion-1; $i>=$oldversion; $i--) { + if (($record->{$i.':resource.'.$id.'.regrader'}) && + ($record->{$i.':resource.'.$id.'.tries'} eq '') && + ($record->{$i.':resource.'.$id.'.award'} eq '')) { + $reset = 1; + } elsif (($record->{$i.":resource.$id.solved"} eq 'correct_by_student') && + ($record->{$i.":resource.$id.awarded"} >= 1)) { + unless ($reset) { + push(@parts_to_hide,$id); + last; + } + } + } + } + } + return @parts_to_hide; +} + +=pod + item store_aggregates() Sends hash of values to be incremented in nohist_resourcetracker.db @@ -762,15 +1261,15 @@ sub store_aggregates { foreach my $part (@parts) { if ($env{'request.role'} =~/^st/) { if ($Apache::lonhomework::results{'resource.'.$part.'.award'} - eq 'APPROX_ANS' || - $Apache::lonhomework::results{'resource.'.$part.'.award'} - eq 'EXACT_ANS') { + eq 'APPROX_ANS' || + $Apache::lonhomework::results{'resource.'.$part.'.award'} + eq 'EXACT_ANS') { $aggregate{$symb."\0".$part."\0correct"} = 1; } if ($Apache::lonhomework::results{'resource.'.$part.'.tries'} == 1) { $aggregate{$symb."\0".$part."\0users"} = 1; } else { - my (undef,$last_reset) = &Apache::grades::get_last_resets($symb,$env{'request.course.id'},[$part]); + my (undef,$last_reset) = &Apache::grades::get_last_resets($symb,$env{'request.course.id'},[$part]); if ($last_reset) { if (&Apache::grades::get_num_tries(\%Apache::lonhomework::history,$last_reset,$part) == 0) { $aggregate{$symb."\0".$part."\0users"} = 1; @@ -779,7 +1278,7 @@ sub store_aggregates { } $aggregate{$symb."\0".$part."\0attempts"} = 1; } - if (($Apache::lonhomework::results{'resource.'.$part.'.type'} eq 'anonsurvey') || + if (($Apache::lonhomework::results{'resource.'.$part.'.type'} eq 'anonsurvey') || ($Apache::lonhomework::results{'resource.'.$part.'.type'} eq 'anonsurveycred') || ($Apache::lonhomework::results{'resource.'.$part.'.type'} eq 'randomizetry')) { if ($Apache::lonhomework::results{'resource.'.$part.'.type'} eq 'randomizetry') { @@ -787,7 +1286,7 @@ sub store_aggregates { } else { $anoncounter{$symb."\0".$part} = 1; } - my $needsrelease = $Apache::lonnet::needsrelease{'parameter:type:'.$Apache::lonhomework::results{'resource.'.$part.'.type'}}; + my $needsrelease = $Apache::lonnet::needsrelease{'parameter:type:'.$Apache::lonhomework::results{'resource.'.$part.'.type'}.':'}; if ($needsrelease) { my $curr_required = $env{'course.'.$env{'request.course.id'}.'.internal.releaserequired'}; if ($curr_required eq '') { @@ -802,7 +1301,7 @@ sub store_aggregates { } } } - if (keys (%aggregate) > 0) { + if (keys(%aggregate) > 0) { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, $cdomain,$cname); } @@ -821,8 +1320,8 @@ sub checkout_msg { 'resource'=>'The resource needs to be checked out', 'id_expln'=>'As a resource gets checked out, a unique timestamped ID is given to it, and a permanent record is left in the system.', 'warning'=>'Checking out resources is subject to course policies, and may exclude future credit even if done erroneously.', - 'checkout'=>'Check out Exam for Viewing', - 'checkout?'=>'Check out Exam?'); + 'checkout'=>'Check out Bubblesheet Exam for Viewing', + 'checkout?'=>'Check out Bubblesheet Exam?'); my $uri = &Apache::lonenc::check_encrypt($env{'request.uri'}); return (<$lt{'resource'} @@ -830,7 +1329,7 @@ sub checkout_msg {

$lt{'warning'}

- +
ENDCHECKOUT } @@ -844,19 +1343,19 @@ sub firstaccess_msg { my $foldertitle=&Apache::lonnet::gettitle($map); &Apache::lonxml::debug("map is $map title is $foldertitle"); - $result .= "

".&mt('The resources in "[_1]" are open for a limited time.' - .' Once you click the "Show Resource" button below you have [_2] to complete all resources "[_1]".' - ,$foldertitle,$time)."

"; + $result .= "

".&mt('The resources in "[_1]" are open for a limited time.',$foldertitle)."

" + .'

'.&mt('Once you click the "Show Resource" button below you have [_2] to complete all resources "[_1]".' + ,$foldertitle,$time)."

"; } elsif ($interval[1] eq 'course') { my $course = $env{'course.'.$env{'request.course.id'}.'.description'}; - $result .= "

".&mt('The resources in "[_1]" are open for a limited time.' - .' Once you click the "Show Resource" button below you have [_2] to complete all resources "[_1]".' - ,$course,$time)."

"; + $result .= "

".&mt('The resources in "[_1]" are open for a limited time.',$course)."

" + .'

'.&mt('Once you click the "Show Resource" button below you have [_2] to complete all resources "[_1]".' + ,$course,$time)."

"; } else { my $title=&Apache::lonnet::gettitle($symb); - $result .= "

".&mt('This resource "[_1]" is open for a limited time.' - .' Once you click the "Show Resource" button below you have [_2] to complete this resource "[_1]".' - ,$title,$time)."

"; + $result .= "

".&mt('This resource "[_1]" is open for a limited time.',$title)."

" + .'

'.&mt('Once you click the "Show Resource" button below you have [_2] to complete this resource "[_1]".' + ,$title,$time)."

"; } my $uri = &Apache::lonenc::check_encrypt($env{'request.uri'}); my $buttontext = &mt('Show Resource'); @@ -864,7 +1363,7 @@ sub firstaccess_msg { $result .= (< - + ENDCHECKOUT return $result; @@ -899,6 +1398,7 @@ sub init_problem_globals { @Apache::inputtags::importlist = (); @Apache::inputtags::previous=(); @Apache::inputtags::previous_version=(); + $Apache::inputtags::leniency=''; $Apache::structuretags::printanswer='No'; @Apache::structuretags::whileconds=(); @Apache::structuretags::whilebody=(); @@ -914,6 +1414,12 @@ sub reset_problem_globals { undef(%Apache::lonhomework::history); undef(%Apache::lonhomework::results); undef($Apache::inputtags::part); + undef($Apache::inputtags::leniency); + if ($type eq 'Task') { + undef($Apache::inputtags::slot_name); + } elsif ($type eq 'problem') { + undef($Apache::lonhomework::rawrndseed); + } #don't undef this, lonhomework.pm takes care of this, we use this to #detect if we try to do 2 problems in one file # undef($Apache::lonhomework::parsing_a_problem); @@ -1011,14 +1517,39 @@ sub start_problem { &Apache::run::run($expression,$safeeval); my $status; my $accessmsg; + my $resource_due; + my $ipused; my $name= &get_resource_name($parstack,$safeeval); - my ($result,$form_tag_start); + my ($result,$form_tag_start,$slot_name,$slot,$probpartlist); + + if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' || + $target eq 'tex') { + if ($env{'form.markaccess'}) { + my @interval=&Apache::lonnet::EXT("resource.0.interval"); + my ($timelimit) = split(/_/,$interval[0]); + &Apache::lonnet::set_first_access($interval[1],$timelimit); + } + + ($status,$accessmsg,$slot_name,$slot,$ipused) = + &Apache::lonhomework::check_slot_access('0','problem'); + push (@Apache::inputtags::status,$status); + } + if ($target eq 'web' || $target eq 'webgrade' || $target eq 'tex' || $target eq 'edit') { - ($result,$form_tag_start) = + ($result,$form_tag_start,$probpartlist) = &page_start($target,$token,$tagstack,$parstack,$parser,$safeeval, $name); + } elsif (($target eq 'grade') && ($Apache::lonhomework::type eq 'randomizetry')) { + my ($symb)= &Apache::lonnet::whichuser(); + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + my $res = $navmap->getBySymb($symb); + if (ref($res)) { + $probpartlist = $res->parts(); + } + } } if ($target eq 'tex' and $env{'request.symb'} =~ m/\.page_/) {$result='';} @@ -1026,28 +1557,22 @@ sub start_problem { if ($target eq 'analyze') { my $rndseed=&setup_rndseed($safeeval,$target); } if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' || $target eq 'tex') { - #handle exam checkout - if ($Apache::lonhomework::type eq 'exam') { - my $token= - $Apache::lonhomework::history{"resource.0.outtoken"}; - if (($env{'form.doescheckout'}) && (!$token)) { - $token=&Apache::lonxml::maketoken(); - $Apache::lonhomework::history{"resource.0.outtoken"}= - $token; - } - $result.=&Apache::lonxml::printtokenheader($target,$token); - } - if ($env{'form.markaccess'}) { - my @interval=&Apache::lonnet::EXT("resource.0.interval"); - &Apache::lonnet::set_first_access($interval[1]); - } - - ($status,$accessmsg,my $slot_name,my $slot) = - &Apache::lonhomework::check_slot_access('0','problem'); - push (@Apache::inputtags::status,$status); #handle rand seed in construction space - my $rndseed=&setup_rndseed($safeeval,$target); + my $rndseed=&setup_rndseed($safeeval,$target,$probpartlist); + if (($target eq 'grade') && &Apache::response::submitted()) { + if ($Apache::lonhomework::type eq 'randomizetry') { + $Apache::lonhomework::results{'resource.0.rndseed'}=$rndseed; + } else { + my @parts; + if (ref($probpartlist) eq 'ARRAY') { + @parts = @{$probpartlist}; + } + unless (@parts) { + $Apache::lonhomework::results{'resource.0.rndseed'}=$Apache::lonhomework::rawrndseed; + } + } + } my ($symb)=&Apache::lonnet::whichuser(); if ($env{'request.state'} ne "construct" && @@ -1094,18 +1619,25 @@ sub start_problem { ( $status eq 'BANNED') || ( $status eq 'UNAVAILABLE') || ( $status eq 'NOT_IN_A_SLOT') || - ( $status eq 'INVALID_ACCESS')) { + ( $status eq 'NOTRESERVABLE') || + ( $status eq 'RESERVABLE') || + ( $status eq 'RESERVABLE_LATER') || + ( $status eq 'INVALID_ACCESS') || + ( $status eq 'NEED_DIFFERENT_IP')) { my $bodytext=&Apache::lonxml::get_all_text("/problem",$parser, $style); if ( $target eq "web" ) { my $msg; if ($status eq 'UNAVAILABLE') { - $msg.='

'.&mt('Unable to determine if this resource is open due to network problems. Please try again later.').'

'; + $msg.='

'.&mt('Unable to determine if this resource is open due to network problems. Please try again later.').'

'; } elsif ($status eq 'NOT_IN_A_SLOT') { - $msg.='

'.&mt('You are not currently signed up to work at this time and/or place.').'

'; + $msg.='

'.&mt('You are not currently signed up to work at this time and/or place.').'

'; + } elsif (($status eq 'RESERVABLE') || ($status eq 'RESERVABLE_LATER') || + ($status eq 'NOTRESERVABLE')) { + $msg.='

'.&mt('Access requires reservation to work at specific time/place.').'

'; } elsif ($status ne 'NOT_YET_VIEWED') { - $msg.='

'.&mt('Not open to be viewed').'

'; - } + $msg.='

'.&mt('Not open to be viewed').'

'; + } if ($status eq 'CLOSED' || $status eq 'INVALID_ACCESS') { $msg.=&mt('The problem ').$accessmsg; } elsif ($status eq 'UNCHECKEDOUT') { @@ -1114,7 +1646,24 @@ sub start_problem { $msg.=&firstaccess_msg($accessmsg,$symb); } elsif ($status eq 'NOT_IN_A_SLOT') { $msg.=&Apache::bridgetask::add_request_another_attempt_button("Sign up for time to work"); - } + } elsif ($status eq 'RESERVABLE') { + $msg.=&mt('Available to make a reservation.').' '.&mt('Reservation window closes [_1].', + &Apache::lonnavmaps::timeToHumanString($accessmsg,'end')). + '
'. + &Apache::bridgetask::add_request_another_attempt_button("Sign up for time to work"); + } elsif ($status eq 'RESERVABLE_LATER') { + $msg.=&mt('Window to make a reservation will open [_1].', + &Apache::lonnavmaps::timeToHumanString($accessmsg,'start')); + } elsif ($status eq 'NOTRESERVABLE') { + $msg.=&mt('Not available to make a reservation.'); + } elsif ($status eq 'NEED_DIFFERENT_IP') { + if ($ipused) { + $msg.=&mt('You must use the same computer ([_1]) you used when you first accessed this resource using your time/place-based reservation.',"IP: $ipused"); + } else { + $msg.=&mt('Each student must use a different computer to access this resource at this time and/or place.').'
'. + &mt('Somebody else has already used this particular computer for that purpose.'); + } + } $result.=$msg.'
'; } elsif ($target eq 'tex') { my $startminipage = ($env{'form.problem_split'}=~/yes/i)? '' @@ -1134,22 +1683,57 @@ sub start_problem { $result .= &Apache::bridgetask::proctor_validation_screen($slot); } elsif ($target eq 'grade') { - &Apache::bridgetask::proctor_check_auth($slot_name,$slot, - 'problem'); + my $checkinresult = &Apache::bridgetask::proctor_check_auth($slot_name,$slot, + 'problem'); + if ($checkinresult = /^error:/) { + $result .= 'error'; + } } } elsif ($target eq 'web') { - if ($status eq 'CAN_ANSWER' - && $slot_name ne '' - && $Apache::lonhomework::history{'resource.0.checkedin'} eq '') { - # unproctored slot access, self checkin - &Apache::bridgetask::check_in('problem',undef,undef, - $slot_name); - } + if ($status eq 'CAN_ANSWER') { + $resource_due = &Apache::lonhomework::due_date(0, $env{'request.symb'}); + if ($slot_name ne '') { + my $checked_in = + $Apache::lonhomework::history{'resource.0.checkedin'}; + if ($checked_in eq '') { + # unproctored slot access, self checkin + my $needsiptied; + if (ref($slot)) { + $needsiptied = $slot->{'iptied'}; + } + my $check = &Apache::bridgetask::check_in('problem',undef,undef, + $slot_name,$needsiptied); + if ($check =~ /^error: /) { + &Apache::lonnet::logthis("Error during self-checkin of problem (symb: $env{'request.symb'}) using slot: $slot_name"); + } else { + $checked_in = + $Apache::lonhomework::results{"resource.0.checkedin"}; + } + } + if ((ref($slot) eq 'HASH') && ($checked_in ne '')) { + if ($slot->{'starttime'} < time()) { + if (!$resource_due) { + $resource_due = $slot->{'endtime'}; + } elsif ($slot->{'endtime'} < $resource_due) { + $resource_due = $slot->{'endtime'}; + } + } + } + } + if ($resource_due) { + my $time_left = $resource_due - time(); + if ($resource_due && ($time_left > 0) && ($target eq 'web')) { + $result .= &Apache::lonhtmlcommon::set_due_date($resource_due); + } + } + } $result.="\n $form_tag_start \t". ''; # create a page header and exit if ($env{'request.state'} eq "construct") { - $result.= &problem_web_to_edit_header($env{'form.rndseed'}); + unless ($env{'form.inhibitmenu'} eq 'yes') { + $result.= &problem_web_to_edit_header($env{'form.rndseed'}); + } if ($Apache::lonhomework::type eq 'practice') { $result.= ''. @@ -1162,16 +1746,20 @@ sub start_problem { $result .= ''."\n"; } - foreach my $field ('questiontype','rndseed') { + foreach my $field ('trial','questiontype') { if ($env{"form.grade_$field"} ne '') { $result .= ''."\n"; } } - } + if ($env{'form.grade_imsexport'}) { + $result = ''; + } } elsif ($target eq 'tex') { $result .= 'INSERTTEXFRONTMATTERHERE'; + $result .= &select_metadata_hyphenation(); + } } elsif ($target eq 'edit') { @@ -1213,7 +1801,7 @@ sub end_problem { my $id = $Apache::inputtags::part; my $weight = &Apache::lonnet::EXT("resource.$id.weight"); my $packages=&Apache::lonnet::metadata($env{'request.uri'},'packages'); - my @packages = split /,/,$packages; + my @packages = split(/,/,$packages); my $allow_print_points = 0; foreach my $partial_key (@packages) { if ($partial_key=~m/^part_0$/) { @@ -1228,6 +1816,7 @@ sub end_problem { } my $name_of_resourse= &Apache::lonxml::latex_special_symbols(&get_resource_name($parstack,$safeeval),'header'); my $begin_doc=' \typeout{STAMPOFPASSEDRESOURCESTART Resource

"'.$name_of_resourse.'"

located in
'.$env{'request.uri'}.'
STAMPOFPASSEDRESOURCEEND} \noindent '; + &clear_required_languages(); my $toc_line='\vskip 1 mm\noindent '.$startminipage. '\addcontentsline{toc}{subsection}{'.$name_of_resourse.'}'; @@ -1274,7 +1863,7 @@ sub end_problem { } else { $frontmatter.= $begin_doc.$toc_line; if ($Apache::lonhomework::type eq 'exam' and $allow_print_points==1) { - $frontmatter .= '\fbox{\textit{'.$weight.' pt}}'; + $frontmatter .= '\fbox{\textit{'.&mt('[quant,_1,pt,pt]',$weight ).'}}'; } } } else { @@ -1284,7 +1873,7 @@ sub end_problem { if (not $env{'request.symb'} =~ m/\.page_/) { $frontmatter .= $begin_doc.$toc_line; if (($Apache::lonhomework::type eq 'exam') and ($allow_print_points==1)) { - $frontmatter .= '\fbox{\textit{'.$weight.' pt}}'; + $frontmatter .= '\fbox{\textit{'.&mt('[quant,_1,pt,pt]',$weight ).'}}'; } } else { $frontmatter .= '\vskip 1mm \\\\\\\\'.$startminipage; @@ -1308,7 +1897,8 @@ sub end_problem { } } elsif ( ($target eq 'web' || $target eq 'tex') && $Apache::inputtags::part eq '0' && - $status ne 'UNCHECKEDOUT' && $status ne 'NOT_YET_VIEWED') { + $status ne 'UNCHECKEDOUT' && $status ne 'NOT_YET_VIEWED' + && !$env{'form.grade_imsexport'}) { # if part is zero, no s existed, so we need show the current # grading status my $gradestatus = &Apache::inputtags::gradestatus($Apache::inputtags::part,$target); @@ -1318,12 +1908,19 @@ sub end_problem { (($target eq 'web') && ($env{'request.state'} ne 'construct')) || ($target eq 'answer') || ($target eq 'tex') ) { - if ($target ne 'tex' && - $env{'form.answer_output_mode'} ne 'tex') { + if (($target ne 'tex') && + ($env{'form.answer_output_mode'} ne 'tex') && + (!$env{'form.grade_imsexport'})) { $result.=""; } if ($target eq 'web') { - $result.= &Apache::loncommon::end_page({'discussion' => 1}); + # + # Closing not added by end_page(). + # Added separately at end of this routine, after added + # so document will be valid xhtml. + # + $result.= &Apache::loncommon::end_page({'discussion' => 1, + 'notbody' => 1}); } elsif ($target eq 'tex') { my $endminipage = ''; if (not $env{'form.problem_split'}=~/yes/) { @@ -1374,6 +1971,19 @@ sub end_problem { &reset_problem_globals('problem'); + # + # This shouild be just above the return so that the + # time put in the javascript is as late as possible in the + # computation: + # + if ($target eq 'web') { + $result .= &Apache::lonhtmlcommon::set_compute_end_time(); + # + # Closing tags delayed so any tags + # not in head can appear inside body, for valid xhtml. + # + $result .= "\n"; + } return $result; } @@ -1500,54 +2110,101 @@ sub end_block { } return $result; } - +# +# +# ... +# +# +# This declares the intent to provide content that can be rendered in the +# set of languages in the include specificatino but not in the exclude +# specification. If a currently preferred language is in the include list +# the content in the ... is rendered +# If the currently preferred language is in the exclude list, +# the content in the ..>[2]->{'include'}; my $exclude = $token->[2]->{'exclude'}; my @preferred_languages=&Apache::lonlocal::preferred_languages(); -# This should not even happen, since we should at least have the server language - if (!$preferred_languages[0]) { $preferred_languages[0]='en'; } -# Now loop over all languages in order of preference + + # This should not even happen, since we should at least have the server language + + if (!$preferred_languages[0]) { + $preferred_languages[0]='en'; + } + + # Now loop over all languages in order of preference + + my $render; foreach my $preferred_language (@preferred_languages) { -# If the languageblock has no arguments, show the contents - $result=1; + + # If neither include/nor exlude is present the block is going + # to get rendered. + my $found=0; -# Do we have an include argument? + $render=1; + + # If include is specified, don't render the block + # unless the preferred language is included in the set. + if ($include) { -# If include is specified, by default, don't render the block - $result=0; + $render=0; foreach my $included_language (split(/\,/,$include)) { -# ... but if my preferred language is included, render it if ($included_language eq $preferred_language) { - $result=1; + $render=1; $found=1; + last; # Only need to find the first. } } } -# Do we have an exclude argument? + # Do we have an exclude argument? + # If so, and one of the languages matches a preferred language + # inhibit rendering the block. Note that in the pathalogical case the + # author has specified a preferred language in both the include and exclude + # attribte exclude is preferred. + if ($exclude) { - $result=1; + $render=1; foreach my $excluded_language (split(/\,/,$exclude)) { if ($excluded_language eq $preferred_language) { - $result=0; + $render=0; $found=1; + last; # Only need to find the first. } } } - if ($found) { last; } + if ($found) { + last; # Done on any match of include or exclude. + } } - if ( ! $result ) { + # If $render not true skip the entire block until + # + + if ( ! $render ) { my $skip=&Apache::lonxml::get_all_text("/languageblock",$parser, $style); &Apache::lonxml::debug("skipping ahead :$skip: $$parser[-1]"); } - $result=''; + # If $render is true, we've not skipped the contents of the + # and the normal loncapa processing flow will render it as a matter of course. + } elsif ($target eq 'edit') { $result .=&Apache::edit::tag_start($target,$token); $result .=&Apache::edit::text_arg(&mt('Include Language:'),'include', @@ -1572,9 +2229,47 @@ sub end_languageblock { } return $result; } - +# languagblock specific tags: { - my %available_texts; + # For chunks of a resource that has translations, this hash contains + # the translations available indexed by language name. + # + + my %available_texts; + + # starts a block of a resource that has multiple translations. + # See the tag as well. + # When is encountered if there is a translation for the + # currently preferred language, that is rendered inthe web/tex/webgrade + # targets. Otherwise, the default text is rendered. + # + # Note that is only registered for the duration of the + # ... block + # + # Pathalogical case handling: + # - If there is no that specifies a 'default' and there is no + # translation that matches a preferred language, nothing is rendered. + # - Nested ... might be linguistically supported by + # XML due to the stack nature of tag registration(?) however the rendered + # output will be incorrect because there is only one %available_texts + # has and end_translated clears it. + # - Material outside of a ... block within the + # ... block won't render either e.g.: + # + # The following will be in your preferred langauge: + # + # This section in english + # + # + # Hier es ist auf Deutsch. + # + # + # En Francais + # + # + # + # The introductory text prior to the first tag is not rendered. + # sub start_translated { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; &Apache::lonxml::register('Apache::structuretags',('lang')); @@ -1593,14 +2288,55 @@ sub end_languageblock { my @possibilities = keys(%available_texts); my $which = &Apache::loncommon::languages(\@possibilities) || 'default'; - $result = $available_texts{$which}; + if ($target eq 'tex') { + $result = &select_hyphenation($which); + } + $result .= $available_texts{$which}; + if ($target eq 'tex') { + $result .= &select_metadata_hyphenation(); # Restore original language. + } } undef(%available_texts); &Apache::lonxml::deregister('Apache::structuretags',('lang')); return $result; } - + # + # Specifies that the block contained within it is a translation + # for a specific language specified by the 'which' attribute. The + # 'other' attribute can be used by itself or in conjunction with + # which to specify this tag _may_ be used as a translation for some + # list of languages. e.g.: + # specifying that the block provides a translation for US (primary) + # Canadian, Australian and UK Englush. + # + # Comment: this seems a bit silly why not just support a list of languages + # e.g. and ditch the other attribute? + # + # Effect: + # The material within the .. block is stored in the + # specified set of $available_texts hash entries, the appropriate one + # is selected at time. + # + # Pathalogical case handling: + # If a language occurs multiple times within a block, + # only the last one is rendered e.g.: + # + # + # + # Red green color blindness is quite common affecting about 7.8% of + # the male population, but onloy about .65% of the female population. + # + # Red green colour blindness is quite common affecting about 7.8% of + # the male population, but onloy about .65% of the female population. + # + # + # + # renders the correct spelling of color (colour) for people who have specified + # a preferred language that is one of the British Commonwealth languages + # even though those are also listed as valid selections for the US english + # block. + # sub start_lang { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' || @@ -1631,7 +2367,8 @@ sub end_languageblock { } return ''; } -} +} # end langauge block specific tags. + sub start_instructorcomment { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; @@ -1723,9 +2460,15 @@ sub end_while { } $return = &Apache::run::run($code,$safeeval); } - if ($error) { - &Apache::lonxml::error('
'.&mt('Code ran too long. It ran for more than').' '.$Apache::lonnet::perlvar{'lonScriptTimeout'}.' '.&mt('seconds occurred while running <while> on line').' '.$line.'
'); - } + if ($error) { + &Apache::lonxml::error( + '
'
+               .&mt('Code ran too long. It ran for more than [_1] seconds.',
+                        $Apache::lonnet::perlvar{'lonScriptTimeout'})
+               .&mt('This occurred while running <while> on line [_1].',
+                        $line)
+               .'
'); + } } elsif ($target eq "edit") { $result.= &Apache::edit::tag_end($target,$token,''); } @@ -1792,8 +2535,8 @@ sub start_randomlist { } } } - for(0 .. $show) { - $bodytext .= "$randomlist[ $idx_arr[$_] ]"; + for my $i (0 .. $show) { + $bodytext .= "$randomlist[ $idx_arr[$i] ]"; } &Apache::lonxml::newparser($parser,\$bodytext); } @@ -1845,6 +2588,7 @@ sub ordered_show_check { return $in_order_show; } + sub start_startpartmarker { my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_; my $result=''; @@ -1853,7 +2597,7 @@ sub start_startpartmarker { $result.=&mt('Marker for the start of a part. Place end marker below to wrap in-between tags into a new part.').'
".&mt('Text Block')."
".&Apache::loncommon::insert_folding_button() + ." ".&mt('Text Block')."'.&mt('Delete?').' ' .&Apache::edit::deletelist($target,$token) .'' + .'' .&Apache::lonhtmlcommon::dragmath_button($areaid,1) - .'' .&Apache::edit::insertlist($target,$token) .'' . + .'' . &Apache::loncommon::helpLatexCheatsheet(). &Apache::edit::end_row(). &Apache::edit::start_spanning_row()."\n". @@ -2278,11 +3038,12 @@ sub start_simpleeditbutton { # .&mt('Note: it can take up to 10 minutes for changes to take effect for all users.') # .&Apache::loncommon::help_open_topic('Caching') # .'

'; - $result.=&Apache::lonhtmlcommon::start_funclist() + $result.=&Apache::loncommon::head_subbox( + &Apache::lonhtmlcommon::start_funclist() .&Apache::lonhtmlcommon::add_item_funclist( '' .&mt('Edit').'') - .&Apache::lonhtmlcommon::end_funclist(); + .&Apache::lonhtmlcommon::end_funclist()); } return $result;