--- loncom/homework/lonhomework.pm	2010/08/13 01:33:48	1.317.4.1
+++ loncom/homework/lonhomework.pm	2025/01/05 21:53:42	1.392
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # The LON-CAPA Homework handler
 #
-# $Id: lonhomework.pm,v 1.317.4.1 2010/08/13 01:33:48 raeburn Exp $
+# $Id: lonhomework.pm,v 1.392 2025/01/05 21:53:42 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -47,16 +47,25 @@ use Apache::externalresponse();
 use Apache::rankresponse();
 use Apache::matchresponse();
 use Apache::chemresponse();
+use Apache::functionplotresponse();
 use Apache::drawimage();
+use Apache::loncapamath();
+use Apache::loncourseuser();
+use Apache::grades();
 use Apache::Constants qw(:common);
 use Apache::loncommon();
+use Apache::lonparmset();
+use Apache::lonnavmaps();
 use Apache::lonlocal;
+use LONCAPA qw(:DEFAULT :match);
+use LONCAPA::ltiutils();
 use Time::HiRes qw( gettimeofday tv_interval );
 use HTML::Entities();
 use File::Copy();
 
 # FIXME - improve commenting
 
+my $registered_cleanup;
 
 BEGIN {
     &Apache::lonxml::register_insert();
@@ -111,7 +120,7 @@ sub get_target {
 	} elsif ( $env{'form.grade_target'} eq 'webgrade'
 		  && ($Apache::lonhomework::queuegrade eq 'F' )) {
 	    return ($env{'form.grade_target'});
-        } elsif ($env{'form.grade_target'} eq 'answer') {
+	} elsif ($env{'form.grade_target'} eq 'answer') {
             if ($env{'form.answer_output_mode'} eq 'tex') {
                 return ($env{'form.grade_target'});
             }
@@ -128,43 +137,56 @@ sub get_target {
 	    return ('web');
 	}
     } elsif ($env{'request.state'} eq "construct") {
+#
+# We are in construction space, editing and testing problems
+#
 	if ( defined($env{'form.grade_target'}) ) {
 	    return ($env{'form.grade_target'});
 	}
 	if ( defined($env{'form.preview'})) {
 	    if ( defined($env{'form.submitted'})) {
+#
+# We are doing a problem preview
+#
 		return ('grade', 'web');
 	    } else {
 		return ('web');
 	    }
 	} else {
 	    if ($env{'form.problemstate'} eq 'WEB_GRADE') {
-		#$env{'form.webgrade'} = 'yes';
 		return ('grade','webgrade','answer');
-	    } elsif (($env{'form.problemmode'} eq 'view') ||
-		     ($env{'form.problemmode'} eq 'discard')) {
-		if ( defined($env{'form.submitted'}) &&
-		     (!defined($env{'form.resetdata'})) &&
-		     (!defined($env{'form.newrandomization'}))) {
-		    return ('grade', 'web','answer');
-		} else {
-		    return ('web','answer');
-		}
-	    } elsif ($env{'form.problemmode'} eq 'edit') {
-		if ( $env{'form.submitted'} eq 'edit' ) {
-		    if ( $env{'form.submitbutton'} eq &mt('Save and View') ) {
-			return ('modified','web','answer');
-		    } else {
-			return ('modified','no_output_web','edit');
-		    }
-		} else {
+            } elsif ($env{'form.problemmode'} eq 'view') {
+                return ('grade','web','answer');
+	    } elsif ($env{'form.problemmode'} eq 'saveview') {
+                return ('modified','web','answer');
+            } elsif ($env{'form.problemmode'} eq 'discard') {
+                return ('web','answer');
+            } elsif (($env{'form.problemmode'} eq 'saveedit') ||
+                     ($env{'form.problemmode'} eq 'undo')) {
+                my %editors = &Apache::loncommon::permitted_editors();
+                if ($editors{'edit'}) {
+                    return ('modified','no_output_web','edit');
+                } else {
+                    return ('web');
+                }
+            } elsif ($env{'form.problemmode'} eq 'edit') {
+                my %editors = &Apache::loncommon::permitted_editors();
+                if ($editors{'edit'}) {
 		    return ('no_output_web','edit');
-		}
+                } else {
+                    return ('web');
+                }
 	    } else {
 		return ('web');
 	    }
-	}
+        }
+#
+# End of Authoring Space
+#
     }
+#
+# Huh? We are nowhere, so do nothing.
+#
     return ();
 }
 
@@ -174,16 +196,6 @@ sub setup_vars {
 #  return ';$external::target='.$target.';';
 }
 
