--- loncom/homework/lonhomework.pm 2016/08/06 23:23:18 1.344.2.3 +++ loncom/homework/lonhomework.pm 2015/06/29 15:42:07 1.355 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Homework handler # -# $Id: lonhomework.pm,v 1.344.2.3 2016/08/06 23:23:18 raeburn Exp $ +# $Id: lonhomework.pm,v 1.355 2015/06/29 15:42:07 damieng Exp $ # # Copyright Michigan State University Board of Trustees # @@ -49,8 +49,10 @@ use Apache::matchresponse(); use Apache::chemresponse(); use Apache::functionplotresponse(); use Apache::drawimage(); +use Apache::loncapamath(); use Apache::Constants qw(:common); use Apache::loncommon(); +use Apache::lonparmset(); use Apache::lonlocal; use Time::HiRes qw( gettimeofday tv_interval ); use HTML::Entities(); @@ -208,7 +210,7 @@ sub proctor_checked_in { } sub check_slot_access { - my ($id,$type,$partlist)=@_; + my ($id,$type)=@_; # does it pass normal muster my ($status,$datemsg)=&check_access($id); @@ -271,7 +273,7 @@ sub check_slot_access { $slotstatus=$status; } - my ($is_correct,$got_grade,$checkinslot,$checkedin,$consumed_uniq); + my ($is_correct,$got_grade,$checkedin); if ($type eq 'Task') { my $version=$Apache::lonhomework::history{'resource.0.version'}; $got_grade = @@ -283,56 +285,10 @@ sub check_slot_access { $checkedin = $Apache::lonhomework::history{"resource.$version.0.checkedin"}; } elsif ($type eq 'problem') { + $got_grade = 1; $checkedin = $Apache::lonhomework::history{"resource.0.checkedin"}; - } - if ($checkedin) { - $checkinslot = $Apache::lonhomework::history{"$checkin.slot"}; - my %slot=&Apache::lonnet::get_slot($checkinslot); - $consumed_uniq = $slot{'uniqueperiod'}; - } - if ($type eq 'problem') { - if ((ref($partlist) eq 'ARRAY') && (@{$partlist} > 0)) { - my ($numcorrect,$numgraded) = (0,0); - foreach my $part (@{$partlist}) { - my $currtries = $Apache::lonhomework::history{"resource.$part.tries"}; - my $maxtries = &Apache::lonnet::EXT("resource.$part.maxtries",$symb); - my $probstatus = &Apache::structuretags::get_problem_status($part); - my $earlyout; - unless (($probstatus eq 'no') || - ($probstatus eq 'no_feedback_ever')) { - if ($Apache::lonhomework::history{"resource.$part.solved"} =~/^correct_/) { - $numcorrect ++; - } else { - $earlyout = 1; - } - } - if (($currtries == $maxtries) || ($is_correct)) { - $earlyout = 1; - } else { - $numgraded ++; - } - last if ($earlyout); - } - my $numparts = scalar(@{$partlist}); - if ($numparts == $numcorrect) { - $is_correct = 1; - } - if ($numparts == $numgraded) { - $got_grade = 1; - } - } else { - my $currtries = $Apache::lonhomework::history{"resource.0.tries"}; - my $maxtries = &Apache::lonnet::EXT("resource.0.maxtries",$symb); - my $probstatus = &Apache::structuretags::get_problem_status('0'); - unless (($probstatus eq 'no') || - ($probstatus eq 'no_feedback_ever')) { - $is_correct = - ($Apache::lonhomework::history{"resource.0.solved"} =~/^correct_/); - } - unless (($currtries == $maxtries) || ($is_correct)) { - $got_grade = 1; - } - } + $is_correct = + ($Apache::lonhomework::history{"resource.0.solved"} =~/^correct_/); } &Apache::lonxml::debug(" slot is $slotstatus checkedin ($checkedin) got_grade ($got_grade) is_correct ($is_correct)"); @@ -363,57 +319,15 @@ sub check_slot_access { &Apache::loncommon::get_future_slots($cnum,$cdom,$now,$symb); if ((ref($reservable_now_order) eq 'ARRAY') && (ref($reservable_now) eq 'HASH')) { if (@{$reservable_now_order} > 0) { - if ((!$checkedin) || (ref($consumed_uniq) ne 'ARRAY')) { - $slotstatus = 'RESERVABLE'; - $datemsg = $reservable_now->{$reservable_now_order->[-1]}{'endreserve'}; - } else { - my ($uniqstart,$uniqend,$useslot); - if (ref($consumed_uniq) eq 'ARRAY') { - ($uniqstart,$uniqend)=@{$consumed_uniq}; - } - foreach my $slot (reverse(@{$reservable_now_order})) { - if ($reservable_now->{$slot}{'uniqueperiod'} =~ /^(\d+)\,(\d+)$/) { - my ($new_uniq_start,$new_uniq_end) = ($1,$2); - next if (! - ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) || - ($uniqstart > $new_uniq_end && $uniqend > $new_uniq_end )); - } - $useslot = $slot; - last; - } - if ($useslot) { - $slotstatus = 'RESERVABLE'; - $datemsg = $reservable_now->{$useslot}{'endreserve'}; - } - } + $slotstatus = 'RESERVABLE'; + $datemsg = $reservable_now->{$reservable_now_order->[-1]}{'endreserve'}; } } unless ($slotstatus eq 'RESERVABLE') { if ((ref($reservable_future_order) eq 'ARRAY') && (ref($reservable_future) eq 'HASH')) { if (@{$reservable_future_order} > 0) { - if ((!$checkedin) || (ref($consumed_uniq) ne 'ARRAY')) { - $slotstatus = 'RESERVABLE_LATER'; - $datemsg = $reservable_future->{$reservable_future_order->[0]}{'startreserve'}; - } else { - my ($uniqstart,$uniqend,$useslot); - if (ref($consumed_uniq) eq 'ARRAY') { - ($uniqstart,$uniqend)=@{$consumed_uniq}; - } - foreach my $slot (@{$reservable_future_order}) { - if ($reservable_future->{$slot}{'uniqueperiod'} =~ /^(\d+),(\d+)$/) { - my ($new_uniq_start,$new_uniq_end) = ($1,$2); - next if (! - ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) || - ($uniqstart > $new_uniq_end && $uniqend > $new_uniq_end )); - } - $useslot = $slot; - last; - } - if ($useslot) { - $slotstatus = 'RESERVABLE_LATER'; - $datemsg = $reservable_future->{$useslot}{'startreserve'}; - } - } + $slotstatus = 'RESERVABLE_LATER'; + $datemsg = $reservable_future->{$reservable_future_order->[0]}{'startreserve'}; } } } @@ -855,8 +769,7 @@ sub analyze_header { .&Apache::loncommon::head_subbox( &Apache::loncommon::CSTR_pageheader()); $result .= - &Apache::lonxml::message_location().' -
'. ''. @@ -870,8 +783,9 @@ sub analyze_header {
- -
'; + ' + .&Apache::lonxml::message_location(). + ''; &Apache::lonxml::add_messages(\$result); $request->print($result); $request->rflush(); @@ -1050,9 +964,8 @@ sub editxmlmode { $problem=''; } - if (($env{'form.problemmode'} eq 'saveeditxml') || - ($env{'form.problemmode'} eq 'saveviewxml') || + ($env{'form.problemmode'} eq 'saveviewxml') || ($env{'form.problemmode'} eq 'undoxml')) { my $error=&handle_save_or_undo($request,\$problem, \$env{'form.editxmltext'}); @@ -1098,27 +1011,88 @@ sub editxmlmode { '
'. &Apache::structuretags::remember_problem_state().' -
- -
-

'.&mt('Problem Editing').' '.&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index').'

-
- '.&Apache::loncommon::helpLatexCheatsheet('Problem_LON-CAPA_Functions','Script Functions').' -
'; +
+
'. + &mt('Problem Editing').' '.&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index'). + '
'; - $result.=''. + $result.=''. &Apache::structuretags::problem_edit_buttons('editxml'); + $result.='
'; + + $result .= '
    '; + + unless ($env{'environment.nocodemirror'}) { + # dropdown menus + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Problem Templates"), template_dropdown_datastructure()); + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Response Types"), responseblock_dropdown_datastructure()); + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Conditional Blocks"), conditional_scripting_datastructure()); + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Miscellaneous"), misc_datastructure()); + } + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Help") . ' ' . &mt(', + helpmenu_datastructure(),""); + + $result.="
