--- loncom/homework/lonhomework.pm	2006/06/08 22:33:57	1.249
+++ 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.249 2006/06/08 22:33:57 albertel Exp $
+# $Id: lonhomework.pm,v 1.392 2025/01/05 21:53:42 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -47,22 +47,53 @@ 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 HTML::Entities();
 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 Apache::lonnet();
+use HTML::Entities();
+use File::Copy();
 
 # FIXME - improve commenting
 
+my $registered_cleanup;
 
 BEGIN {
     &Apache::lonxml::register_insert();
 }
 
 
+=pod
+
+=item set_bubble_lines()
+
+Called at analysis time to set the bubble lines
+hash for the problem.. This should be called in the
+end_problemtype tag in analysis mode.
+
+We fetch the hash of part id counters from lonxml
+    and push them into analyze:{part_id.bubble_lines}.
+
+=cut
+
+sub set_bubble_lines {
+    my %bubble_counters = &Apache::lonxml::get_bubble_line_hash();
+
+    foreach my $key (keys(%bubble_counters)) {
+	$Apache::lonhomework::analyze{"$key.bubble_lines"} =
+	    $bubble_counters{"$key"};
+    }
+}
+
 #
 # Decides what targets to render for.
 # Implicit inputs:
@@ -89,55 +120,73 @@ 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') {
+            if ($env{'form.answer_output_mode'} eq 'tex') {
+                return ($env{'form.grade_target'});
+            }
+        }
 	if ($env{'form.webgrade'} &&
 	    ($Apache::lonhomework::modifygrades eq 'F'
 	     || $Apache::lonhomework::queuegrade eq 'F' )) {
 	    return ('grade','webgrade');
 	}
 	if ( defined($env{'form.submitted'}) &&
-	     ( !defined($env{'form.resetdata'})) &&
 	     ( !defined($env{'form.newrandomization'}))) {
 	    return ('grade', 'web');
 	} else {
 	    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.problemmode'} eq &mt('View') ||
-		 $env{'form.problemmode'} eq &mt('Discard Edits and View')) {
-		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 &mt('Edit') ||
-		      $env{'form.problemmode'} eq 'Edit') {
-		if ( $env{'form.submitted'} eq 'edit' ) {
-		    if ( $env{'form.submit'} eq &mt('Submit Changes and View') ) {
-			return ('modified','web','answer');
-		    } else {
-			return ('modified','no_output_web','edit');
-		    }
-		} else {
+	    if ($env{'form.problemstate'} eq 'WEB_GRADE') {
+		return ('grade','webgrade','answer');
+            } 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 ();
 }
 
@@ -147,16 +196,6 @@ sub setup_vars {
 #  return ';$external::target='.$target.';';
 }
 
-sub createmenu {
-    my ($which,$request)=@_;
-    if ($which eq 'grade') {
-	$request->print('<script 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'});
@@ -167,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, 
@@ -182,75 +221,57 @@ sub proctor_checked_in {
 	    return 1;
 	}
     }
-    
     return 0;
 }
 
-$Apache::lonxml::browse='';
-sub check_ip_acc {
-    my ($acc)=@_;
-    &Apache::lonxml::debug("acc is $acc");
-    if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) { 
-	return 1;
-    }
-    my $allowed=0;
-    my $ip=$ENV{'REMOTE_ADDR'};
-    my $name;
-    foreach my $pattern (split(',',$acc)) {
-	$pattern =~ s/^\s*//;
-	$pattern =~ s/\s*$//;
-	if ($pattern =~ /\*$/) {
-	    #35.8.*
-	    $pattern=~s/\*//;
-	    if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
-	} elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {    
-	    #35.8.3.[34-56]
-	    my $low=$2;
-	    my $high=$3;
-	    $pattern=$1;
-	    if ($ip =~ /^\Q$pattern\E/) { 
-		my $last=(split(/\./,$ip))[3];
-		if ($last <=$high && $last >=$low) { $allowed=1; }
-	    }
-	} elsif ($pattern =~ /^\*/) {
-	    #*.msu.edu
-	    $pattern=~s/\*//;
-	    if (!defined($name)) {
-		use Socket;
-		my $netaddr=inet_aton($ip);
-		($name)=gethostbyaddr($netaddr,AF_INET);
-	    }
-	    if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
-	} elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
-	    #127.0.0.1
-	    if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
-	} else {
-	    #some.name.com
-	    if (!defined($name)) {
-		use Socket;
-		my $netaddr=inet_aton($ip);
-		($name)=gethostbyaddr($netaddr,AF_INET);
-	    }
-	    if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
-	}
-	if ($allowed) { last; }
-    }
-    return $allowed;
-}
-
 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.$id.useslots");