-sub createmenu {
-    my ($which,$request)=@_;
-    if ($which eq 'grade') {
-	$request->print('<script type="text/javascript" language="JavaScript"> 
-          hwkmenu=window.open("/res/adm/pages/homeworkmenu.html","homeworkremote",
-                 "height=350,width=150,menubar=no");
-          </script>');
-    }
-}
-
 sub proctor_checked_in {
     my ($slot_name,$slot,$type)=@_;
     my @possible_proctors=split(",",$slot->{'proctor'});
@@ -194,7 +206,7 @@ sub proctor_checked_in {
     if ($type eq 'Task') {
 	my $version=$Apache::lonhomework::history{'resource.0.version'};
 	$key ="resource.$version.0.checkedin";
-    } elsif ($type eq 'problem') {
+    } elsif (($type eq 'problem') || ($type eq 'tool')) {
 	$key ='resource.0.checkedin';
     }
     # backward compatability, used to be username@domain, 
@@ -209,24 +221,57 @@ sub proctor_checked_in {
 	    return 1;
 	}
     }
-    
     return 0;
 }
 
 sub check_slot_access {
-    my ($id,$type)=@_;
+    my ($id,$type,$symb,$partlist)=@_;
 
     # does it pass normal muster
-    my ($status,$datemsg)=&check_access($id);
-    
-    my $useslots = &Apache::lonnet::EXT("resource.0.useslots");
+    my ($status,$datemsg)=&check_access($id,$symb);
+
+    my $useslots = &Apache::lonnet::EXT("resource.0.useslots",$symb);
     if ($useslots ne 'resource' && $useslots ne 'map' 
 	&& $useslots ne 'map_map') {
 	return ($status,$datemsg);
     }
 
-    if ($status eq 'SHOW_ANSWER' ||
-	$status eq 'CLOSED' ||
+    my $checkin = 'resource.0.checkedin';
+    my $version;
+    if ($type eq 'Task') {
+        $version=$Apache::lonhomework::history{'resource.version'};
+        $checkin = "resource.$version.0.checkedin";
+    }
+    my $checkedin = $Apache::lonhomework::history{$checkin};
+    my ($returned_slot,$slot_name,$checkinslot,$ipused,$blockip,$now,$ip,
+        $consumed_uniq);
+    $now = time;
+    $ip=$ENV{'REMOTE_ADDR'} || $env{'request.host'};
+
+    if ($checkedin) {
+        $checkinslot = $Apache::lonhomework::history{"$checkin.slot"};
+        my %slot=&Apache::lonnet::get_slot($checkinslot);
+        $consumed_uniq = $slot{'uniqueperiod'};
+        if ($slot{'iptied'}) {
+            $ipused = $Apache::lonhomework::history{"$checkin.ip"};
+            unless (($ip ne '') && 
+                    (($ipused eq $ip) || ($ENV{'REMOTE_ADDR'} eq '127.0.0.1'))) {
+                $blockip = $slot{'iptied'};
+                $slot_name = $checkinslot;
+                $returned_slot = \%slot;
+            }
+        }
+    }
+
+    if ($status eq 'SHOW_ANSWER') {
+        if ($blockip eq 'answer') {
+            return ('NEED_DIFFERENT_IP','',$slot_name,$returned_slot,$ipused);
+        } else {
+            return ($status,$datemsg);
+        }
+    }
+
+    if ($status eq 'CLOSED' ||
 	$status eq 'INVALID_ACCESS' ||
 	$status eq 'UNAVAILABLE') {
 	return ($status,$datemsg);
@@ -234,46 +279,91 @@ sub check_slot_access {
     if ($env{'request.state'} eq "construct") {
 	return ($status,$datemsg);
     }
-    
+
     if ($type eq 'Task') {
-	my $version=$Apache::lonhomework::history{'resource.version'};
-	if ($Apache::lonhomework::history{"resource.$version.0.checkedin"} &&
+	if ($checkedin &&
 	    $Apache::lonhomework::history{"resource.$version.0.status"} eq 'pass') {
-	    return ('SHOW_ANSWER');
-	}
+	    if ($blockip eq 'answer') {
+                return ('NEED_DIFFERENT_IP','',$slot_name,$returned_slot,$ipused);
+            } else {
+	        return ('SHOW_ANSWER');
+            }
+        }
+    } elsif (($type eq 'problem') &&
+             ($Apache::lonhomework::browse eq 'F') &&
+             ($ENV{'REMOTE_ADDR'} eq '127.0.0.1') &&
+             ($env{'form.grade_courseid'} eq $env{'request.course.id'}) &&
+             (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}))) {
+        return ($status,$datemsg);
     }
 
-    my $availablestudent = &Apache::lonnet::EXT("resource.0.availablestudent");
-    my $available = &Apache::lonnet::EXT("resource.0.available");
+    my $availablestudent = &Apache::lonnet::EXT("resource.0.availablestudent",$symb);
+    my $available = &Apache::lonnet::EXT("resource.0.available",$symb);
     my @slots= (split(':',$availablestudent),split(':',$available));
 
 #    if (!@slots) {
 #	return ($status,$datemsg);
 #    }
+    undef($returned_slot);
+    undef($slot_name);
     my $slotstatus='NOT_IN_A_SLOT';
-    my ($returned_slot,$slot_name);
+    my $num_usable_slots = 0;
+    if (!$symb) {
+        ($symb) = &Apache::lonnet::whichuser();
+    }
     foreach my $slot (@slots) {
 	$slot =~ s/(^\s*|\s*$)//g;
 	&Apache::lonxml::debug("getting $slot");
 	my %slot=&Apache::lonnet::get_slot($slot);
 	&Apache::lonhomework::showhash(%slot);
-	if ($slot{'starttime'} < time &&
-	    $slot{'endtime'} > time &&
+        next if ($slot{'endtime'} < $now);
+        $num_usable_slots ++;
+	if ($slot{'starttime'} < $now &&
+	    $slot{'endtime'} > $now &&
 	    &Apache::loncommon::check_ip_acc($slot{'ip'})) {
-	    &Apache::lonxml::debug("$slot is good");
-	    $slotstatus='NEEDS_CHECKIN';
-	    $returned_slot=\%slot;
-	    $slot_name=$slot;
-	    last;
-	}
+            if ($slot{'iptied'}) {
+                if ($env{'request.course.id'}) {
+                    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                    if ($slot eq $checkinslot) {
+                        if ($ip eq $ipused) {
+                            &Apache::lonxml::debug("$slot is good");
+                            $slotstatus ='NEEDS_CHECKIN'; 
+                        } else {
+                            $slotstatus = 'NEED_DIFFERENT_IP';
+                            $slot_name = $slot;
+                            $returned_slot = \%slot;
+                            last;
+                        }
+                    } elsif ($ip) {
+                        my $uniqkey = "$slot\0$symb\0$ip";
+                        my %used_ip = &Apache::lonnet::get('slot_uniqueips',[$uniqkey],$cdom,$cnum);
+                        if ($used_ip{$uniqkey}) {
+                            $slotstatus = 'NEED_DIFFERENT_IP';
+                        } else {
+                            &Apache::lonxml::debug("$slot is good");
+                            $slotstatus ='NEEDS_CHECKIN';
+                        }
+                    }
+                }
+            } else {
+	        &Apache::lonxml::debug("$slot is good");
+	        $slotstatus='NEEDS_CHECKIN';
+            }
+            if ($slotstatus eq 'NEEDS_CHECKIN') {
+	        $returned_slot=\%slot;
+	        $slot_name=$slot;
+	        last;
+            }
+        }
     }
     if ($slotstatus eq 'NEEDS_CHECKIN' &&
 	&proctor_checked_in($slot_name,$returned_slot,$type)) {
-	&Apache::lonxml::debug("protoctor checked in");
-	$slotstatus='CAN_ANSWER';
+	&Apache::lonxml::debug("proctor checked in");
+	$slotstatus=$status;
     }
 
-    my ($is_correct,$got_grade,$checkedin);
+    my ($is_correct,$got_grade);
     if ($type eq 'Task') {
 	my $version=$Apache::lonhomework::history{'resource.0.version'};
 	$got_grade = 
@@ -282,13 +372,49 @@ sub check_slot_access {
 	$is_correct =  
 	    ($Apache::lonhomework::history{"resource.$version.0.status"} eq 'pass'
 	     || $Apache::lonhomework::history{"resource.0.solved"} =~ /^correct_/ );
-	$checkedin =
-	    $Apache::lonhomework::history{"resource.$version.0.checkedin"};
-    } elsif ($type eq 'problem') {
-	$got_grade  = 1;
-	$checkedin  = $Apache::lonhomework::history{"resource.0.checkedin"};
-	$is_correct =
-	    ($Apache::lonhomework::history{"resource.0.solved"} =~/^correct_/);
+    } elsif (($type eq 'problem') || ($type eq 'tool')) {
+        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) {
+                    $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;
+            }
+        }
     }
     
     &Apache::lonxml::debug(" slot is $slotstatus checkedin ($checkedin) got_grade ($got_grade) is_correct ($is_correct)");
@@ -305,43 +431,127 @@ sub check_slot_access {
     # However, the problem is not closed, and potentially, another slot might be
     # used to gain access to it to work on it, until the due date is reached, and the
     # problem then becomes CLOSED.  Therefore return the slotstatus - 
-    # (which will be NOT_IN_SLOT).
-    if (!defined($slot_name)
-	&& $checkedin 
-	&& $type eq 'problem') {
-        return ($slotstatus);
+    # (which will be one of: NOT_IN_A_SLOT, RESERVABLE, RESERVABLE_LATER, or NOTRESERVABLE).
+
+    if (!defined($slot_name) && (($type eq 'problem') || ($type eq 'tool'))) {
+        if ($slotstatus eq 'NOT_IN_A_SLOT') {
+            if (!$num_usable_slots) {
+                ($slotstatus,$datemsg) = &check_reservable_slot($slotstatus,$symb,$now,$checkedin,
+                                                                $consumed_uniq);
+            }
+        }
+        return ($slotstatus,$datemsg);
     }
 
     if ($slotstatus eq 'NOT_IN_A_SLOT' 
 	&& $checkedin ) {
 
 	if ($got_grade) {
-	    return ('SHOW_ANSWER');
+            if ($blockip eq 'answer') {
+                return ('NEED_DIFFERENT_IP','',$slot_name,$returned_slot,$ipused);
+            } else {
+	        return ('SHOW_ANSWER');
+            }
 	} else {
 	    return ('WAITING_FOR_GRADE');
 	}
 
     }
 
-    if ( $is_correct) {
-	if ($type eq 'problem') {
+    if (($is_correct) && ($blockip ne 'answer')) {
+	if (($type eq 'problem') || ($type eq 'tool')) {
 	    return ($status);
 	}
 	return ('SHOW_ANSWER');
     }
 
     if ( $status eq 'CANNOT_ANSWER' && 
-	 ($slotstatus ne 'NEEDS_CHECKIN' && $slotstatus ne 'NOT_IN_A_SLOT')) {
+	 ($slotstatus ne 'NEEDS_CHECKIN' && $slotstatus ne 'NOT_IN_A_SLOT' &&
+          $slotstatus ne 'NEED_DIFFERENT_IP') ) {
 	return ($status,$datemsg);
     }
+    return ($slotstatus,$datemsg,$slot_name,$returned_slot,$ipused);
+}
 
-    return ($slotstatus,$datemsg,$slot_name,$returned_slot);
+sub check_reservable_slot {
+    my ($slotstatus,$symb,$now,$checkedin,$consumed_uniq) = @_;
+    my $datemsg;
+    if ($slotstatus eq 'NOT_IN_A_SLOT') {
+        if ($env{'request.course.id'}) {
+            my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+            unless ($symb) {
+                ($symb)=&Apache::lonnet::whichuser();
+            }
+            $slotstatus = 'NOTRESERVABLE';
+            my ($reservable_now_order,$reservable_now,$reservable_future_order,
+                $reservable_future) =
+                &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'};
+                        }
+                    }
+                }
+            }
+            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'};
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return ($slotstatus,$datemsg);
 }
 
 # JB, 9/24/2002: Any changes in this function may require a change
 # in lonnavmaps::resource::getDateStatus.
 sub check_access {
-    my ($id) = @_;
+    my ($id,$symb) = @_;
     my $date ='';
     my $status;
     my $datemsg = '';
@@ -371,27 +581,34 @@ sub check_access {
     &Apache::lonxml::debug("checking for part :$id:");
     &Apache::lonxml::debug("time:".time);
 
-    my ($symb)=&Apache::lonnet::whichuser();
+    unless ($symb) {
+        ($symb)=&Apache::lonnet::whichuser();
+    }
     &Apache::lonxml::debug("symb:".$symb);
     #if ($env{'request.state'} ne "construct" && $symb ne '') {
     if ($env{'request.state'} ne "construct") {
-        my $idacc = &Apache::lonnet::EXT("resource.$id.acc");
+        my $idacc = &Apache::lonnet::EXT("resource.$id.acc",$symb);
 	my $allowed=&Apache::loncommon::check_ip_acc($idacc);
 	if (!$allowed && ($Apache::lonhomework::browse ne 'F')) {
 	    $status='INVALID_ACCESS';
 	    $date=&mt("can not be accessed from your location.");
 	    return($status,$date);
 	}
-	
+	if ($env{'form.grade_imsexport'}) {
+            if (($env{'request.course.id'}) && 
+                (&Apache::lonnet::allowed('mdc',$env{'request.course.id'}))) {
+                return ('SHOW_ANSWER');
+            }
+        }
 	foreach my $temp ("opendate","duedate","answerdate") {
 	    $lastdate = $date;
 	    if ($temp eq 'duedate') {
-		$date = &due_date($id);
+		$date = &due_date($id,$symb);
 	    } else {
-		$date = &Apache::lonnet::EXT("resource.$id.$temp");
+		$date = &Apache::lonnet::EXT("resource.$id.$temp",$symb);
 	    }
 	    
-	    my $thistype = &Apache::lonnet::EXT("resource.$id.$temp.type");
+	    my $thistype = &Apache::lonnet::EXT("resource.$id.$temp.type",$symb);
 	    if ($thistype =~ /^(con_lost|no_such_host)/ ||
 		$date     =~ /^(con_lost|no_such_host)/) {
 		$status='UNAVAILABLE';
@@ -400,10 +617,10 @@ sub check_access {
 	    }
 	    if ($thistype eq 'date_interval') {
 		if ($temp eq 'opendate') {
-		    $date=&Apache::lonnet::EXT("resource.$id.duedate")-$date;
+		    $date=&Apache::lonnet::EXT("resource.$id.duedate",$symb)-$date;
 		}
 		if ($temp eq 'answerdate') {
-		    $date=&Apache::lonnet::EXT("resource.$id.duedate")+$date;
+		    $date=&Apache::lonnet::EXT("resource.$id.duedate",$symb)+$date;
 		}
 	    }
 	    &Apache::lonxml::debug("found :$date: for :$temp:");
@@ -423,30 +640,33 @@ sub check_access {
 	    $datemsg=$date;
 	} elsif ($type eq 'opendate') {
 	    $status='CLOSED';
-	    $datemsg = &mt("will open on")." $date";
+	    $datemsg = &mt('will open on [_1]',$date);
 	} elsif ($type eq 'duedate') {
 	    $status='CAN_ANSWER';
-	    $datemsg = &mt("is due at")." $date";
+	    $datemsg = &mt('is due at [_1]',$date);
 	} elsif ($type eq 'answerdate') {
 	    $status='CLOSED';
-	    $datemsg = &mt("was due on")." $lastdate".&mt(", and answers will be available on")." $date";
+	    $datemsg = &mt('was due on [_1], and answers will be available on [_2]',
+                               $lastdate,$date);
 	}
     }
     if ($status eq 'CAN_ANSWER' ||
 	(($Apache::lonhomework::browse eq 'F') && ($status eq 'CLOSED'))) {
 	#check #tries, and if correct.
 	my $tries = $Apache::lonhomework::history{"resource.$id.tries"};
-	my $maxtries = &Apache::lonnet::EXT("resource.$id.maxtries");
+	my $maxtries = &Apache::lonnet::EXT("resource.$id.maxtries",$symb);
 	if ( $tries eq '' ) { $tries = '0'; }
 	if ( $maxtries eq '' && 
 	     $env{'request.state'} ne 'construct') { $maxtries = '2'; } 
 	if ($maxtries && $tries >= $maxtries) { $status = 'CANNOT_ANSWER'; }
 	# if (correct and show prob status) or excused then CANNOT_ANSWER
-	if(($Apache::lonhomework::history{"resource.$id.solved"}=~/^correct/
-	    &&
-	    &show_problem_status())
-	   ||
-	   $Apache::lonhomework::history{"resource.$id.solved"}=~/^excused/) {
+	if ( ($Apache::lonhomework::history{"resource.$id.solved"}=~/^correct/)
+	      && (&show_problem_status()) ) {
+            if (($Apache::lonhomework::history{"resource.$id.awarded"} >= 1) ||
+                (&Apache::lonnet::EXT("resource.$id.retrypartial",$symb) !~/^1|on|yes$/i)) {
+	        $status = 'CANNOT_ANSWER';
+            }
+        } elsif ($Apache::lonhomework::history{"resource.$id.solved"}=~/^excused/) {
 	    $status = 'CANNOT_ANSWER';
 	}
 	if ($status eq 'CANNOT_ANSWER'
@@ -455,17 +675,18 @@ sub check_access {
 	}
     }
     if ($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER') {
-	my @interval=&Apache::lonnet::EXT("resource.$id.interval");
+	my @interval=&Apache::lonnet::EXT("resource.$id.interval",$symb);
 	&Apache::lonxml::debug("looking for interval @interval");
-	if ($interval[0]) {
-	    my $first_access=&Apache::lonnet::get_first_access($interval[1]);
+	if ($interval[0]=~ /^\d+/) {
+	    my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb);
 	    &Apache::lonxml::debug("looking for accesstime $first_access");
 	    if (!$first_access) {
 		$status='NOT_YET_VIEWED';
-		my $due_date = &due_date($id);
+		my $due_date = &due_date($id,$symb);
 		my $seconds_left = $due_date - time;
-		if ($seconds_left > $interval[0] || $due_date eq '') {
-		    $seconds_left = $interval[0];
+		my ($timelimit) = ($interval[0] =~ /^(\d+)/);
+		if ($seconds_left > $timelimit || $due_date eq '') {
+		    $seconds_left = $timelimit;
 		}
 		$datemsg=&seconds_to_human_length($seconds_left);
 	    }
@@ -477,7 +698,6 @@ sub check_access {
   #    return ('UNCHECKEDOUT','needs to be checked out');
   #}
 
-
     &Apache::lonxml::debug("sending back :$status:$datemsg:");
     if (($Apache::lonhomework::browse eq 'F') && ($status eq 'CLOSED')) {
 	&Apache::lonxml::debug("should be allowed to browse a resource when closed");
@@ -501,7 +721,8 @@ sub due_date {
 	my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb);
 	&Apache::lonxml::debug("looking for first_access $first_access ($interval[1])");
 	if (defined($first_access)) {
-	    my $interval = $first_access+$interval[0];
+	    my ($timelimit) = ($interval[0] =~ /^(\d+)/);
+	    my $interval = $first_access+$timelimit;
 	    $date = (!$due_date || $interval < $due_date) ? $interval
                                                           : $due_date;
 	} else {
@@ -510,7 +731,7 @@ sub due_date {
     } else {
 	$date = $due_date;
     }
-    return $date
+    return $date;
 }
 
 sub seconds_to_human_length {
@@ -559,7 +780,7 @@ sub showarray {
 sub showhashsubset {
     my ($hash,$keyre) = @_;
     my $resultkey;
-    foreach $resultkey (sort keys %$hash) {
+    foreach $resultkey (sort(keys(%$hash))) {
 	if ($resultkey !~ /$keyre/) { next; }
 	if (ref($$hash{$resultkey})  eq 'ARRAY' ) {
 	    &Apache::lonxml::debug("$resultkey ---- ".
@@ -577,12 +798,18 @@ sub showhashsubset {
 
 sub setuppermissions {
     $Apache::lonhomework::browse= &Apache::lonnet::allowed('bre',$env{'request.filename'});
+    unless ($Apache::lonhomework::browse eq 'F') {
+        $Apache::lonhomework::browse=&Apache::lonnet::allowed('bro',$env{'request.filename'}); 
+    }
     my $viewgrades = &Apache::lonnet::allowed('vgr',$env{'request.course.id'});
     if (! $viewgrades && 
 	exists($env{'request.course.sec'}) && 
 	$env{'request.course.sec'} !~ /^\s*$/) {
 	$viewgrades = &Apache::lonnet::allowed('vgr',$env{'request.course.id'}.
                                                '/'.$env{'request.course.sec'});
+        if ($viewgrades) {
+            $Apache::lonhomework::viewgradessec = $env{'request.course.sec'};
+        }
     }
     $Apache::lonhomework::viewgrades = $viewgrades;
 
@@ -600,6 +827,9 @@ sub setuppermissions {
 	$modifygrades = 
 	    &Apache::lonnet::allowed('mgr',$env{'request.course.id'}.
 				     '/'.$env{'request.course.sec'});
+        if ($modifygrades) {
+            $Apache::lonhomework::modifygradessec = $env{'request.course.sec'};
+        }
     }
     $Apache::lonhomework::modifygrades = $modifygrades;
 
@@ -618,7 +848,9 @@ sub setuppermissions {
 sub unset_permissions {
     undef($Apache::lonhomework::queuegrade);
     undef($Apache::lonhomework::modifygrades);
+    undef($Apache::lonhomework::modifygradessec);
     undef($Apache::lonhomework::viewgrades);
+    undef($Apache::lonhomework::viewgradessec);
     undef($Apache::lonhomework::browse);
 }
 
@@ -636,12 +868,13 @@ sub setupheader {
 }
 
 sub handle_save_or_undo {
-    my ($request,$problem,$result) = @_;
+    my ($request,$problem,$result,$getobjref) = @_;
+
     my $file    = &Apache::lonnet::filelocation("",$request->uri);
     my $filebak =$file.".bak";
     my $filetmp =$file.".tmp";
     my $error=0;
-    if ($env{'form.Undo'} eq &mt('undo')) {
+    if (($env{'form.problemmode'} eq 'undo') || ($env{'form.problemmode'} eq 'undoxml')) {
 	my $error=0;
 	if (!&File::Copy::copy($file,$filetmp)) { $error=1; }
 	if ((!$error) && (!&File::Copy::copy($filebak,$file))) { $error=1; }
@@ -664,6 +897,7 @@ sub handle_save_or_undo {
 	}
     } else {
         &Apache::lonnet::correct_line_ends($result);
+
 	my $fs=Apache::File->new(">$filebak");
 	if (defined($fs)) {
 	    print $fs $$problem;
@@ -677,6 +911,30 @@ sub handle_save_or_undo {
 	my $fh=Apache::File->new(">$file");
 	if (defined($fh)) {
 	    print $fh $$result;
+            if (ref($getobjref) eq 'SCALAR') {
+                if ($file =~ m{([^/]+)\.(html?)$}) {
+                    my $fname = $1;
+                    my $ext = $2;
+                    my $path = $file;
+                    $path =~ s/\Q$fname\E\.\Q$ext\E$//; 
+                    my (%allfiles,%codebase);
+                    &Apache::lonnet::extract_embedded_items($file,\%allfiles,
+                                                           \%codebase,$result);
+                    if (keys(%allfiles) > 0) {
+                        my $url = $request->uri;
+                        my $state = <<STATE;
+    <input type="hidden" name="action" value="upload_embedded" />
+    <input type="hidden" name="url" value="$url" />
+STATE
+                        $$getobjref = "<h3>".&mt("Reference Warning")."</h3>".
+                                      "<p>".&mt("Completed upload of the file. This file contained references to other files.")."</p>".
+                                      "<p>".&mt("Please select the locations from which the referenced files are to be uploaded.")."</p>".
+                                      &Apache::loncommon::ask_for_embedded_content($url,$state,\%allfiles,\%codebase,
+                                      {'error_on_invalid_names'   => 1,
+                                       'ignore_remote_references' => 1,});
+                    }
+                }
+            }
 	} else {
 	    &Apache::lonxml::info('<span class="LC_error">'.
 				  &mt("Unable to write to [_1]",
@@ -691,11 +949,20 @@ sub handle_save_or_undo {
 
 sub analyze_header {
     my ($request) = @_;
-    my $js = &Apache::structuretags::setmode_javascript();
+    my $js = &Apache::lonxml::setmode_javascript();
 
     # Breadcrumbs
-    my $brcrum = [{'href' => &Apache::loncommon::authorspace(),
-                   'text' => 'Construction Space'},
+    my $text = 'Authoring Space';
+    my $href = &Apache::loncommon::authorspace($request->uri);
+    if ($env{'request.course.id'}) {
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        if ($href eq "/priv/$cdom/$cnum/") {
+            $text = 'Course Authoring Space';
+        }
+    }
+    my $brcrum = [{'href' => $href,
+                   'text' => $text},
                   {'href' => '',
                    'text' => 'Problem Testing'},
                   {'href' => '',
@@ -707,24 +974,32 @@ sub analyze_header {
                                        {'bread_crumbs' => $brcrum,})
        .&Apache::loncommon::head_subbox(
                 &Apache::loncommon::CSTR_pageheader());
+    my %lt = &Apache::lonlocal::texthash(
+                 edit => 'Edit',
+                 editxml => 'EditXML',
+             );
     $result .= 
-	&Apache::lonxml::message_location().'
-            <form name="lonhomework" method="post" action="'.
+	    '<form name="lonhomework" method="post" action="'.
 	    &HTML::Entities::encode($env{'request.uri'},'<>&"').'">'.
             '<input type="hidden" name="problemmode" value="'.
             $env{'form.problemmode'}.'" />'.
 	    &Apache::structuretags::remember_problem_state().'
-            <div class="LC_edit_problem_analyze_header">
-            <input type="button" name="submitmode" value="'.&mt("EditXML").'" '.
-            'onclick="javascript:setmode(this.form,'."'editxml'".')" />
-            <input type="button" name="submitmode" value="'.&mt('Edit').'" '.
-            'onclick="javascript:setmode(this.form,'."'edit'".')" />
-            <hr />
+            <div class="LC_edit_problem_analyze_header">';
+     my %editors = &Apache::loncommon::permitted_editors();
+     foreach my $item ('editxml','edit') {
+         next unless ($editors{$item});
+         $result .= '<input type="button" name="submitmode" value="'.$lt{$item}.'" '.
+                    'onclick="javascript:setmode(this.form,'."'$item'".')" />'.
+                    "\n";
+     }
+     $result .=
+            '<hr />
             <input type="button" name="submitmode" value="'.&mt("View").'" '.
             'onclick="javascript:setmode(this.form,'."'view'".')" />
             <hr />
-            </div>
-            </form>';
+            </div>'
+            .&Apache::lonxml::message_location().
+            '</form>';
     &Apache::lonxml::add_messages(\$result);
     $request->print($result);
     $request->rflush();
@@ -746,13 +1021,9 @@ sub analyze {
     my $rndseed=$env{'form.rndseed'};
     &analyze_header($request);
     my %prog_state=
-	&Apache::lonhtmlcommon::Create_PrgWin($request,&mt('Analyze Progress'),
-					      &mt('Getting Problem Variants'),
-					      $env{'form.numtoanalyze'},
-					      'inline',undef);
+	&Apache::lonhtmlcommon::Create_PrgWin($request,$env{'form.numtoanalyze'});
     for(my $i=1;$i<$env{'form.numtoanalyze'}+1;$i++) {
-	&Apache::lonhtmlcommon::Increment_PrgWin($request,\%prog_state,
-						 &mt('last problem'));
+	&Apache::lonhtmlcommon::Increment_PrgWin($request,\%prog_state,'last problem');
 	if (&Apache::loncommon::connection_aborted($request)) { return; }
         my $thisseed=$i+$rndseed;
 	my $subresult=&Apache::lonnet::ssi($request->uri,
@@ -761,7 +1032,7 @@ sub analyze {
 	(my $garbage,$subresult)=split(/_HASH_REF__/,$subresult,2);
 	my %analyze=&Apache::lonnet::str2hash($subresult);
 	my @parts;
-	if (defined(@{ $analyze{'parts'} })) {
+        if (ref($analyze{'parts'}) eq 'ARRAY') {
 	    @parts=@{ $analyze{'parts'} };
 	}
 	foreach my $part (@parts) {
@@ -794,15 +1065,15 @@ sub analyze {
 	    }
 	}
     }
-    &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state,
-					  &mt('Analyzing Results'));
+    &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state,&mt('Analyzing Results'));
     $request->print('<hr />'
                    .'<h3>'
                    .&mt('List of possible answers')
                    .'</h3>'
     );
     foreach my $part (sort(keys(%allparts))) {
-	if (defined(@{ $overall{$part.'.answer'} })) {
+        if ((ref($overall{$part.'.answer'}) eq 'ARRAY') &&
+            (@{$overall{$part.'.answer'}} > 0)) {
 	    for (my $i=0;$i<scalar(@{ $overall{$part.'.answer'} });$i++) {
 		my $num_cols=scalar(@{ $overall{$part.'.answer'}[$i][0] });
                 $request->print(&Apache::loncommon::start_data_table()
@@ -900,20 +1171,22 @@ sub editxmlmode {
     my $problem=&Apache::lonnet::getfile($file);
     if ($problem eq -1) {
 	&Apache::lonxml::error(
-            '<b> '
+            '<p class="LC_error">'
            .&mt('Unable to find [_1]',
                 '<span class="LC_filename">'.$file.'</span>')
-           .'</b>');
+           .'</p>');
 
 	$problem='';
     }
-    if (defined($env{'form.editxmltext'}) || defined($env{'form.Undo'})) {
+    if (($env{'form.problemmode'} eq 'saveeditxml') ||
+        ($env{'form.problemmode'} eq 'saveviewxml') ||
+        ($env{'form.problemmode'} eq 'undoxml')) {
 	my $error=&handle_save_or_undo($request,\$problem,
 				       \$env{'form.editxmltext'});
 	if (!$error) { $problem=&Apache::lonnet::getfile($file); }
     }
     &Apache::lonhomework::showhashsubset(\%env,'^form');
-    if ( $env{'form.submitbutton'} eq &mt('Save and View') ) {
+    if ($env{'form.problemmode'} eq 'saveviewxml') {
 	&Apache::lonhomework::showhashsubset(\%env,'^form');
 	$env{'form.problemmode'}='view';
 	&renderpage($request,$file);
@@ -925,28 +1198,34 @@ sub editxmlmode {
 	my $js =
 	    &Apache::edit::js_change_detection(). 
 	    &Apache::loncommon::resize_textarea_js().
-            &Apache::structuretags::setmode_javascript().
+            &Apache::lonxml::setmode_javascript().
             &Apache::lonhtmlcommon::dragmath_js("EditMathPopup");
-	my $only_body =  ($env{'environment.remote'} eq 'off')? 0 : 1;
-	my $dragmath_button = 
-            &Apache::lonhtmlcommon::dragmath_button("LC_editxmltext",1);
 
     # Breadcrumbs
-    my $brcrum = [{'href' => &Apache::loncommon::authorspace(),
-                   'text' => 'Construction Space'},
+    my $text = 'Authoring Space';
+    my $href = &Apache::loncommon::authorspace($request->uri);
+    if ($env{'request.course.id'}) {
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        if ($href eq "/priv/$cdom/$cnum/") {
+            $text = 'Course Authoring Space';
+        }
+    }
+    my $brcrum = [{'href' => $href,
+                   'text' => $text},
                   {'href' => '',
                    'text' => 'Problem Editing'}];
 
 	my $start_page = 
 	    &Apache::loncommon::start_page(&mt("EditXML [_1]",$file),$js,
 					   {'no_auto_mt_title' => 1,
-					    'only_body'        => $only_body,
+					    'only_body'        => 0,
 					    'add_entries'      => {
 						'onresize' => q[resize_textarea('LC_editxmltext','LC_aftertextarea')],
 						'onload'   => q[resize_textarea('LC_editxmltext','LC_aftertextarea')],
-                                    },
+                                                                  },
                                                 'bread_crumbs' => $brcrum,
-});
+                                             });
 
     $result=$start_page
            .&Apache::loncommon::head_subbox(
@@ -955,39 +1234,89 @@ sub editxmlmode {
             '<form '.&Apache::edit::form_change_detection().' name="lonhomework" method="post" action="'.
 	    &HTML::Entities::encode($env{'request.uri'},'<>&"').'">'.
 	    &Apache::structuretags::remember_problem_state().'
-            <div class="LC_edit_problem_editxml_header">
-              <table class="LC_edit_problem_header_title"><tr><td>
-               <h2>'.&mt('Problem Editing').' '.&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index').'</h2>
-                </td><td align="right">
-                  '.&Apache::loncommon::helpLatexCheatsheet('Problem_LON-CAPA_Functions','Script Functions').'
-                </td></tr>
-              </table>
-              <div class="LC_edit_problem_discards">
-                <input type="hidden" name="problemmode" value="editxml" />
-
-                <input type="button" name="submitmode" accesskey="d" value="'.&mt('Discard Edits and View').'" '.
-                'onclick="javascript:setmode(this.form,'."'discard'".')" />
-                <input type="button" '.&Apache::edit::submit_ask_anyway('setmode(this.form,'."'edit'".')').'name="submitmode" accesskey="e" value="'.&mt('Edit').'" />
-                <input type="submit" name="Undo" accesskey="u" value="'.&mt('undo').'" />
-                '.$dragmath_button.'
-              </div>
-              <div class="LC_edit_problem_saves">
-                <input type="submit" name="submitbutton" accesskey="s" value="'.&mt('Save').'"  />
-                <input type="submit" name="submitbutton" accesskey="v" value="'.&mt('Save and View').'" />
-              </div>
-              <hr style="clear:both;" />
-	      '.&Apache::lonxml::message_location().'
-            </div>
-            '  . '
-            <textarea '.&Apache::edit::element_change_detection().
-	              ' rows="'.$rows.'" cols="'.$cols.'" style="width:100%" '.
-		      ' name="editxmltext" id="LC_editxmltext">'.
-		      &HTML::Entities::encode($problem,'<>&"').'</textarea>
-            <div id="LC_aftertextarea">
-            </div>
-            </form>'.&Apache::loncommon::end_page();
-	&Apache::lonxml::add_messages(\$result);
-	$request->print($result);
+            <div class="LC_edit_problem_header">
+              <div class="LC_edit_problem_header_title">'.
+                &mt('Problem Editing').' '.&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index').
+              '</div><div class="LC_edit_actionbar" id="actionbar">';
+
+    $result.='<input type="hidden" name="problemmode" value="saveedit" />'.
+                  &Apache::structuretags::problem_edit_buttons('editxml');
+    $result.='<div>';
+
+    $result .= '<ol class="LC_primary_menu" style="display:inline-block;font-size:90%;vertical-align:middle;">';
+
+    my $nocodemirror = &Apache::loncommon::nocodemirror();
+    unless ($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") . ' <img src="/adm/help/help.png" alt="' . &mt("Help") .
+        '" style="vertical-align:text-bottom; height: auto; margin:0; "/>',
+        helpmenu_datastructure(),"");
+
+    $result.="</ol></div>";
+
+    $result .= '</div></div>' .
+        &Apache::lonxml::message_location() .
+        &Apache::loncommon::xmleditor_js() .
+        '<textarea ' . &Apache::edit::element_change_detection() .
+        ' rows="'.$rows.'" cols="'.$cols.'" style="width:100%" ' .
+        ' name="editxmltext" id="LC_editxmltext">' .
+        &HTML::Entities::encode($problem,'<>&"') .
+        '</textarea> <div id="LC_aftertextarea"> </div> </form>';
+
+    my $resource = $env{'request.ambiguous'};
+    unless ($nocodemirror) {
+        $result .= '<link rel="stylesheet" href="/adm/codemirror/codemirror-combined-xml.css">
+        <script src="/adm/codemirror/codemirror-compressed-xml.js"></script>
+        <script>
+            CodeMirror.defineMode("mixedmode", function(config) {
+                return CodeMirror.multiplexingMode(
+                    CodeMirror.getMode(config, "xml"),
+                    {
+                        open: "\<script type=\"loncapa/perl\"\>", close: "\</script\>",
+                        mode: CodeMirror.getMode(config, "perl"),
+                        delimStyle: "tag",
+                    }
+              );
+            });
+            var cm = CodeMirror.fromTextArea(document.getElementById("LC_editxmltext"),
+            {
+                mode: "mixedmode",
+                lineWrapping: true,
+                lineNumbers: true,
+                tabSize: 4,
+                indentUnit: 4,
+
+                autoCloseTags: true,
+                autoCloseBrackets: true,
+                height: "auto",
+                styleActiveLine: true,
+
+                extraKeys: {
+                    "Tab": "indentMore",
+                    "Shift-Tab": "indentLess",
+                }
+            });
+            restoreScrollPosition("'.$resource.'");
+        </script>';
+    }
+
+    $result .= &Apache::loncommon::end_page();
+    &Apache::lonxml::add_messages(\$result);
+    $request->print($result);
     }
     return '';
 }
@@ -996,7 +1325,7 @@ sub editxmlmode {
 #    Render the page in whatever target desired.
 #
 sub renderpage {
-    my ($request,$file,$targets,$return_string) = @_;
+    my ($request,$file,$targets,$return_string,$donebuttonmsg,$viewasuser,$symb) = @_;
 
     my @targets = @{$targets || [&get_target()]};
     &Apache::lonhomework::showhashsubset(\%env,'form.');
@@ -1022,12 +1351,13 @@ sub renderpage {
 	    $problem='';
 	    my $filename=(split('/',$file))[-1];
 	    my $error =
-		"<b> ".&mt('Unable to find [_1]',
+		'<p class="LC_error">'
+               .&mt('Unable to find [_1]',
 			   '<span class="LC_filename">'.$filename.'</span>')
-		."</b>";
+		."</p>";
 	    $result.=
 		&Apache::loncommon::simple_error_page($request,'Not available',
-						      $error);
+						      $error,{'no_auto_mt_msg' => 1});
 	    return;
 	}
 
@@ -1036,6 +1366,10 @@ sub renderpage {
 	if ($target eq 'answer') { &showhash(%Apache::lonhomework::history); }
 	if ($target eq 'web') {&Apache::lonhomework::showhashsubset(\%env,'^form');}
 
+        if (($target eq 'web') && ($viewasuser ne '') && ($symb ne '')) {
+            $env{'request.user_in_effect'} = $viewasuser;
+        }
+
 	&Apache::lonxml::debug("Should be parsing now");
 	$result .= &Apache::lonxml::xmlparse($request, $target, $problem,
 					     &setup_vars($target),%mystyle);
@@ -1048,7 +1382,12 @@ sub renderpage {
 	    if ($target eq 'analyze') {
 		$result=&Apache::lonnet::hashref2str(\%Apache::lonhomework::analyze);
 		undef(%Apache::lonhomework::analyze);
-	    }
+	    } elsif ($target eq 'web') {
+                if ($donebuttonmsg) {
+                    $result =~ s{</body>}{};
+                    $result.= &Apache::loncommon::confirmwrapper(&Apache::lonhtmlcommon::confirm_success($donebuttonmsg,1))."\n</body>";
+                }
+            }
 	    #my $td=&tv_interval($t0);
 	    #if ( $Apache::lonxml::debug) {
 	    #$result =~ s:</body>::;
@@ -1056,15 +1395,23 @@ sub renderpage {
 	    #}
 #	    $request->print($result);
 	    $overall_result.=$result;
+            if (($target eq 'web') && ($viewasuser ne '') && ($symb ne '')) {
+                my ($vuname,$vudom) = split(/:/,$viewasuser);
+                $overall_result .= &Apache::grades::view_as_user($symb,$vuname,$vudom).
+                                   '</body></html>';
+            }
 #	    $request->rflush();
 	}
+        if (($target eq 'web') && ($viewasuser ne '') && ($symb ne '')) {
+            undef($env{'request.user_in_effect'});
+        }
 	#$request->print(":Result ends");
 	#my $td=&tv_interval($t0);
     }
     if (!$return_string) {
 	&Apache::lonxml::add_messages(\$overall_result);
 	$request->print($overall_result);   
-	$request->rflush();   
+	$request->rflush();
     } else {
 	return $overall_result;
     }
@@ -1075,7 +1422,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;
@@ -1084,13 +1449,9 @@ 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')),
-                      &mt(&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;
     foreach my $file (@files) {
         next if ($file->[1] !~ /\S/);
@@ -1106,6 +1467,7 @@ sub get_template_list {
     my $count = 0;
     my $currentcategory='';
     my $first = 1;
+    my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
     foreach my $file (@files) {
 	next if ($file->[1] !~ /\S/);
         if ($file->[2] ne $currentcategory) {
@@ -1128,11 +1490,16 @@ sub get_template_list {
         if ($file->[3]) {
            $result.=&Apache::loncommon::help_open_topic($file->[3]);
         }
+        # Provide example link
         my $filename=$file->[0];
-        $filename=~s/^\/home\/httpd\/html//;
-        $result.=' <span class="LC_fontsize_small">'
-                .'<a href="'.$filename.'?inhibitmenu=yes" target="sample">'.&mt('Example').'</a>'
-                .'</span><br />'."\n";
+        $filename=~s{^\Q$londocroot\E}{};
+        if (!(grep($filename =~ /\Q$_\E$/,@noexamplelink))) {
+            $result .= ' <span class="LC_fontsize_small">'
+                      .&Apache::loncommon::modal_link(
+                           $filename.'?inhibitmenu=yes',&mt('Example'),600,420,'sample')
+                      .'</span>';
+        }
+        $result .= '<br />'."\n";
         $count ++;
     }
     if ($numfiles > 0) {
@@ -1144,17 +1511,44 @@ sub get_template_list {
 sub newproblem {
     my ($request) = @_;
 
+    if ($env{'form.mode'} eq 'blank'){
+        my $dest = &Apache::lonnet::filelocation("",$request->uri);
+        my $templatefilename =
+            $request->dir_config('lonIncludes').'/templates/blank.problem';
+        &File::Copy::copy($templatefilename,$dest);
+        &renderpage($request,$dest);
+        return;
+    }
+    my $errormsg;
     if ($env{'form.template'}) {
-	my $file = $env{'form.template'};
-	my $dest = &Apache::lonnet::filelocation("",$request->uri);
-	&File::Copy::copy($file,$dest);
-	&renderpage($request,$dest);
-	return;
+        my $file;
+        my ($extension) = ($env{'form.template'} =~ /\.(\w+)$/);
+        if ($extension) {
+            my @files = &get_template_list($extension);
+            foreach my $poss (@files) {
+                if (ref($poss) eq 'ARRAY') {
+                    if ($env{'form.template'} eq $poss->[0]) {
+                        $file = $env{'form.template'};
+                        last;
+                    }
+                }
+            }
+            if ($file) {
+	        my $dest = &Apache::lonnet::filelocation("",$request->uri);
+	        &File::Copy::copy($file,$dest);
+	        &renderpage($request,$dest);
+	        return;
+            } else {
+                $errormsg = '<p class="LC_error">'.&mt('Invalid template file.').'</p>';
+            }
+        } else {
+            $errormsg = '<p class="LC_error">'.&mt('Invalid template file; template needs to be a .problem, .library, or .task file.').'</p>';
+        }
     }
 
     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 =
@@ -1165,13 +1559,19 @@ sub newproblem {
 	&renderpage($request,$dest);
     } else {
 	my $url=&HTML::Entities::encode($request->uri,'<>&"');
-	my $shownurl=$url;	
-	$shownurl=~s-^/~-/priv/-;
 	my $dest = &Apache::lonnet::filelocation("",$request->uri);
-	my $errormsg;
 	my $instructions;
-        my $brcrum = [{'href' => &Apache::loncommon::authorspace(),
-                       'text' => 'Construction Space'},
+        my $text = 'Authoring Space';
+        my $href = &Apache::loncommon::authorspace($request->uri);
+        if ($env{'request.course.id'}) {
+            my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+            my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+            if ($href eq "/priv/$cdom/$cnum/") {
+                $text = 'Course Authoring Space';
+            }
+        }
+        my $brcrum = [{'href' => $href,
+                       'text' => $text},
                       {'href' => '',
                        'text' => "Create New $extension"}];
 	my $start_page = 
@@ -1185,7 +1585,7 @@ sub newproblem {
        .'<h1>'.&mt("Creating a new $extension resource.")."</h1>
 $errormsg
 ".&mt("The requested file [_1] currently does not exist.",
-      '<span class="LC_filename">'.$shownurl.'</span>').'
+      '<span class="LC_filename">'.$url.'</span>').'
 <p class="LC_info">
 '.&mt("To create a new $extension, select a template from the".
       " list below. Then click on the \"Create $extension\" button.").'
@@ -1218,21 +1618,90 @@ 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",$symb);
+    if (@interval > 1) {
+        if ($interval[1] eq 'course') {
+            return ('fail',&mt('Ending of timed events not supported for intervals set course-wide'));
+        } else {
+            my $now = time;
+            my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb);
+            if ($first_access > 0) {
+                my ($timelimit,$donesuffix) = split(/_/,$interval[0],2);
+                if ($donesuffix =~ /^done(?:|\:[^\:]+\:)(.*)$/) {
+                    my ($dummy,$proctor,$secret) = split(/_/,$1);
+                    if (($proctor) && ($secret ne '')) {
+                        my $key = $env{'form.LC_interval_done_proctorpass'};
+                        $key =~ s/^\s+//;
+                        $key =~ s/\s+$//;
+                        if ($env{'form.LC_interval_done_proctorpass'} ne $secret) {
+                            return ('fail',
+                                   &mt('Incorrect key entered by proctor'));
+                        }
+                    }
+                    if ($first_access+$timelimit > $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'});
+                        if ($result eq '') {
+                            # Record action in "User Notes"
+                            &Apache::lonmsg::store_instructor_comment(
+                                'Pressed Done button for symb:<br />'.$symb,
+                                $env{'user.name'}, $env{'user.domain'});
+                            return ('ok');
+                        } else {
+                            return ('fail',&mt('Error ending timed event: [_1]',$result));
+                        }
+                    } else {
+                        return ('fail',&mt('Timed event already ended'));
+                    }
+                } else {
+                    return ('fail',&mt('Timed event can not be ended before the time limit'));
+                }
+            } else {
+                return ('fail',&mt('Timer not yet started for this timed event'));
+            }
+        }
+    } else {
+        return ('fail',&mt('No timer in use'));
+    }
+    return();
+}
 
 sub handler {
     #my $t0 = [&gettimeofday()];
     my $request=$_[0];
+
     $Apache::lonxml::request=$request;
     $Apache::lonxml::debug=$env{'user.debug'};
     $env{'request.uri'}=$request->uri;
     &setuppermissions();
 
+# -------------------------------------- Flag and buffer for registered cleanup
+    $registered_cleanup=0;
+    undef(@Apache::lonhomework::ltipassback);
+
     my $file=&Apache::lonnet::filelocation("",$request->uri);
 
     #check if we know where we are
-    if ($env{'request.course.fn'} && !&Apache::lonnet::symbread()) { 
+    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' && 
+	if ($Apache::lonhomework::browse ne 'F' &&
 	    $env{'request.state'} ne "construct") {
 	    #should know where we are, so ask
 	    &unset_permissions();
@@ -1244,6 +1713,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();
@@ -1256,8 +1726,17 @@ sub handler {
 		#first visit to problem in construction space
 		$env{'form.problemmode'}= 'view';
 		&renderpage($request,$file);
-	    } elsif ($env{'form.problemmode'} eq 'editxml') {
-		&editxmlmode($request,$file);
+	    } elsif (($env{'form.problemmode'} eq 'editxml') || 
+                     ($env{'form.problemmode'} eq 'saveeditxml') ||
+                     ($env{'form.problemmode'} eq 'saveviewxml') ||
+                     ($env{'form.problemmode'} eq 'undoxml')) {
+                my %editors = &Apache::loncommon::permitted_editors();
+                if (($editors{'xml'}) || ($env{'form.problemmode'} eq 'saveviewxml') || ($env{'form.problemmode'} eq 'undoxml')) {
+		    &editxmlmode($request,$file);
+                } else {
+                    &update_construct_style();
+                    &renderpage($request,$file);
+                }
 	    } elsif ($env{'form.problemmode'} eq 'calcanswers') {
 		&analyze($request,$file);
 	    } else {
@@ -1265,13 +1744,55 @@ 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
+        my ($donebuttonresult,$donemsg,$viewasuser);
+        if ($symb && $env{'form.LC_interval_done'} eq 'true') {  
+            ($donebuttonresult,$donemsg) = &zero_timer($symb);
+            undef($env{'form.LC_interval_done'});
+            undef($env{'form.LC_interval_done_proctorpass'});
+        }
+        if (($env{'form.LC_viewas'} ne '') && $symb && $env{'request.course.id'} &&
+            ($Apache::lonhomework::viewgrades || $Apache::lonhomework::modifygrades)) {
+            if ($env{'form.LC_viewas'} =~ /^($match_username):($match_domain)$/) {
+                my ($possuname,$possudom) = ($1,$2);
+                my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+                my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+                my ($canview,$posssec);
+                if ($env{'request.course.sec'} ne '') {
+                    if ($Apache::lonhomework::modifygradessec eq $env{'request.course.sec'}) {
+                        $canview = 'section';
+                        $posssec = $env{'request.course.sec'};
+                    } elsif ($Apache::lonhomework::viewgradessec eq $env{'request.course.sec'}) {
+                        $canview = 'section';
+                        $posssec = $env{'request.course.sec'};
+                    }
+                }
+                my $crstype = &Apache::loncommon::course_type();
+                if (&Apache::loncourseuser::is_course_user($possudom,$possuname,$cdom,$cnum,
+                                                           $canview,$crstype,$posssec)) {
+                    $viewasuser = $possuname.':'.$possudom;
+                }
+            }
+            undef($env{'form.LC_viewas'});
+        }
 	# just render the page normally outside of construction space
 	&Apache::lonxml::debug("not construct");
-	&renderpage($request,$file);
+	&renderpage($request,$file,undef,undef,$donemsg,$viewasuser,$symb);
+        if (@Apache::lonhomework::ltipassback) {
+            unless ($registered_cleanup) {
+                my $handlers = $request->get_handlers('PerlCleanupHandler');
+                $request->set_handlers('PerlCleanupHandler' =>
+                                       [\&do_ltipassback,@{$handlers}]);
+                $registered_cleanup=1;
+            }
+        }
     }
     #my $td=&tv_interval($t0);
     #&Apache::lonxml::debug("Spent $td seconds processing");
@@ -1282,5 +1803,387 @@ 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(<FH>){
+                $code.= $_ unless $_ =~ /(<problem>)|(<\/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 = '
+<translated>
+    <lang which="en"></lang>
+    <lang which="default"></lang>
+</translated>';
+    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></$tag>";
+}
+
+
+sub helpmenu_datastructure {
+
+	# filename, title, width, height
+	my $helpers = [
+		['Problem_LON-CAPA_Functions.hlp', &mt('Script Functions'), 800, 600],
+		['Greek_Symbols.hlp', &mt('Greek Symbols'), 500, 600],
+ 		['Other_Symbols.hlp', &mt('Other Symbols'), 500, 600],
+		['Authoring_Output_Tags.hlp', &mt('Output Tags'), 800, 600],
+		['Authoring_Multilingual_Problems.hlp', 
+			&mt('How to create problems in different languages'), 800, 600],
+		['loncapa.html', &mt('Language reference'), 800, 600],
+	];
+
+	my $help_structure = [];
+
+	foreach my $count (0..(scalar(@{$helpers})-1)) {
+		my $filename = $helpers->[$count]->[0];
+		my $title = $helpers->[$count]->[1];
+		my $width = $helpers->[$count]->[2];
+                my $height = $helpers->[$count]->[3];
+                if ($width eq '') {
+                    $width = 500;
+                }
+                if ($height eq '') {
+                    $height = 600;
+                }
+		my $href = &HTML::Entities::encode("javascript:openMyModal('/adm/help/$filename',$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|&#39;|\\&#39;|g;
+    return $return;
+}
+
+sub do_ltipassback {
+    if (@Apache::lonhomework::ltipassback) {
+        my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
+        my $ip = &Apache::lonnet::get_host_ip($lonhost);
+        foreach my $item (@Apache::lonhomework::ltipassback) {
+            &Apache::lonhomework::run_passback($item,$lonhost,$ip);
+        }
+        undef(@Apache::lonhomework::ltipassback);
+    }
+    return OK;
+}
+
+sub run_passback {
+    my ($item,$lonhost,$ip) = @_;
+    if (ref($item) eq 'HASH') {
+        if ((ref($item->{'lti'}) eq 'HASH') && ($item->{'cid'} =~ /^($match_domain)_($match_courseid)$/)) {
+            my ($cdom,$cnum) = ($1,$2);
+            my $msgformat = $item->{'lti'}->{'passbackformat'};
+            my $sigmethod = 'HMAC-SHA1';
+            my $ltinum = $item->{'ltinum'};
+            my $id = $item->{'pbid'};
+            my $url = $item->{'pburl'};
+            my $type = $item->{'pbtype'};
+            my $pbscope = $item->{'pbscope'};
+            my $map = $item->{'pbmap'};
+            my $symb = $item->{'pbsymb'};
+            my $uname = $item->{'uname'};
+            my $udom = $item->{'udom'};
+            my $uhome = $item->{'uhome'};
+            my $usec = $item->{'usec'};
+            my $keynum = $item->{'lti'}->{'cipher'};
+            my $crsdef = $item->{'crsdef'};
+            my $scoretype = $item->{'format'};
+            my $scope = $item->{'scope'};
+            my $clientip = $item->{'clientip'};
+            my ($total,$possible,%total_by_symb,%possible_by_symb);
+            if ((exists($item->{'total_s'})) && (ref($item->{'total_s'}) eq 'HASH')) {
+                %total_by_symb = %{$item->{'total_s'}};
+                if ($pbscope eq 'resource') {
+                    if (exists($total_by_symb{$symb})) {
+                        $total = $total_by_symb{$symb};
+                    } else {
+                        $total = $item->{'total'};
+                    }
+                }
+            } elsif ($pbscope eq 'resource') {
+                $total = $item->{'total'};
+            }
+            if ((exists($item->{'possible_s'})) && (ref($item->{'possible_s'}) eq 'HASH')) {
+                %possible_by_symb = %{$item->{'possible_s'}};
+                if ($pbscope eq 'resource') {
+                    if (exists($possible_by_symb{$symb})) {
+                        $possible = $possible_by_symb{$symb};
+                    } else {
+                        $possible = $item->{'possible'};
+                    }
+                }
+            } elsif ($pbscope eq 'resource') {
+                $possible = $item->{'possible'};
+            }
+            if (($pbscope eq 'map') || ($pbscope eq 'nonrec')) {
+                if ((keys(%total_by_symb)) && (keys(%possible_by_symb))) {
+                    ($total,$possible) =
+                    &get_lti_score($uname,$udom,$usec,$map,$pbscope,\%total_by_symb,\%possible_by_symb);
+                } else {
+                   ($total,$possible) = &get_lti_score($uname,$udom,$usec,$map,$pbscope);
+                }
+            } elsif ($pbscope eq 'course') {
+                ($total,$possible) = &get_lti_score($uname,$udom,$usec);
+            }
+            $item->{'total'} = $total;
+            $item->{'possible'} = $possible;
+            if (($id ne '') && ($url ne '') && ($possible)) {
+                my ($sent,$score,$code,$result) =
+                    &LONCAPA::ltiutils::send_grade($cdom,$cnum,$crsdef,$type,$ltinum,$keynum,$id,
+                                                   $url,$scoretype,$sigmethod,$msgformat,$total,$possible);
+                $item->{'score'} = $score;
+                my ($linkprotector,$linkuri,$no_passback,$appname);
+                if ($item->{'linkprot'}) {
+                    ($linkprotector,$linkuri) = split(/:/,$item->{'linkprot'});
+                }
+                if ($sent) {
+                    if ($code == 200) {
+                        if ($item->{'linkprot'}) {
+                            my $skey = join("\0",($linkuri,$linkprotector,$scope));
+                            my $namespace = $cdom.'_'.$cnum.'_lp_passback';
+                            my $store = {
+                                          'score' => $score,
+                                          'ip' => $ip,
+                                          'host' => $Apache::lonnet::perlvar{'lonHostID'},
+                                          'protector' => $linkprotector,
+                                          'deeplink' => $linkuri,
+                                          'scope' => $scope,
+                                          'url' => $url,
+                                          'id' => $id,
+                                          'clientip' => $clientip,
+                                          'whodoneit' => $env{'user.name'}.':'.$env{'user.domain'},
+                                        };
+                            my $value='';
+                            foreach my $key (keys(%{$store})) {
+                                $value.=&escape($key).'='.&Apache::lonnet::freeze_escape($store->{$key}).'&';
+                            }
+                            $value=~s/\&$//;
+                            &Apache::lonnet::courselog(&escape($linkuri).':'.$uname.':'.$udom.':EXPORT:'.$value);
+                            &Apache::lonnet::store_userdata({'score' => $score},$skey,$namespace,$udom,$uname);
+                        }
+                    } else {
+                        if ($item->{'linkprot'}) {
+                           $no_passback = "Passback response was $code ($result).";
+                        }
+                    }
+                } else {
+                    if ($item->{'linkprot'}) {
+                        $no_passback = 'No passback of scores.';
+                    }
+                }
+                if ($no_passback) {
+                    if ($item->{'linkprot'}) {
+                        my ($ltinum,$ltitype) = ($linkprotector =~ /^(\d+)(c|d)$/);
+                        if ($ltitype eq 'c') {
+                            my %lti = &Apache::lonnet::get_course_lti($cnum,$cdom,'provider');
+                            if (ref($lti{$ltinum}) eq 'HASH') {
+                                $appname = $lti{$ltinum}{'name'};
+                            }
+                        } elsif ($ltitype eq 'd') {
+                            my %lti = &Apache::lonnet::get_domain_lti($cdom,'linkprot');
+                            if (ref($lti{$ltinum}) eq 'HASH') {
+                                $appname = $lti{$ltinum}{'name'};
+                            }
+                        }
+                        $no_passback .= " LTI launcher $linkprotector ($appname) for $linkuri (${cdom}_${cnum})";
+                        &Apache::lonnet::logthis($no_passback." for $uname:$udom");
+                        &Apache::lonnet::log($udom,$uname,$uhome,"$no_passback score=$score total=$total poss=$possible");
+                        if ($item->{'linkprot'}) {
+                            my $pendingkey = &Time::HiRes::time().':'.$uname.':'.$udom.':'.
+                                             "$linkuri\0$linkprotector\0$scope";
+                            &Apache::lonnet::put('linkprot_passback_pending',{$pendingkey => $item},$cdom,$cnum);
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+
+sub get_lti_score {
+    my ($uname,$udom,$usec,$mapurl,$pbscope,$totals,$possibles) = @_;
+    my $navmap = Apache::lonnavmaps::navmap->new($uname,$udom,$usec);
+    if (ref($navmap)) {
+        my $iterator;
+        if ($mapurl ne '') {
+            my $map = $navmap->getResourceByUrl($mapurl);
+            my $firstres = $map->map_start();
+            my $finishres = $map->map_finish();
+            my $recursive = 1;
+            if ($pbscope eq 'nonrec') {
+                $recursive = 0;
+            }
+            $iterator = $navmap->getIterator($firstres,$finishres,undef,$recursive);
+        } else {
+            $iterator = $navmap->getIterator(undef,undef,undef,1);
+        }
+        if (ref($iterator)) {
+            my $depth = 1;
+            my $total = 0;
+            my $possible = 0;
+            my (%totals_by_symb,%possibles_by_symb);
+            if (ref($totals) eq 'HASH') {
+                %totals_by_symb = %{$totals};
+            }
+            if (ref($possibles) eq 'HASH') {
+                %possibles_by_symb = %{$possibles};
+            }
+            $iterator->next(); # ignore first BEGIN_MAP
+            my $curRes = $iterator->next();
+            while ( $depth > 0 ) {
+                if ($curRes == $iterator->BEGIN_MAP()) {$depth++;}
+                if ($curRes == $iterator->END_MAP()) { $depth--; }
+                if (ref($curRes) && $curRes->is_gradable() && !$curRes->randomout) {
+                    my $currsymb = $curRes->symb();
+                    if (($currsymb) && (exists($totals_by_symb{$currsymb})) &&
+                        (exists($possibles_by_symb{$currsymb}))) {
+                        $total += $totals_by_symb{$currsymb};
+                        $possible += $possibles_by_symb{$currsymb};
+                    } else {
+                        my $parts = $curRes->parts();
+                        foreach my $part (@{$parts}) {
+                            next if ($curRes->solved($part) eq 'excused');
+                            $total += $curRes->weight($part) * $curRes->awarded($part);
+                            $possible += $curRes->weight($part);
+                        }
+                    }
+                }
+                $curRes = $iterator->next();
+            }
+            if ($total > $possible) {
+                $total = $possible;
+            }
+            return ($total,$possible);
+        }
+    }
+    return;
+}
+
 1;
 __END__