"; - $result.='
'.&Apache::lonxml::message_location().'
'. - ' -
-
- '.&Apache::loncommon::end_page(); - &Apache::lonxml::add_messages(\$result); - $request->print($result); + $result .= '
' . + &Apache::lonxml::message_location() . + &Apache::loncommon::xmleditor_js() . + '
'; + + my $resource = $env{'request.ambiguous'}; + unless($env{'environment.nocodemirror'}){ + $result .= ' + + '; + } + + $result .= &Apache::loncommon::end_page(); + &Apache::lonxml::add_messages(\$result); + $request->print($result); } return ''; } @@ -1153,8 +1127,10 @@ sub renderpage { $problem=''; my $filename=(split('/',$file))[-1]; my $error = - &mt('Unable to find [_1]', - ''.$filename.''); + '

' + .&mt('Unable to find [_1]', + ''.$filename.'') + ."

"; $result.= &Apache::loncommon::simple_error_page($request,'Not available', $error,{'no_auto_mt_msg' => 1}); @@ -1205,7 +1181,25 @@ sub finished_parsing { undef($Apache::lonhomework::parsing_a_task); } -sub get_template_list { +# function extracted from get_template_html +# returns "key" -> list +# key: path of template +# value 1: title +# value 2: category +# value 3: name of help topic ??? +sub get_template_list{ + my ($extension) = @_; + + my @files = glob($Apache::lonnet::perlvar{'lonIncludes'}. + '/templates/*.'.$extension); + @files = map {[$_,&mt(&Apache::lonnet::metadata($_, 'title')), + (&Apache::lonnet::metadata($_, 'category')?&mt(&Apache::lonnet::metadata($_, 'category')):&mt('Miscellaneous')), + &mt(&Apache::lonnet::metadata($_, 'help'))]} (@files); + @files = sort {$a->[2].$a->[1] cmp $b->[2].$b->[1]} (@files); + return @files; +} + +sub get_template_html { my ($extension) = @_; my $result; my @allnames; @@ -1214,12 +1208,7 @@ sub get_template_list { if ($extension eq 'survey' || $extension eq 'exam') { $glob_extension = 'problem'; } - my @files = glob($Apache::lonnet::perlvar{'lonIncludes'}. - '/templates/*.'.$glob_extension); - @files = map {[$_,&mt(&Apache::lonnet::metadata($_, 'title')), - (&Apache::lonnet::metadata($_, 'category')?&mt(&Apache::lonnet::metadata($_, 'category')):&mt('Miscellaneous')), - &Apache::lonnet::metadata($_, 'help')]} (@files); - @files = sort {$a->[2].$a->[1] cmp $b->[2].$b->[1]} (@files); + my @files = &get_template_list($extension); my ($midpoint,$seconddiv,$numfiles); my @noexamplelink = ('blank.problem','blank.library','script.library'); $numfiles = 0; @@ -1281,6 +1270,12 @@ sub get_template_list { sub newproblem { my ($request) = @_; + if ($env{'form.mode'} eq 'blank'){ + my $dest = &Apache::lonnet::filelocation("",$request->uri); + &File::Copy::copy('/home/httpd/html/res/adm/includes/templates/blank.problem',$dest); + &renderpage($request,$dest); + return; + } if ($env{'form.template'}) { my $file = $env{'form.template'}; my $dest = &Apache::lonnet::filelocation("",$request->uri); @@ -1291,7 +1286,7 @@ sub newproblem { my ($extension) = ($request->uri =~ m/\.(\w+)$/); &Apache::lonxml::debug("Looking for :$extension:"); - my $templatelist=&get_template_list($extension); + my $templatelist=&get_template_html($extension); if ($env{'form.newfile'} && !$templatelist) { # no templates found my $templatefilename = @@ -1353,10 +1348,46 @@ sub update_construct_style { } } +# +# Sets interval for current user so time left will be zero, either for the entire folder +# containing the current resource, or just the resource, depending on value of first item +# in interval array retrieved from EXT("resource.0.interval"); +# +sub zero_timer { + my ($symb) = @_; + my ($hastimeleft,$first_access,$now); + my @interval=&Apache::lonnet::EXT("resource.0.interval"); + if (@interval > 1) { + if ($interval[1] eq 'course') { + return; + } else { + my $now = time; + my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb); + if ($first_access > 0) { + if ($first_access+$interval[0] > $now) { + my $done_time = $now - $first_access; + my $snum = 1; + if ($interval[1] eq 'map') { + $snum = 2; + } + my $result = + &Apache::lonparmset::storeparm_by_symb_inner($symb,'0_interval', + $snum,$done_time, + 'date_interval', + $env{'user.name'}, + $env{'user.domain'}); + return $result; + } + } + } + } + return; +} sub handler { #my $t0 = [&gettimeofday()]; my $request=$_[0]; + $Apache::lonxml::request=$request; $Apache::lonxml::debug=$env{'user.debug'}; $env{'request.uri'}=$request->uri; @@ -1365,7 +1396,7 @@ sub handler { my $file=&Apache::lonnet::filelocation("",$request->uri); #check if we know where we are - if ($env{'request.course.fn'} && !&Apache::lonnet::symbread('','',1,1)) { + if ($env{'request.course.fn'} && !&Apache::lonnet::symbread('','',1,1)) { # if we are browsing we might not be able to know where we are if ($Apache::lonhomework::browse ne 'F' && $env{'request.state'} ne "construct") { @@ -1379,6 +1410,7 @@ sub handler { &unset_permissions(); return OK; } + &Apache::lonxml::debug("Permissions:$Apache::lonhomework::browse:$Apache::lonhomework::viewgrades:$Apache::lonhomework::modifygrades:$Apache::lonhomework::queuegrade"); &Apache::lonxml::debug("Problem Mode ".$env{'form.problemmode'}); my ($symb) = &Apache::lonnet::whichuser(); @@ -1403,10 +1435,18 @@ sub handler { &renderpage($request,$file); } } else { + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, + ['mode']); # requested file doesn't exist in contruction space &newproblem($request); } } else { + # Set the event timer to zero if the "done button" was clicked. The button is + # part of the doneButton form created in lonmenu.pm + if ($symb && $env{'form.LC_interval_done'} eq 'true') { + &zero_timer($symb); + undef($env{'form.LC_interval_done'}); + } # just render the page normally outside of construction space &Apache::lonxml::debug("not construct"); &renderpage($request,$file); @@ -1420,5 +1460,175 @@ sub handler { } +sub template_dropdown_datastructure { + # gathering the all templates and their path, title, category and help topic + my @templates = get_template_list('problem'); + # template category => title + my %tmplthash = (); + # template title => path + my %tmpltcontent = (); + + foreach my $template (@templates){ + # put in hash if the template is not empty + unless ($template->[1] eq ''){ + push(@{$tmplthash{$template->[2]}}, $template->[1]); + push(@{$tmpltcontent{$template->[1]}},$template->[0]); + } + } + + my $catList = []; + foreach my $cat (sort keys %tmplthash) { + my $catItems = []; + foreach my $title (sort @{$tmplthash{$cat}}) { + my $path = $tmpltcontent{$title}->[0]; + my $code; + open(FH, "<$path"); + while(){ + $code.= $_ unless $_ =~ /()|(<\/problem>)/; + } + close(FH); + + if ($code ne '') { + my $href = 'javascript:insertText(\'' . &convert_for_js(&HTML::Entities::encode($code,'<>&"')) . '\')'; + my $currItem = [$href, $title, undef]; + push @{$catItems}, $currItem; + } + } + push @{$catList}, [$catItems, $cat, undef]; + } + + return $catList; +} + +sub responseblock_dropdown_datastructure { + + my $mathCat = [ + [ + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_formularesponse())) . "\')", &mt("Formula Response"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_functionplotresponse())) . "\')", &mt("Function Plot Response"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_mathresponse())) . "\')", &mt("Math Response"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_numericalresponse())) . "\')", &mt("Numerical Response"), undef] + ], + &mt("Math"), + undef + ]; + + my $miscCat = [ + [ + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_imageresponse())) . "\')", &mt("Click on Image"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_customresponse())) . "\')", &mt("Custom Response"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_externalresponse())) . "\')", &mt("External Response"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_matchresponse())) . "\')", &mt("Match Two Lists"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_radiobuttonresponse())) . "\')", &mt("One out of N statements"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_optionresponse())) . "\')", &mt("Select from Options"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_rankresponse())) . "\')", &mt("Rank Values"), undef] + ], + &mt("Miscellaneous"), + undef + ]; + + my $chemCat = [ + [ + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_reactionresponse())) . "\')", &mt("Chemical Reaction"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_organicresponse())) . "\')", &mt("Organic Chemical Structure"), undef] + ], + &mt("Chemistry"), + undef + ]; + + my $textCat = [ + [ + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_stringresponse())) . "\')", &mt("String Response"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_essayresponse())) . "\')", &mt("Essay"), undef] + ], + &mt("Text"), + undef + ]; + + return [$mathCat, $miscCat, $chemCat, $textCat]; +} + + +sub conditional_scripting_datastructure { +# TODO: corresponding routines should be used for the javascript:insertText parts +# instead of the placeholder routine default_xml_tag with the tags +# e.g. &default_xml_tag("postanswerdate") should be replaced with a routine which +# returns the corresponding content for this case + +#TODO translated is currently temporarily here, another solution should be found where the +# needed string can be retrieved + + my $translatedTag = ' + + + +'; + return [ + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode($translatedTag)) . "\')", &mt("Translated Block"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("block"))) . "\')", &mt("Conditional Block"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("postanswerdate"))) . "\')", &mt("After Answer Date Block"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("preduedate"))) . "\')", &mt("Before Due Date Block"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("solved"))) . "\')", &mt("Block For After Solved"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("notsolved"))) . "\')", &mt("Block For When Not Solved"), undef] + ]; +} + +sub misc_datastructure { + return [ + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_img())) . "\')", &mt("Image"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::lonplot::insert_gnuplot())) . "\')", &mt("GNU Plot"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_organicstructure())) . "\')", &mt("Organic Structure"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::edit::insert_script())) . "\')", &mt("Script Block"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("allow"))) . "\')", &mt("File Dependencies"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("import"))) . "\')", &mt("Import a File"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&Apache::londefdef::insert_meta())) . "\')", &mt("Custom Metadata"), undef], + ["javascript:insertText(\'" . &convert_for_js(&HTML::Entities::encode(&default_xml_tag("part"))) . "\')", &mt("Problem Part"), undef] + ]; +} + +# helper routine for the datastructure building subroutines +sub default_xml_tag { + my ($tag) = @_; + return "\n<$tag>"; +} + + +sub helpmenu_datastructure { + + my $width = 500; + my $height = 600; + + my $helpers = [ + ['Problem_LON-CAPA_Functions', &mt('Script Functions')], + ['Greek_Symbols', &mt('Greek Symbols')], + ['Other_Symbols', &mt('Other Symbols')], + ['Authoring_Output_Tags', &mt('Output Tags')], + ['Authoring_Multilingual_Problems', + &mt('How to create problems in different languages')] + ]; + + my $help_structure = []; + + foreach my $count (0..(scalar(@{$helpers})-1)) { + my $filename = $helpers->[$count]->[0]; + my $title = $helpers->[$count]->[1]; + my $href = &HTML::Entities::encode("javascript:openMyModal('/adm/help/$filename.hlp',$width,$height,'yes');"); + push @{$help_structure}, [$href, $title, undef]; + } + + return $help_structure; +} + +# we need substitution to not break javascript code +sub convert_for_js { + my $return = shift; + $return =~ s|script|ESCAPEDSCRIPT|g; + $return =~ s|\\|\\\\|g; + $return =~ s|\n|\\r\\n|g; + $return =~ s|'|\\'|g; + $return =~ s|'|\\'|g; + return $return; +} + 1; __END__