-    if ($useslots ne 'resource' && $useslots ne 'map') {
+    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);
@@ -258,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');
-	}
-    }
-
-    my @slots=
-	(split(':',&Apache::lonnet::EXT("resource.$id.availablestudent")),
-	 split(':',&Apache::lonnet::EXT("resource.$id.available")));
+	    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",$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 &&
-	    &check_ip_acc($slot{'ip'})) {
-	    &Apache::lonxml::debug("$slot is good");
-	    $slotstatus='NEEDS_CHECKIN';
-	    $returned_slot=\%slot;
-	    $slot_name=$slot;
-	    last;
-	}
+        next if ($slot{'endtime'} < $now);
+        $num_usable_slots ++;
+	if ($slot{'starttime'} < $now &&
+	    $slot{'endtime'} > $now &&
+	    &Apache::loncommon::check_ip_acc($slot{'ip'})) {
+            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 = 
@@ -306,11 +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"};
+    } 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)");
@@ -323,31 +427,131 @@ sub check_slot_access {
 	return ('WAITING_FOR_GRADE');
     }
 
+    # Previously used slot is no longer open, and has been checked in for this version.
+    # 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 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 (($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 = '';
@@ -358,8 +562,8 @@ sub check_access {
     if ($env{'request.state'} eq "construct") {
 	if ($env{'form.problemstate'}) {
 	    if ($env{'form.problemstate'} =~ /^CANNOT_ANSWER/) {
-		if ( ! ($env{'form.problemstate'} eq 'CANNOT_ANSWER_correct' &&
-			lc($Apache::lonhomework::problemstatus) eq 'no')) {
+		if ( ! ($env{'form.problemstate'} eq 'CANNOT_ANSWER_correct' 
+			&& &hide_problem_status())) {
 		    return ('CANNOT_ANSWER',
 			    &mt('is in this state due to author settings.'));
 		}
@@ -377,26 +581,34 @@ sub check_access {
     &Apache::lonxml::debug("checking for part :$id:");
     &Apache::lonxml::debug("time:".time);
 
-    my ($symb)=&Apache::lonxml::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 $allowed=&check_ip_acc(&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';
@@ -405,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:");
@@ -418,7 +630,7 @@ sub check_access {
 		$date = &mt("an indeterminate date"); $passed = 0;
 	    } else {
 		if (time < $date) { $passed = 0; } else { $passed = 1; }
-		$date = localtime $date;
+		$date = &Apache::lonlocal::locallocaltime($date);
 	    }
 	    if (!$passed) { $type=$temp; last; }
 	}
@@ -428,43 +640,55 @@ 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/
-	    &&
-	    lc($Apache::lonhomework::problemstatus) ne 'no')
-	   ||
-	   $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'
+	    && &show_answer_problem_status()) {
+	    $status = 'SHOW_ANSWER';
+	}
     }
     if ($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER') {
-	my $interval=&Apache::lonnet::EXT("resource.$id.interval");
-	&Apache::lonxml::debug("looking for interval $interval");
-	if ($interval) {
-	    my $first_access=&Apache::lonnet::get_first_access('map');
+	my @interval=&Apache::lonnet::EXT("resource.$id.interval",$symb);
+	&Apache::lonxml::debug("looking for interval @interval");
+	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);
-		$datemsg=&seconds_to_human_length($due_date-time);
+		my $due_date = &due_date($id,$symb);
+		my $seconds_left = $due_date - time;
+		my ($timelimit) = ($interval[0] =~ /^(\d+)/);
+		if ($seconds_left > $timelimit || $due_date eq '') {
+		    $seconds_left = $timelimit;
+		}
+		$datemsg=&seconds_to_human_length($seconds_left);
 	    }
 	}
     }
@@ -474,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");
@@ -486,24 +709,29 @@ sub check_access {
 }
 # this should work exactly like the copy in lonnavmaps.pm
 sub due_date {
-    my ($part_id,$symb)=@_;
+    my ($part_id,$symb,$udom,$uname)=@_;
     my $date;
-    my $interval= &Apache::lonnet::EXT("resource.$part_id.interval",$symb);
-    &Apache::lonxml::debug("looking for interval $part_id $symb $interval");
-    my $due_date= &Apache::lonnet::EXT("resource.$part_id.duedate",$symb);
+    my @interval= &Apache::lonnet::EXT("resource.$part_id.interval",$symb,
+				       $udom,$uname);
+    &Apache::lonxml::debug("looking for interval $part_id $symb @interval");
+    my $due_date= &Apache::lonnet::EXT("resource.$part_id.duedate",$symb,
+				       $udom,$uname);
     &Apache::lonxml::debug("looking for due_date $part_id $symb $due_date");
-    if (defined($interval)) {
-	my $first_access=&Apache::lonnet::get_first_access('map',$symb);
+    if ($interval[0] =~ /\d+/) {
+	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)) {
-	    $interval = $first_access+$interval;
-	    $date = ($interval < $due_date)? $interval : $due_date;
+	    my ($timelimit) = ($interval[0] =~ /^(\d+)/);
+	    my $interval = $first_access+$timelimit;
+	    $date = (!$due_date || $interval < $due_date) ? $interval
+                                                          : $due_date;
 	} else {
 	    $date = $due_date;
 	}
     } else {
 	$date = $due_date;
     }
-    return $date
+    return $date;
 }
 
 sub seconds_to_human_length {
@@ -552,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 ---- ".
@@ -570,18 +798,24 @@ 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;
 
     if ($Apache::lonhomework::browse eq 'F' && 
 	$env{'form.devalidatecourseresdata'} eq 'on') {
-	my (undef,$courseid) = &Apache::lonxml::whichuser();
+	my (undef,$courseid) = &Apache::lonnet::whichuser();
 	&Apache::lonnet::devalidatecourseresdata($env{"course.$courseid.num"},
 					      $env{"course.$courseid.domain"});
     }
@@ -593,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;
 
@@ -608,6 +845,15 @@ sub setuppermissions {
     return '';
 }
 
+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);
+}
+
 sub setupheader {
     my $request=$_[0];
     &Apache::loncommon::content_type($request,'text/html');
@@ -622,43 +868,79 @@ 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;
-
-    &Apache::lonnet::correct_line_ends($result);
-
-    if ($env{'form.Undo'} eq &mt('undo')) {
+    if (($env{'form.problemmode'} eq 'undo') || ($env{'form.problemmode'} eq 'undoxml')) {
 	my $error=0;
-	if (!copy($file,$filetmp)) { $error=1; }
-	if ((!$error) && (!copy($filebak,$file))) { $error=1; }
-	if ((!$error) && (!move($filetmp,$filebak))) { $error=1; }
+	if (!&File::Copy::copy($file,$filetmp)) { $error=1; }
+	if ((!$error) && (!&File::Copy::copy($filebak,$file))) { $error=1; }
+	if ((!$error) && (!&File::Copy::move($filetmp,$filebak))) { $error=1; }
 	if (!$error) {
-	    &Apache::lonxml::info("<p><b>".&mt("Undid changes, Switched")." $filebak ".&mt("and")." $file</b></p>");
+	    &Apache::lonxml::info("<p><b>".
+				  &mt("Undid changes, Switched [_1] and [_2]",
+				      '<span class="LC_filename">'.$filebak.
+				      '</span>',
+				      '<span class="LC_filename">'.$file.
+				      '</span>')."</b></p>");
 	} else {
-	    &Apache::lonxml::info("<p><font color=\"red\" size=\"+1\"><b>".&mt("Unable to undo, unable to switch")." $filebak ".&mt("and")." $file</b></font></p>");
+	    &Apache::lonxml::info("<p><span class=\"LC_error\">".
+				  &mt("Unable to undo, unable to switch [_1] and [_2]",
+				      '<span class="LC_filename">'.
+				      $filebak.'</span>',
+				      '<span class="LC_filename">'.
+				      $file.'</span>')."</span></p>");
 	    $error=1;
 	}
     } else {
+        &Apache::lonnet::correct_line_ends($result);
+
 	my $fs=Apache::File->new(">$filebak");
 	if (defined($fs)) {
 	    print $fs $$problem;
-	    &Apache::lonxml::info("<b>".&mt("Making Backup to").
-				  " $filebak</b>");
 	} else {
-	    &Apache::lonxml::info("<font color=\"red\" size=\"+1\"><b>".&mt("Unable to make backup")." $filebak</b></font>");
+	    &Apache::lonxml::info("<span class=\"LC_error\">".
+				  &mt("Unable to make backup [_1]",
+				      '<span class="LC_filename">'.
+				      $filebak.'</span>')."</span>");
 	    $error=2;
 	}
 	my $fh=Apache::File->new(">$file");
 	if (defined($fh)) {
 	    print $fh $$result;
-	    &Apache::lonxml::info("<b>".&mt("Saving Modifications to").
-				  " $file</b>");
+            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("<font color=\"red\" size=\"+1\"><b>".
-				  &mt("Unable to write to")." $file</b></font>");
+	    &Apache::lonxml::info('<span class="LC_error">'.
+				  &mt("Unable to write to [_1]",
+				      '<span class="LC_filename">'.
+				      $file.'</span>').
+				  '</span>');
 	    $error|=4;
 	}
     }
@@ -667,20 +949,57 @@ sub handle_save_or_undo {
 
 sub analyze_header {
     my ($request) = @_;
-    my $result =
-	&Apache::loncommon::start_page('Analyzing a problem',undef);
+    my $js = &Apache::lonxml::setmode_javascript();
+
+    # Breadcrumbs
+    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' => '',
+                   'text' => 'Analyzing a problem'}];
 
+    my $result =
+        &Apache::loncommon::start_page('Analyzing a problem',
+                                       $js,
+                                       {'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().'
-            <input type="submit" name="problemmode" value="'.&mt("EditXML").'" />
-            <input type="submit" name="problemmode" value="'.&mt('Edit').'" />
-            <hr />
-            <input type="submit" name="submit" value="'.&mt("View").'" />
+            <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 />
-            </form>';
+            </div>'
+            .&Apache::lonxml::message_location().
+            '</form>';
     &Apache::lonxml::add_messages(\$result);
     $request->print($result);
     $request->rflush();
@@ -702,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,
@@ -717,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) {
@@ -725,43 +1040,89 @@ sub analyze {
 	    if ($analyze{$part.'.type'} eq 'numericalresponse'	||
 		$analyze{$part.'.type'} eq 'stringresponse'	||
 		$analyze{$part.'.type'} eq 'formularesponse'   ) {
-		my $concatanswer=join("\0",@{ $analyze{$part.'.answer'} });
-		if (($concatanswer eq '') || ($concatanswer=~/^\@/)) {
-		    @{$analyze{$part.'.answer'}}=('<font color="red">'.&mt('Error').'</font>');
+		foreach my $name (keys(%{ $analyze{$part.'.answer'} })) {
+		    my $i=0;
+		    foreach my $answer_part (@{ $analyze{$part.'.answer'}{$name} }) {
+			push( @{ $overall{$part.'.answer'}[$i] },
+			      $answer_part);
+			my $concatanswer= join("\0",@{ $answer_part });
+			if (($concatanswer eq '') || ($concatanswer=~/^\@/)) {
+			    $answer_part = ['<span class="LC_error">'.&mt('Error').'</span>'];
+			}
+			$seedexample{join("\0",$part,$i,@{$answer_part})}=
+			    $thisseed;
+			$i++;
+		    }
+		}
+		if (!keys(%{ $analyze{$part.'.answer'} })) {
+		    my $answer_part = 
+			['<span class="LC_error">'.&mt('Error').'</span>'];
+		    $seedexample{join("\0",$part,0,@{$answer_part})}=
+			$thisseed;
+		    push( @{ $overall{$part.'.answer'}[0] },
+			  $answer_part);
 		}
-		push( @{ $overall{$part.'.answer'} },
-		      [@{ $analyze{$part.'.answer'} }]);
-                $seedexample{join("\0",@{ $analyze{$part.'.answer'}})}=$thisseed;
 	    }
 	}
     }
-    &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state,
-					  &mt('Analyzing Results'));
-    $request->print('<hr />'.&mt('List of possible answers').': ');
+    &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'} })) {
-	    my $num_cols=scalar(@{ $overall{$part.'.answer'}->[0] });
-	    $request->print('<table class="thinborder"><tr><th colspan="'.($num_cols+1).'">'.&mt('Part').' '.$part.'</th></tr>');
-	    my %frequency;
-	    foreach my $answer (sort {$a->[0] <=> $b->[0]} (@{ $overall{$part.'.answer'} })) {
-		$frequency{join("\0",@{ $answer })}++;
-	    }
-	    $request->print('<tr><th colspan="'.($num_cols).'">'.&mt('Answer').'</th><th>'.&mt('Frequency').'<br />('
-			    .&mt('click for example').')</th></tr>');
-	    foreach my $answer (sort {(split("\0",$a))[0] <=> (split("\0",$b))[0]} (keys(%frequency))) {
-		$request->print('<tr><td class="center">'.
-				join('</td><td class="center">',split("\0",$answer)).
-				'</td><td class="center"><a href="'.$request->uri.'?rndseed='.$seedexample{$answer}.'">'.$frequency{$answer}.
-				'</a></td></tr>');
+        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()
+                               .&Apache::loncommon::start_data_table_header_row()
+                               .'<th colspan="'.($num_cols+1).'">'
+                               .&mt('Part').' '.$part
+                );
+		if (scalar(@{ $overall{$part.'.answer'} }) > 1) {
+		    $request->print(' '.&mt('Answer [_1]',$i+1));
+		}
+		$request->print('</th>'
+                               .&Apache::loncommon::end_data_table_header_row()
+                );
+		my %frequency;
+		foreach my $answer (sort {$a->[0] <=> $b->[0]} (@{ $overall{$part.'.answer'}[$i] })) {
+		    $frequency{join("\0",@{ $answer })}++;
+		}
+                $request->print(&Apache::loncommon::start_data_table_header_row()
+                               .'<th colspan="'.($num_cols).'">'.&mt('Answer').'</th>'
+                               .'<th>'.&mt('Frequency').'<br />'
+                               .'('.&mt('click for example').')</th>'
+                               .&Apache::loncommon::end_data_table_header_row()
+                );
+		foreach my $answer (sort {(split("\0",$a))[0] <=> (split("\0",$b))[0]} (keys(%frequency))) {
+                    $request->print(&Apache::loncommon::start_data_table_row()
+                                   .'<td>'
+                                   .join('</td><td>',split("\0",$answer))
+				   .'</td>'
+                                   .'<td>'
+                                   .'<a href="'.$request->uri.'?rndseed='.$seedexample{join("\0",$part,$i,$answer)}.'">'.$frequency{$answer}.'</a>'
+				   .'</td>'
+                                   .&Apache::loncommon::end_data_table_row()
+                    );
+		}
+                $request->print(&Apache::loncommon::end_data_table());
 	    }
-	    $request->print('</table>');
 	} else {
-	    $request->print('<p>'.&mt('Response').' '.$part.' '.
-			    &mt('is not analyzable at this time').'</p>');
+            $request->print('<p class="LC_warning">'
+                           .&mt('Response [_1] is not analyzable at this time.',$part)
+			   .'</p>'
+            );
 	}
     }
     if (scalar(keys(%allparts)) == 0 ) {
-	$request->print('<p>'.&mt('Found no analyzable responses in this problem, currently only Numerical, Formula and String response styles are supported.').'</p>');
+        $request->print('<p class="LC_warning">'
+                       .&mt('Found no analyzable responses in this problem.'
+                           .' Currently only Numerical, Formula and String response styles are supported.')
+                       .'</p>'
+        );
     }
     &Apache::lonhtmlcommon::Close_PrgWin($request,\%prog_state);
     &analyze_footer($request);
