--- loncom/homework/structuretags.pm 2014/11/24 02:36:26 1.524 +++ loncom/homework/structuretags.pm 2016/09/14 02:42:43 1.550 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # definition of tags that give a structure to a document # -# $Id: structuretags.pm,v 1.524 2014/11/24 02:36:26 raeburn Exp $ +# $Id: structuretags.pm,v 1.550 2016/09/14 02:42:43 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -224,9 +224,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"; '; } } @@ -419,6 +517,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(); + } } } @@ -428,23 +529,25 @@ sub page_start { } elsif (!defined($found{'body'}) && $env{'request.state'} eq 'construct') { if ($target eq 'web' || $target eq 'edit') { - # 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 + 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, @@ -511,7 +614,7 @@ sub page_start { "\t".''."\n"; } } - return ($page_start,$form_tag_start); + return ($page_start,$form_tag_start,$partlist); } #use Time::HiRes(); @@ -542,7 +645,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') { @@ -603,7 +706,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 ++; } @@ -611,7 +720,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; @@ -621,6 +730,9 @@ sub setup_rndseed { } } $set_safespace = 1; + if ($target eq 'grade') { + $Apache::lonhomework::rawrndseed = $rndseed; + } } if ($set_safespace) { if ($safeeval) { @@ -650,53 +762,81 @@ sub problem_edit_action_button { sub problem_edit_buttons { 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 - my $result='
'. + $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); + if ($env{'environment.nocodemirror'}) { + $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
"; -# Buttons that save - $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"; return $result; } sub problem_edit_header { - return ''. + my ($mode)=@_; + my $return = ''. &remember_problem_state('edit').' -
-
-'.&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::start_table($token)."" .'' - .'' + .'' .''
'; +
+
+ '.&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(); @@ -846,6 +986,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.='
@@ -881,7 +1027,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= @@ -889,7 +1035,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=(); } } @@ -1148,8 +1294,8 @@ sub store_aggregates { } else { $anoncounter{$symb."\0".$part} = 1; } - my $needsrelease = $Apache::lonnet::needsrelease{'parameter:type:'.$Apache::lonhomework::results{'resource.'.$part.'.type'}}; - if ($needsrelease) { + 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 '') { &Apache::lonnet::update_released_required($needsrelease); @@ -1163,7 +1309,7 @@ sub store_aggregates { } } } - if (keys (%aggregate) > 0) { + if (keys(%aggregate) > 0) { &Apache::lonnet::cinc('nohist_resourcetracker',\%aggregate, $cdomain,$cname); } @@ -1260,6 +1406,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=(); @@ -1275,8 +1422,11 @@ 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 @@ -1376,27 +1526,38 @@ sub start_problem { my $status; my $accessmsg; my $resource_due; + my $ipused; my $name= &get_resource_name($parstack,$safeeval); - my ($result,$form_tag_start,$slot_name,$slot); + 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"); - &Apache::lonnet::set_first_access($interval[1],$interval[0]); + my ($timelimit) = split(/_/,$interval[0]); + &Apache::lonnet::set_first_access($interval[1],$timelimit); } - ($status,$accessmsg,$slot_name,$slot) = + ($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='';} @@ -1406,7 +1567,20 @@ sub start_problem { $target eq 'tex') { #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" && @@ -1436,7 +1610,10 @@ sub start_problem { $form_tag_start.='
'; } elsif (($env{'request.state'} ne "construct") && ($Apache::lonhomework::type eq 'randomizetry') && - ($status eq 'CAN_ANSWER')) { + ($status eq 'CAN_ANSWER') && + ($env{'course.'.$env{'request.course.id'}.'.type'} ne 'Placement') && + (!$env{'request.role.adv'})) { +# "New Problem Variation Each Try" header suppressed for Placement Tests, unless course personnel. my $reqtries = &Apache::lonnet::EXT("resource.$Apache::inputtags::part.randomizeontries"); my $problemstatus = &get_problem_status($Apache::inputtags::part); $form_tag_start.=&randomizetry_problem_header($problemstatus,$reqtries); @@ -1456,7 +1633,8 @@ sub start_problem { ( $status eq 'NOTRESERVABLE') || ( $status eq 'RESERVABLE') || ( $status eq 'RESERVABLE_LATER') || - ( $status eq 'INVALID_ACCESS')) { + ( $status eq 'INVALID_ACCESS') || + ( $status eq 'NEED_DIFFERENT_IP')) { my $bodytext=&Apache::lonxml::get_all_text("/problem",$parser, $style); if ( $target eq "web" ) { @@ -1489,7 +1667,14 @@ sub start_problem { &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)? '' @@ -1509,8 +1694,11 @@ 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') { @@ -1520,10 +1708,18 @@ sub start_problem { $Apache::lonhomework::history{'resource.0.checkedin'}; if ($checked_in eq '') { # unproctored slot access, self checkin - &Apache::bridgetask::check_in('problem',undef,undef, - $slot_name); - $checked_in = - $Apache::lonhomework::results{"resource.0.checkedin"}; + 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()) { @@ -1546,7 +1742,12 @@ sub start_problem { ''; # create a page header and exit if ($env{'request.state'} eq "construct") { - $result.= &problem_web_to_edit_header($env{'form.rndseed'}); + if ($env{'form.inhibitmenu'} eq 'yes') { + # error messages can be useful in any case + $result.= &Apache::lonxml::message_location(); + } else { + $result.= &problem_web_to_edit_header($env{'form.rndseed'}); + } if ($Apache::lonhomework::type eq 'practice') { $result.= ''. @@ -1614,7 +1815,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$/) { @@ -1732,7 +1933,39 @@ sub end_problem { # Added separately at end of this routine, after added # so document will be valid xhtml. # - $result.= &Apache::loncommon::end_page({'discussion' => 1, + my $showdisc = 1; + if (($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement') && + (!$env{'request.role.adv'})) { +# For Placement Tests footer with "Post Discussion" and "Send Feedback" links is suppressed. + $showdisc = 0; + my ($symb)= &Apache::lonnet::whichuser(); + if ($symb) { + my $navmap = Apache::lonnavmaps::navmap->new(); + if (ref($navmap)) { + my $hastries = &Apache::lonplacementtest::has_tries($symb,$navmap); +# For Placement Tests test status is displayed if this is the last resource in the course +# and there are no tries left + unless ($hastries) { + if (&Apache::lonplacementtest::is_lastres($symb,$navmap)) { + my ($score,$incomplete) = + &Apache::lonplacementtest::check_completion(undef,undef,1); + if (!$incomplete) { + $result .= &Apache::lonplacementtest::showresult(1,1); + } elsif ($incomplete < 100) { + $result.= &Apache::lonplacementtest::showincomplete($incomplete,1); + } + } else { +# For Placement Tests score is displayed if test has just been completed + my ($score,$incomplete) = &Apache::lonplacementtest::check_completion(undef,undef,1); + if (!$incomplete) { + $result.= &Apache::lonplacementtest::showresult(1,1); + } + } + } + } + } + } + $result.= &Apache::loncommon::end_page({'discussion' => $showdisc, 'notbody' => 1}); } elsif ($target eq 'tex') { my $endminipage = ''; @@ -2348,8 +2581,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); } @@ -2454,6 +2687,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=(); @@ -2463,6 +2697,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.'\';'; @@ -2513,7 +2755,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_(.*)$/) { @@ -2611,6 +2853,7 @@ sub end_part { } pop @Apache::inputtags::status; $Apache::inputtags::part=''; + $Apache::inputtags::leniency=''; $Apache::lonhomework::type = $Apache::lonhomework::default_type; return $result; } @@ -2761,13 +3004,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)."
".&mt('Text Block')."
".&Apache::loncommon::insert_folding_button() + ." ".&mt('Text Block')."'.&mt('Delete?').' ' .&Apache::edit::deletelist($target,$token) .'' - .&Apache::lonhtmlcommon::dragmath_button($areaid,1) - .''; + if ($env{'environment.nocodemirror'}) { + $result.=&Apache::lonhtmlcommon::dragmath_button($areaid,1); + } else { + $result.=' '; + } + $result.='' .&Apache::edit::insertlist($target,$token) .'