@@ -769,62 +1130,193 @@ sub analyze {
     return $result;
 }
 
+{
+    my $show_problem_status;
+    sub reset_show_problem_status {
+	undef($show_problem_status);
+    }
+
+    sub set_show_problem_status {
+	my ($new_status) = @_;
+	$show_problem_status = lc($new_status);
+    }
+
+    sub hide_problem_status {
+	return ($show_problem_status eq 'no'
+		|| $show_problem_status eq 'no_feedback_ever');
+    }
+
+    sub show_problem_status {
+	return ($show_problem_status eq 'yes'
+		|| $show_problem_status eq 'answer'
+		|| $show_problem_status eq '');
+    }
+    
+    sub show_some_problem_status {
+	return ($show_problem_status eq 'no');
+    }
+
+    sub show_no_problem_status {
+	return ($show_problem_status eq 'no_feedback_ever');
+    }
+  
+    sub show_answer_problem_status {
+	return ($show_problem_status eq 'answer');
+    }
+}
+
 sub editxmlmode {
     my ($request,$file) = @_;
     my $result;
     my $problem=&Apache::lonnet::getfile($file);
     if ($problem eq -1) {
-	&Apache::lonxml::error("<b> ".&mt('Unable to find').
-			       " <i>$file</i></b>");
+	&Apache::lonxml::error(
+            '<p class="LC_error">'
+           .&mt('Unable to find [_1]',
+                '<span class="LC_filename">'.$file.'</span>')
+           .'</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.submit'} eq &mt('Submit Changes and View') ) {
+    if ($env{'form.problemmode'} eq 'saveviewxml') {
 	&Apache::lonhomework::showhashsubset(\%env,'^form');
-	$env{'form.problemmode'}='View';
+	$env{'form.problemmode'}='view';
 	&renderpage($request,$file);
     } else {
 	my ($rows,$cols) = &Apache::edit::textarea_sizes(\$problem);
-	my $xml_help = '<table><tr><td>'.
-	    &Apache::loncommon::helpLatexCheatsheet("Problem_Editor_XML_Index",
-						    "Problem Editing Help").
-						    '</td><td>'.
-       &Apache::loncommon::help_open_menu('',undef,undef,undef,5,'Authoring').
-                '</td></tr></table>';
 	if ($cols > 80) { $cols = 80; }
 	if ($cols < 70) { $cols = 70; }
 	if ($rows < 20) { $rows = 20; }
-	my $start_page = 
-	    &Apache::loncommon::start_page("EditXML $file",
-					   &Apache::edit::js_change_detection());
+	my $js =
+	    &Apache::edit::js_change_detection(). 
+	    &Apache::loncommon::resize_textarea_js().
+            &Apache::lonxml::setmode_javascript().
+            &Apache::lonhtmlcommon::dragmath_js("EditMathPopup");
+
+    # Breadcrumbs
+    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'}];
 
-	$result.=$start_page.
-	    &renderpage($request,$file,['no_output_web'],1).
-	    &Apache::lonxml::message_location().'
-            <form '.&Apache::edit::form_change_detection().' name="lonhomework" method="POST" action="'.
+	my $start_page = 
+	    &Apache::loncommon::start_page(&mt("EditXML [_1]",$file),$js,
+					   {'no_auto_mt_title' => 1,
+					    '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(
+                &Apache::loncommon::CSTR_pageheader());
+	$result.=&renderpage($request,$file,['no_output_web'],1).
+            '<form '.&Apache::edit::form_change_detection().' name="lonhomework" method="post" action="'.
 	    &HTML::Entities::encode($env{'request.uri'},'<>&"').'">'.
 	    &Apache::structuretags::remember_problem_state().'
-            <input type="hidden" name="problemmode" value="'.&mt('EditXML').'" />
-            <input type="submit" name="problemmode" accesskey="d" value="'.&mt('Discard Edits and View').'" />
-            <input type="submit" '.&Apache::edit::submit_ask_anyway().'name="problemmode" accesskey="e" value="'.&mt('Edit').'" />
-            <input type="submit" name="Undo" accesskey="u" value="'.&mt('undo').'" />
-            <hr />
-            <input type="submit" name="submit" accesskey="s" value="'.&mt('Submit Changes').'" />
-            <input type="submit" name="submit" accesskey="v" value="'.&mt('Submit Changes and View').'" />
-            <hr />
-            ' . $xml_help . '
-            <textarea '.&Apache::edit::element_change_detection().' style="width:100%" rows="'.$rows.'" cols="'.$cols.'" name="editxmltext">'.
-	    &HTML::Entities::encode($problem,'<>&"').'</textarea><br />
-            <input type="submit" name="submit" accesskey="s" value="'.&mt('Submit Changes').'" />
-            <input type="submit" name="submit" accesskey="v" value="'.&mt('Submit Changes and View').'" />
-            </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 '';
 }
@@ -833,11 +1325,12 @@ 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.');
     &Apache::lonxml::debug("Running targets ".join(':',@targets));
+
     my $overall_result;
     foreach my $target (@targets) {
 	# FIXME need to do something intelligent when a problem goes
@@ -855,9 +1348,17 @@ sub renderpage {
 	my $problem=&Apache::lonnet::getfile($file);
 	my $result;
 	if ($problem eq -1) {
-	    my $filename=(split('/',$file))[-1];
-	    $result.="<b> ".&mt('Unable to find')." <i>$filename</i></b>";
 	    $problem='';
+	    my $filename=(split('/',$file))[-1];
+	    my $error =
+		'<p class="LC_error">'
+               .&mt('Unable to find [_1]',
+			   '<span class="LC_filename">'.$filename.'</span>')
+		."</p>";
+	    $result.=
+		&Apache::loncommon::simple_error_page($request,'Not available',
+						      $error,{'no_auto_mt_msg' => 1});
+	    return;
 	}
 
 	my %mystyle;
@@ -865,10 +1366,14 @@ 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);
-	undef($Apache::lonhomework::parsing_a_problem);
+	&finished_parsing();
 	if (!$output) { $result = ''; }
 	#$request->print("Result follows:");
 	if ($target eq 'modified') {
@@ -877,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>::;
@@ -885,135 +1395,328 @@ 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;
     }
 }
 
-# with no arg it returns a HTML <option> list of the template titles
-# with one arg it returns the filename associated with the arg passed
-sub get_template_list {
-    my ($namewanted,$extension) = @_;
+sub finished_parsing {
+    undef($Apache::lonhomework::parsing_a_problem);
+    undef($Apache::lonhomework::parsing_a_task);
+}
+
+# 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;
     &Apache::lonxml::debug("Looking for :$extension:");
-    foreach my $file (</home/httpd/html/res/adm/includes/templates/*.$extension>) {
-	my $name=&Apache::lonnet::metadata($file,'title');
-	if ($namewanted && ($name eq $namewanted)) {
-	    $result=$file;
-	    last;
-	} else {
-	    if ($name) { push (@allnames, $name); }
-	}
+    my $glob_extension  = $extension;
+    if ($extension eq 'survey' || $extension eq 'exam') {
+	$glob_extension = 'problem';
+    }
+    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/);
+        $numfiles ++;
+    }
+    if ($numfiles > 0) {
+        $result = '<div class="LC_left_float">';
+        $midpoint = int($numfiles/2);
+        if ($numfiles%2) {
+            $midpoint ++;
+        }
+    }
+    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) {
+           $currentcategory=$file->[2];
+           if ((!$seconddiv) && ($count >= $midpoint)) {
+               $result .= '</div></div>'."\n".'<div class="LC_left_float">'."\n";
+               $seconddiv = 1;
+           } elsif (!$first) {
+               $result.='</div>'."\n";
+           } else {
+               $first = 0;
+           }
+           $result.= '<div class="LC_Box">'."\n"
+                    .'<h3 class="LC_hcell">'.$currentcategory.'</h3>'."\n";
+           $count++;
+        }
+	$result .=
+	    '<label><input type="radio" name="template" value="'.$file->[0].'" />'.
+	    $file->[1].'</label>';
+        if ($file->[3]) {
+           $result.=&Apache::loncommon::help_open_topic($file->[3]);
+        }
+        # Provide example link
+        my $filename=$file->[0];
+        $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 (@allnames && !$result) {
-	$result="<option>".&mt("Select a")." $extension ".&mt('template')."</option>\n<option>".
-	    join('</option><option>',sort(@allnames)).'</option>';
+    if ($numfiles > 0) {
+        $result .= '</div></div>'."\n".'<div class="LC_clear_float_footer"></div>'."\n";
     }
     return $result;
 }
 
 sub newproblem {
     my ($request) = @_;
-    my $extension=$request->uri;
-    $extension=~s:^.*\.([\w]+)$:$1:;
+
+    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;
+        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);
-    if ($env{'form.template'} &&
-	$env{'form.template'} ne "Select a $extension template") {
-	use File::Copy;
-	my $file = &get_template_list($env{'form.template'},$extension);
-	my $dest = &Apache::lonnet::filelocation("",$request->uri);
-	copy($file,$dest);
-	&renderpage($request,$dest);
-    } elsif($env{'form.newfile'} && !$templatelist) {
-	# I don't like hard-coded filenames but for now, this will work.
-	use File::Copy;
+    my $templatelist=&get_template_html($extension);
+    if ($env{'form.newfile'} && !$templatelist) {
+	# no templates found
 	my $templatefilename =
-	    $request->dir_config('lonIncludes').'/templates/blank.problem';
+	    $request->dir_config('lonIncludes').'/templates/blank.'.$extension;
 	&Apache::lonxml::debug("$templatefilename");
 	my $dest = &Apache::lonnet::filelocation("",$request->uri);
-	copy($templatefilename,$dest);
+	&File::Copy::copy($templatefilename,$dest);
 	&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;
-	if ($env{'form.newfile'}) {
-	    $errormsg='<p><font color="red">'.&mt('You did not select a template.').'</font></p>'."\n";
-	}
 	my $instructions;
+        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 = 
-	    &Apache::loncommon::start_page("Create New $extension");
-	if ($templatelist) { $instructions=&mt(", select a template from the pull-down menu below.").'<br />'.&mt("Then");}
-	my %lt=&Apache::lonlocal::texthash( 'create' => 'Creating a new',
-			  'resource' => 'resource',
-			  'requested' => 'The requested file',
-			  'not exist' => 'currently does not exist',
-			  'createnew' => 'To create a new',
-			  'click' => 'click on the',
-			  'Create' => 'Create',
-			  'button' => 'button');
-	$request->print(<<ENDNEWPROBLEM);
-$start_page
-<h1>$lt{'create'} $extension $lt{'resource'}</h1>
+            &Apache::loncommon::start_page("Create New $extension",
+                                           undef,
+                                           {'bread_crumbs' => $brcrum,});
+	$request->print(
+        $start_page
+       .&Apache::loncommon::head_subbox(
+                &Apache::loncommon::CSTR_pageheader())
+       .'<h1>'.&mt("Creating a new $extension resource.")."</h1>
 $errormsg
-$lt{'requested'} <tt>$shownurl</tt> $lt{'not exist'}.
-<p>
-<b>$lt{'createnew'} $extension$instructions $lt{'click'} "$lt{'Create'} $extension" $lt{'button'}.</b>
-</p>
-<p><form action="$url" method="POST">
-ENDNEWPROBLEM
+".&mt("The requested file [_1] currently does not exist.",
+      '<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.").'
+</p><div><form action="'.$url.'" method="post">');
+
 	if (defined($templatelist)) {
-	    $request->print("<select name=\"template\">$templatelist</select>");
+	    $request->print($templatelist);
+	}
+	$request->print('<br /><input type="submit" name="newfile" value="'.
+			&mt("Create $extension").'" />');
+	$request->print('</form></div>'.&Apache::loncommon::end_page());
+    }
+    return;
+}
+
+sub update_construct_style {
+    if ($env{'request.state'} eq "construct"
+	&& $env{'form.problemmode'} eq 'view' 
+	&&  defined($env{'form.submitted'})
+	&& !defined($env{'form.resetdata'})
+	&& !defined($env{'form.newrandomization'})) {
+	if ((!$env{'form.style_file'} && $env{'construct.style'})
+	    ||$env{'form.clear_style_file'}) {
+	    &Apache::lonnet::delenv('construct.style');
+	} elsif ($env{'form.style_file'} 
+	    && $env{'construct.style'} ne $env{'form.style_file'}) {
+	    &Apache::lonnet::appenv({'construct.style' => 
+				        $env{'form.style_file'}});
 	}
-	$request->print("<br /><input type=\"submit\" name=\"newfile\" value=\"".&mt('Create')." $extension\" />");
-	$request->print("</form></p>".&Apache::loncommon::end_page());
     }
-    return '';
+}
+
+#
+# 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();
-    # some times multiple problemmodes are submitted, need to select
-    # the last one
-    if ( defined($env{'form.problemmode'}) && ref($env{'form.problemmode'}) ) {
-	my $mode=$env{'form.problemmode'}->[-1];
-	undef $env{'form.problemmode'};
-	$env{'form.problemmode'}=$mode;
-    }
+
+# -------------------------------------- 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
-	    $request->internal_redirect('/adm/ambiguous'); return OK;
+	    &unset_permissions();
+	    $request->internal_redirect('/adm/ambiguous');
+	    return OK;
 	}
     }
-    if (&setupheader($request)) { return OK; }
+    if (&setupheader($request)) {
+	&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::lonxml::whichuser();
+    my ($symb) = &Apache::lonnet::whichuser();
     &Apache::lonxml::debug('symb is '.$symb);
     if ($env{'request.state'} eq "construct") {
 	if ( -e $file ) {
@@ -1021,31 +1724,465 @@ sub handler {
 						    ['problemmode']);
 	    if (!(defined $env{'form.problemmode'})) {
 		#first visit to problem in construction space
-		$env{'form.problemmode'}='View';
+		$env{'form.problemmode'}= 'view';
 		&renderpage($request,$file);
-	    } elsif ($env{'form.problemmode'} eq &mt('EditXML') ||
-		     $env{'form.problemmode'} eq 'EditXML') {
-		&editxmlmode($request,$file);
-	    } elsif ($env{'form.problemmode'} eq &mt('Calculate answers')) {
+	    } 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 {
+		&update_construct_style();
 		&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");
     # always turn off debug messages
     $Apache::lonxml::debug=0;
+    &unset_permissions();
+    return OK;
+
+}
+
+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;