--- loncom/interface/loncommon.pm	2006/10/03 20:14:35	1.461
+++ loncom/interface/loncommon.pm	2007/03/02 23:17:58	1.511
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.461 2006/10/03 20:14:35 albertel Exp $
+# $Id: loncommon.pm,v 1.511 2007/03/02 23:17:58 albertel Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -59,13 +59,14 @@ use Apache::lonnet;
 use GDBM_File;
 use POSIX qw(strftime mktime);
 use Apache::lonmenu();
+use Apache::lonenc();
 use Apache::lonlocal;
 use HTML::Entities;
 use Apache::lonhtmlcommon();
 use Apache::loncoursedata();
 use Apache::lontexconvert();
 use Apache::lonclonecourse();
-use LONCAPA;
+use LONCAPA qw(:DEFAULT :match);
 
 my $readit;
 
@@ -157,7 +158,7 @@ BEGIN {
     opendir(DIR,$designdir);
     while ($filename=readdir(DIR)) {
 	if ($filename!~/\.tab$/) { next; }
-	my ($domain)=($filename=~/^(\w+)\./);
+	my ($domain)=($filename=~/^($match_domain)\./);
 	{
 	    my $designfile = $designdir.'/'.$filename;
 	    if ( open (my $fh,"<$designfile") ) {
@@ -386,20 +387,27 @@ sub selectstudent_link {
 }
 
 sub coursebrowser_javascript {
-    my ($domainfilter)=@_;
+    my ($domainfilter,$sec_element,$formname)=@_;
     my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Group - for which you wish to add/modify a user role');
-   return (<<ENDSTDBRW);
+   my $output = '
 <script type="text/javascript" language="Javascript" >
-    var stdeditbrowser;
+    var stdeditbrowser;'."\n";
+   $output .= <<"ENDSTDBRW";
     function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,crstype) {
         var url = '/adm/pickcourse?';
-        var filter;
-        if (filter != null) {
-           if (filter != '') {
-               url += 'filter='+filter+'&';
-	   }
+        var domainfilter = '';
+        var formid = getFormIdByName(formname);
+        if (formid > -1) {
+            var domid = getIndexByName(formid,udom);
+            if (domid > -1) {
+                if (document.forms[formid].elements[domid].type == 'select-one') {
+                    domainfilter=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
+                }
+                if (document.forms[formid].elements[domid].type == 'hidden') {
+                    domainfilter=document.forms[formid].elements[domid].value;
+                }
+            }
         }
-        var domainfilter='$domainfilter';
         if (domainfilter != null) {
            if (domainfilter != '') {
                url += 'domainfilter='+domainfilter+'&';
@@ -408,11 +416,18 @@ sub coursebrowser_javascript {
         url += 'form=' + formname + '&cnumelement='+uname+
 	                            '&cdomelement='+udom+
                                     '&cnameelement='+desc;
-        if (extra_element !=null && extra_element != '' && formname == 'rolechoice') {
-            url += '&roleelement='+extra_element;
-            if (domainfilter == null || domainfilter == '') {
-                url += '&domainfilter='+extra_element;
+        if (extra_element !=null && extra_element != '') {
+            if (formname == 'rolechoice') {
+                url += '&roleelement='+extra_element;
+                if (domainfilter == null || domainfilter == '') {
+                    url += '&domainfilter='+extra_element;
+                }
             }
+            else {
+                if (formname == 'portform') {
+                    url += '&setroles='+extra_element;
+                }
+            }     
         }
         if (multflag !=null && multflag != '') {
             url += '&multiple='+multflag;
@@ -435,14 +450,74 @@ sub coursebrowser_javascript {
         stdeditbrowser = open(url,title,options,'1');
         stdeditbrowser.focus();
     }
-</script>
+
+    function getFormIdByName(formname) {
+        for (var i=0;i<document.forms.length;i++) {
+            if (document.forms[i].name == formname) {
+                return i;
+            }
+        }
+        return -1; 
+    }
+
+    function getIndexByName(formid,item) {
+        for (var i=0;i<document.forms[formid].elements.length;i++) {
+            if (document.forms[formid].elements[i].name == item) {
+                return i;
+            }
+        }
+        return -1;
+    }
 ENDSTDBRW
+    if ($sec_element ne '') {
+        $output .= &setsec_javascript($sec_element,$formname);
+    }
+    $output .= '
+</script>';
+    return $output;
+}
+
+sub setsec_javascript {
+    my ($sec_element,$formname) = @_;
+    my $setsections = qq|
+function setSect(sectionlist) {
+    var sectionsArray = sectionlist.split(",");
+    var numSections = sectionsArray.length;
+    document.$formname.$sec_element.length = 0;
+    if (numSections == 0) {
+        document.$formname.$sec_element.multiple=false;
+        document.$formname.$sec_element.size=1;
+        document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
+    } else {
+        if (numSections == 1) {
+            document.$formname.$sec_element.multiple=false;
+            document.$formname.$sec_element.size=1;
+            document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
+            document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
+            document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
+        } else {
+            for (var i=0; i<numSections; i++) {
+                document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
+            }
+            document.$formname.$sec_element.multiple=true
+            if (numSections < 3) {
+                document.$formname.$sec_element.size=numSections;
+            } else {
+                document.$formname.$sec_element.size=3;
+            }
+            document.$formname.$sec_element.options[0].selected = false
+        }
+    }
 }
+|;
+    return $setsections;
+}
+
 
 sub selectcourse_link {
    my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype)=@_;
-    return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele.
-        '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select [_1]',$selecttype)."</a>";
+   return "<a href='".'javascript:opencrsbrowser("'.$form.'","'.$unameele.
+        '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course')."</a>";
 }
 
 sub check_uncheck_jscript {
@@ -1240,11 +1315,11 @@ Returns a string containing a <select> e
 
 Args:
   $name - name of the <select> element
-  $value - sclara or array ref of values that should already be selected
+  $value - scalar or array ref of values that should already be selected
   $size - number of rows long the select element is
   $hash - the elements should be 'option' => 'shown text'
           (shown text should already have been &mt())
-  $order - (optional) array ref of the order to show the elments in
+  $order - (optional) array ref of the order to show the elements in
 
 =cut
 
@@ -1260,8 +1335,16 @@ sub multiple_select_form {
         }
     }
     $output.="\n<select name='$name' size='$size' multiple='1'>";
-    my @order = ref($order) ? @$order
-                            : sort(keys(%$hash));
+    my @order;
+    if (ref($order) eq 'ARRAY')  {
+        @order = @{$order};
+    } else {
+        @order = sort(keys(%$hash));
+    }
+    if (exists($$hash{'select_form_order'})) {
+        @order = @{$$hash{'select_form_order'}};
+    }
+        
     foreach my $key (@order) {
         $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
         $output.='selected="selected" ' if ($selected{$key});
@@ -1303,6 +1386,24 @@ sub select_form {
     return $selectform;
 }
 
+# For display filters
+
+sub display_filter {
+    if (!$env{'form.show'}) { $env{'form.show'}=10; }
+    if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
+    return '<nobr><label>'.&mt('Records [_1]',
+			       &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
+							   (&mt('all'),10,20,50,100,1000,10000))).
+	   '</label></nobr> <nobr>'.
+           &mt('Filter [_1]',
+	   &select_form($env{'form.displayfilter'},
+			'displayfilter',
+			('currentfolder' => 'Current folder/page',
+			 'containing' => 'Containing phrase',
+			 'none' => 'None'))).
+			 '<input type="text" name="containingphrase" size="30" value="'.&HTML::Entities::encode($env{'form.containingphrase'}).'" /></nobr>';
+}
+
 sub gradeleveldescription {
     my $gradelevel=shift;
     my %gradelevels=(0 => 'Not specified',
@@ -1388,7 +1489,7 @@ sub get_library_servers {
     my %library_servers;
     foreach my $hostid (keys(%Apache::lonnet::libserv)) {
         if ($Apache::lonnet::hostdom{$hostid} eq $domain) {
-            $library_servers{$hostid} = $Apache::lonnet::hostname{$hostid};
+            $library_servers{$hostid} = &Apache::lonnet::hostname($hostid);
         }
     }
     return %library_servers;
@@ -2067,6 +2168,27 @@ sub getnames {
     }
 }
 
+sub getemails {
+    my ($uname,$udom)=@_;
+    if ($udom eq 'public' && $uname eq 'public') {
+	return;
+    }
+    if (!$udom) { $udom=$env{'user.domain'}; }
+    if (!$uname) { $uname=$env{'user.name'}; }
+    my $id=$uname.':'.$udom;
+    my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
+    if ($cached) {
+	return %{$names};
+    } else {
+	my %loadnames=&Apache::lonnet::get('environment',
+                    			   ['notification','critnotification',
+					    'permanentemail'],
+					   $udom,$uname);
+	&Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
+	return %loadnames;
+    }
+}
+
 # ------------------------------------------------------------------ Screenname
 
 =pod
@@ -2145,6 +2267,19 @@ sub track_student_link {
 	&help_open_topic('View_recent_activity');
 }
 
+# ===================================================== Display a student photo
+
+
+sub student_image_tag {
+    my ($domain,$user)=@_;
+    my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
+    if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
+	return '<img src="'.$imgsrc.'" align="right" />';
+    } else {
+	return '';
+    }
+}
+
 =pod
 
 =back
@@ -2577,7 +2712,9 @@ sub get_student_answers {
   }
   $moreenv{'grade_target'}='answer';
   %moreenv=(%form,%moreenv);
-  my $userview=&Apache::lonnet::ssi('/res/'.$feedurl,%moreenv);
+  $feedurl = &Apache::lonnet::clutter($feedurl);
+  &Apache::lonenc::check_encrypt(\$feedurl);
+  my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
   return $userview;
 }
 
@@ -2596,7 +2733,7 @@ sub submlink {
     my ($text,$uname,$udom,$symb,$target)=@_;
     if (!($uname && $udom)) {
 	(my $cursymb, my $courseid,$udom,$uname)=
-	    &Apache::lonxml::whichuser($symb);
+	    &Apache::lonnet::whichuser($symb);
 	if (!$symb) { $symb=$cursymb; }
     }
     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
@@ -2642,7 +2779,7 @@ sub pprmlink {
     my ($text,$uname,$udom,$symb,$target)=@_;
     if (!($uname && $udom)) {
 	(my $cursymb, my $courseid,$udom,$uname)=
-	    &Apache::lonxml::whichuser($symb);
+	    &Apache::lonnet::whichuser($symb);
 	if (!$symb) { $symb=$cursymb; }
     }
     if (!$symb) { $symb=&Apache::lonnet::symbread(); }
@@ -2691,31 +2828,376 @@ sub maketime {
 #########################################
 
 sub findallcourses {
-    my ($roles) = @_;
+    my ($roles,$uname,$udom) = @_;
     my %roles;
     if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
     my %courses;
     my $now=time;
-    foreach my $key (keys(%env)) {
-	if ( $key=~m{^user\.role\.(\w+)\./(\w+)/(\w+)} ) {
-	    my ($role,$domain,$id) = ($1,$2,$3);
-	    next if ($role eq 'ca' || $role eq 'aa');
-	    next if (%roles && !exists($roles{$role}));
-	    my ($starttime,$endtime)=split(/\./,$env{$key});
-            my $active=1;
-            if ($starttime) {
-		if ($now<$starttime) { $active=0; }
+    if (!defined($uname)) {
+        $uname = $env{'user.name'};
+    }
+    if (!defined($udom)) {
+        $udom = $env{'user.domain'};
+    }
+    if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
+        my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
+        if (!%roles) {
+            %roles = (
+                       cc => 1,
+                       in => 1,
+                       ep => 1,
+                       ta => 1,
+                       cr => 1,
+                       st => 1,
+             );
+        }
+        foreach my $entry (keys(%roleshash)) {
+            my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
+            if ($trole =~ /^cr/) { 
+                next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
+            } else {
+                next if (!exists($roles{$trole}));
+            }
+            if ($tend) {
+                next if ($tend < $now);
+            }
+            if ($tstart) {
+                next if ($tstart > $now);
+            }
+            my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role,$realsec);
+            (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
+            if ($secpart eq '') {
+                ($cnum,$role) = split(/_/,$cnumpart); 
+                $sec = 'none';
+                $realsec = '';
+            } else {
+                $cnum = $cnumpart;
+                ($sec,$role) = split(/_/,$secpart);
+                $realsec = $sec;
             }
-            if ($endtime) {
-                if ($now>$endtime) { $active=0; }
+            $courses{$cdom.'_'.$cnum}{$sec} = $trole.'/'.$cdom.'/'.$cnum.'/'.$realsec;
+        }
+    } else {
+        foreach my $key (keys(%env)) {
+	    if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
+                 $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
+	        my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
+	        next if ($role eq 'ca' || $role eq 'aa');
+	        next if (%roles && !exists($roles{$role}));
+	        my ($starttime,$endtime)=split(/\./,$env{$key});
+                my $active=1;
+                if ($starttime) {
+		    if ($now<$starttime) { $active=0; }
+                }
+                if ($endtime) {
+                    if ($now>$endtime) { $active=0; }
+                }
+                if ($active) {
+                    if ($sec eq '') {
+                        $sec = 'none';
+                    }
+                    $courses{$cdom.'_'.$cnum}{$sec} = 
+                                     $role.'/'.$cdom.'/'.$cnum.'/'.$sec;
+                }
             }
-            if ($active) { $courses{$domain.'_'.$id}=1; }
         }
     }
-    return keys(%courses);
+    return %courses;
 }
 
 ###############################################
+
+sub blockcheck {
+    my ($setters,$activity,$uname,$udom) = @_;
+
+    if (!defined($udom)) {
+        $udom = $env{'user.domain'};
+    }
+    if (!defined($uname)) {
+        $uname = $env{'user.name'};
+    }
+
+    # If uname and udom are for a course, check for blocks in the course.
+
+    if (&Apache::lonnet::is_course($udom,$uname)) {
+        my %records = &Apache::lonnet::dump('comm_block',$udom,$uname);
+        my ($startblock,$endblock)=&get_blocks($setters,$activity,$udom,$uname);
+        return ($startblock,$endblock);
+    }
+
+    my $startblock = 0;
+    my $endblock = 0;
+    my %live_courses = &findallcourses(undef,$uname,$udom);
+
+    # If uname is for a user, and activity is course-specific, i.e.,
+    # boards, chat or groups, check for blocking in current course only.
+
+    if (($activity eq 'boards' || $activity eq 'chat' ||
+         $activity eq 'groups') && ($env{'request.course.id'})) {
+        foreach my $key (keys(%live_courses)) {
+            if ($key ne $env{'request.course.id'}) {
+                delete($live_courses{$key});
+            }
+        }
+    }
+
+    my $otheruser = 0;
+    my %own_courses;
+    if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
+        # Resource belongs to user other than current user.
+        $otheruser = 1;
+        # Gather courses for current user
+        %own_courses = 
+            &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
+    }
+
+    # Gather active course roles - course coordinator, instructor, 
+    # exam proctor, ta, student, or custom role.
+
+    foreach my $course (keys(%live_courses)) {
+        my ($cdom,$cnum);
+        if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
+            $cdom = $env{'course.'.$course.'.domain'};
+            $cnum = $env{'course.'.$course.'.num'};
+        } else {
+            ($cdom,$cnum) = split(/_/,$course); 
+        }
+        my $no_ownblock = 0;
+        my $no_userblock = 0;
+        if ($otheruser) {
+            # Check if current user has 'evb' priv for this
+            if (defined($own_courses{$course})) {
+                foreach my $sec (keys(%{$own_courses{$course}})) {
+                    my $checkrole = 'cm./'.$cdom.'/'.$cnum;
+                    if ($sec ne 'none') {
+                        $checkrole .= '/'.$sec;
+                    }
+                    if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
+                        $no_ownblock = 1;
+                        last;
+                    }
+                }
+            }
+            # if they have 'evb' priv and are currently not playing student
+            next if (($no_ownblock) &&
+                 ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
+        }
+        foreach my $sec (keys(%{$live_courses{$course}})) {
+            my $checkrole = 'cm./'.$cdom.'/'.$cnum;
+            if ($sec ne 'none') {
+                $checkrole .= '/'.$sec;
+            }
+            if ($otheruser) {
+                # Resource belongs to user other than current user.
+                # Assemble privs for that user, and check for 'evb' priv.
+                my ($trole,$tdom,$tnum,$tsec);
+                my $entry = $live_courses{$course}{$sec};
+                if ($entry =~ /^cr/) {
+                    ($trole,$tdom,$tnum,$tsec) = 
+                      ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
+                } else {
+                    ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
+                }
+                my ($spec,$area,$trest,%allroles,%userroles);
+                $area = '/'.$tdom.'/'.$tnum;
+                $trest = $tnum;
+                if ($tsec ne '') {
+                    $area .= '/'.$tsec;
+                    $trest .= '/'.$tsec;
+                }
+                $spec = $trole.'.'.$area;
+                if ($trole =~ /^cr/) {
+                    &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
+                                                      $tdom,$spec,$trest,$area);
+                } else {
+                    &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
+                                                       $tdom,$spec,$trest,$area);
+                }
+                my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
+                if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
+                    if ($1) {
+                        $no_userblock = 1;
+                        last;
+                    }
+                }
+            } else {
+                # Resource belongs to current user
+                # Check for 'evb' priv via lonnet::allowed().
+                if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
+                    $no_ownblock = 1;
+                    last;
+                }
+            }
+        }
+        # if they have the evb priv and are currently not playing student
+        next if (($no_ownblock) &&
+                 ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
+        next if ($no_userblock);
+
+        # Retrieve blocking times and identity of blocker for course
+        # of specified user, unless user has 'evb' privilege.
+        
+        my ($start,$end)=&get_blocks($setters,$activity,$cdom,$cnum);
+        if (($start != 0) && 
+            (($startblock == 0) || ($startblock > $start))) {
+            $startblock = $start;
+        }
+        if (($end != 0)  &&
+            (($endblock == 0) || ($endblock < $end))) {
+            $endblock = $end;
+        }
+    }
+    return ($startblock,$endblock);
+}
+
+sub get_blocks {
+    my ($setters,$activity,$cdom,$cnum) = @_;
+    my $startblock = 0;
+    my $endblock = 0;
+    my $course = $cdom.'_'.$cnum;
+    $setters->{$course} = {};
+    $setters->{$course}{'staff'} = [];
+    $setters->{$course}{'times'} = [];
+    my %records = &Apache::lonnet::dump('comm_block',$cdom,$cnum);
+    foreach my $record (keys(%records)) {
+        my ($start,$end) = ($record =~ m/^(\d+)____(\d+)$/);
+        if ($start <= time && $end >= time) {
+            my ($staff_name,$staff_dom,$title,$blocks) =
+                &parse_block_record($records{$record});
+            if ($blocks->{$activity} eq 'on') {
+                push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
+                push(@{$$setters{$course}{'times'}}, [$start,$end]);
+                if ( ($startblock == 0) || ($startblock > $start) ) {
+                    $startblock = $start;
+                }
+                if ( ($endblock == 0) || ($endblock < $end) ) {
+                    $endblock = $end;
+                }
+            }
+        }
+    }
+    return ($startblock,$endblock);
+}
+
+sub parse_block_record {
+    my ($record) = @_;
+    my ($setuname,$setudom,$title,$blocks);
+    if (ref($record) eq 'HASH') {
+        ($setuname,$setudom) = split(/:/,$record->{'setter'});
+        $title = &unescape($record->{'event'});
+        $blocks = $record->{'blocks'};
+    } else {
+        my @data = split(/:/,$record,3);
+        if (scalar(@data) eq 2) {
+            $title = $data[1];
+            ($setuname,$setudom) = split(/@/,$data[0]);
+        } else {
+            ($setuname,$setudom,$title) = @data;
+        }
+        $blocks = { 'com' => 'on' };
+    }
+    return ($setuname,$setudom,$title,$blocks);
+}
+
+sub build_block_table {
+    my ($startblock,$endblock,$setters) = @_;
+    my %lt = &Apache::lonlocal::texthash(
+        'cacb' => 'Currently active communication blocks',
+        'cour' => 'Course',
+        'dura' => 'Duration',
+        'blse' => 'Block set by'
+    );
+    my $output;
+    $output = '<br />'.$lt{'cacb'}.':<br />';
+    $output .= &start_data_table();
+    $output .= '
+<tr>
+ <th>'.$lt{'cour'}.'</th>
+ <th>'.$lt{'dura'}.'</th>
+ <th>'.$lt{'blse'}.'</th>
+</tr>
+';
+    foreach my $course (keys(%{$setters})) {
+        my %courseinfo=&Apache::lonnet::coursedescription($course);
+        for (my $i=0; $i<@{$$setters{$course}{staff}}; $i++) {
+            my ($uname,$udom) = @{$$setters{$course}{staff}[$i]};
+            my $fullname = &plainname($uname,$udom);
+            if (defined($env{'user.name'}) && defined($env{'user.domain'})
+                && $env{'user.name'} ne 'public' 
+                && $env{'user.domain'} ne 'public') {
+                $fullname = &aboutmewrapper($fullname,$uname,$udom);
+            }
+            my ($openblock,$closeblock) = @{$$setters{$course}{times}[$i]};
+            $openblock = &Apache::lonlocal::locallocaltime($openblock);
+            $closeblock= &Apache::lonlocal::locallocaltime($closeblock);
+            $output .= &Apache::loncommon::start_data_table_row().
+                       '<td>'.$courseinfo{'description'}.'</td>'.
+                       '<td>'.$openblock.' to '.$closeblock.'</td>'.
+                       '<td>'.$fullname.'</td>'.
+                        &Apache::loncommon::end_data_table_row();
+        }
+    }
+    $output .= &end_data_table();
+}
+
+sub blocking_status {
+    my ($activity,$uname,$udom) = @_;
+    my %setters;
+    my ($blocked,$output,$ownitem,$is_course);
+    my ($startblock,$endblock)=&blockcheck(\%setters,$activity,$uname,$udom);
+    if ($startblock && $endblock) {
+        $blocked = 1;
+        if (wantarray) {
+            my $category;
+            if ($activity eq 'boards') {
+                $category = 'Discussion posts in this course';
+            } elsif ($activity eq 'blogs') {
+                $category = 'Blogs';
+            } elsif ($activity eq 'port') {
+                if (defined($uname) && defined($udom)) {
+                    if ($uname eq $env{'user.name'} &&
+                        $udom eq $env{'user.domain'}) {
+                        $ownitem = 1;
+                    }
+                }
+                $is_course = &Apache::lonnet::is_course($udom,$uname);
+                if ($ownitem) { 
+                    $category = 'Your portfolio files';  
+                } elsif ($is_course) {
+                    my $coursedesc;
+                    foreach my $course (keys(%setters)) {
+                        my %courseinfo =
+                             &Apache::lonnet::coursedescription($course);
+                        $coursedesc = $courseinfo{'description'};
+                    }
+                    $category = "Group files in the course '$coursedesc'";
+                } else {
+                    $category = 'Portfolio files belonging to ';
+                    if ($env{'user.name'} eq 'public' && 
+                        $env{'user.domain'} eq 'public') {
+                        $category .= &plainname($uname,$udom);
+                    } else {
+                        $category .= &aboutmewrapper(&plainname($uname,$udom),$uname,$udom);  
+                    }
+                }
+            } elsif ($activity eq 'groups') {
+                $category = 'Groups in this course';
+            }
+            my $showstart = &Apache::lonlocal::locallocaltime($startblock);
+            my $showend = &Apache::lonlocal::locallocaltime($endblock);
+            $output = '<br />'.&mt('[_1] will be inaccessible between [_2] and [_3] because communication is being blocked.',$category,$showstart,$showend).'<br />';
+            if (!($activity eq 'port' && !($ownitem) && !($is_course))) { 
+                $output .= &build_block_table($startblock,$endblock,\%setters);
+            }
+        }
+    }
+    if (wantarray) {
+        return ($blocked,$output);
+    } else {
+        return $blocked;
+    }
+}
+
 ###############################################
 
 =pod
@@ -2888,8 +3370,8 @@ sub bodytag {
  # role and realm
     my ($role,$realm) = split(/\./,$env{'request.role'},2);
     if ($role  eq 'ca') {
-        my ($rdom,$rname) = ($realm =~ m-^/(\w+)/(\w+)$-);
-        $realm = &plainname($rname,$rdom).':'.$rdom;
+        my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
+        $realm = &plainname($rname,$rdom);
     } 
 # realm
     if ($env{'request.course.id'}) {
@@ -3173,8 +3655,8 @@ sub standard_css {
     my $mono                 = 'monospace';
     my $data_table_head      = $tabbg;
     my $data_table_light     = '#EEEEEE';
-    my $data_table_dark      = '#DDD';
-    my $data_table_darker    = '#CCC';
+    my $data_table_dark      = '#DDDDDD';
+    my $data_table_darker    = '#CCCCCC';
     my $data_table_highlight = '#FFFF00';
     my $mail_new             = '#FFBB77';
     my $mail_new_hover       = '#DD9955';
@@ -3185,6 +3667,7 @@ sub standard_css {
     my $mail_other           = '#99BBBB';
     my $mail_other_hover     = '#669999';
     my $table_header         = '#DDDDDD';
+    my $feedback_link_bg     = '#BBBBBB';
 
     my $border = ($env{'browser.type'} eq 'explorer') ? '0px 2px 0px 2px'
 	                                              : '0px 3px 0px 4px';
@@ -3192,9 +3675,20 @@ sub standard_css {
     return <<END;
 h1, h2, h3, th { font-family: $sans }
 a:focus { color: red; background: yellow } 
-table.thinborder { border-collapse: collapse; }
-table.thinborder tr th {  border-style: solid; border-width: 1px; background: $tabbg;}
-table.thinborder tr td { border-style: solid; border-width: 1px}
+table.thinborder,
+table.LC_optres_prior {
+  border-collapse: collapse;
+}
+table.thinborder tr th {
+  border-style: solid;
+  border-width: 1px;
+  background: $tabbg;
+}
+table.thinborder tr td, 
+table.LC_optres_prior tr td {
+  border-style: solid;
+  border-width: 1px
+}
 
 form, .inline { display: inline; }
 .center { text-align: center; }
@@ -3259,6 +3753,9 @@ table#LC_title_bar td.LC_title_bar_who {
   font: small $sans;
   text-align: right;
 }
+span.LC_metadata {
+    font-family: $sans;
+}
 span.LC_title_bar_title {
   font: bold x-large $sans;
 }
@@ -3363,6 +3860,18 @@ table.LC_data_table, table.LC_mail_list
 .LC_data_table_dense {
   font-size: small;
 }
+table.LC_nested_outer {
+  border: 1px solid #000000;
+  border-collapse: separate;
+  border-spacing: 0px;
+  width: 100%;
+}
+table.LC_nested {
+  border: 0px;
+  border-collapse: separate;
+  border-spacing: 0px;
+  width: 100%;
+}
 table.LC_data_table tr th, table.LC_calendar tr th, table.LC_mail_list tr th {
   font-weight: bold;
   background-color: $data_table_head;
@@ -3381,37 +3890,66 @@ table.LC_data_table tr.LC_data_table_hig
   background-color: $data_table_darker;
 }
 table.LC_data_table tr.LC_empty_row td,
-table.LC_whatsnew tr.LC_empty_row td {
+table.LC_nested tr.LC_empty_row td {
   background-color: #FFFFFF;
   font-weight: bold;
   font-style: italic;
   text-align: center;
   padding: 8px;
 }
-
-table.LC_whatsnew {
+table.LC_nested tr.LC_empty_row td {
+  padding: 4ex
 }
-
-table.LC_whatsnew tr th,
-table.LC_whatsnew tr.LC_info_row td {
-  background-color: #CCC;
+table.LC_nested_outer tr th {
+  font-weight: bold;
+  background-color: $data_table_head;
+  font-size: smaller;
+  border-bottom: 1px solid #000000;
+}
+table.LC_nested_outer tr td.LC_subheader {
+  background-color: $data_table_head;
   font-weight: bold;
   font-size: small;
+  border-bottom: 1px solid #000000;
   text-align: right;
 }
-table.LC_whatsnew tr td {
-  background-color: #FFF;
+table.LC_nested tr.LC_info_row td {
+  background-color: #CCC;
+  font-weight: bold;
   font-size: small;
-  text-align: right;
+  text-align: center;
 }
-table.LC_whatsnew tr td.LC_first_item {
+table.LC_nested tr.LC_info_row td.LC_left_item {
   text-align: left;
 }
+table.LC_nested td {
+  background-color: #FFF;
+  font-size: small;
+}
+table.LC_nested_outer tr th.LC_right_item,
+table.LC_nested tr.LC_info_row td.LC_right_item,
+table.LC_nested tr.LC_odd_row td.LC_right_item,
+table.LC_nested tr td.LC_right_item {
+  text-align: right;
+}
 
-table.LC_whatsnew tr.LC_odd_row td {
+table.LC_nested tr.LC_odd_row td {
   background-color: #EEE;
 }
 
+table.LC_createuser {
+}
+
+table.LC_createuser tr.LC_section_row td {
+  font-size: smaller;
+}
+
+table.LC_createuser tr.LC_info_row td  {
+  background-color: #CCC;
+  font-weight: bold;
+  text-align: center;
+}
+
 table.LC_calendar {
   border: 1px solid #000000;
   border-collapse: collapse;
@@ -3454,6 +3992,11 @@ table.LC_mail_list tr.LC_mail_other {
 table.LC_mail_list tr.LC_mail_other:hover {
   background-color: $mail_other_hover;
 }
+table.LC_mail_list tr.LC_mail_even {
+}
+table.LC_mail_list tr.LC_mail_odd {
+}
+
 
 table#LC_portfolio_actions {
   width: auto;
@@ -3698,6 +4241,13 @@ table.LC_descriptive_input td.LC_descrip
   text-align: right;
   font-weight: bold;
 }
+table.LC_feedback_link {
+    background: $feedback_link_bg;
+}
+span.LC_feedback_link {
+    background: $feedback_link_bg;
+    font-size: larger;
+}
 
 END
 }
@@ -3728,6 +4278,7 @@ Inputs: $title - optional title for the
             bgcolor        -> override the default page bgcolor
             no_auto_mt_title
                            -> prevent &mt()ing the title arg
+
 =back
 
 =cut
@@ -3989,6 +4540,11 @@ Inputs:         $args - additional optio
                                  a html attribute
                  frameset     -> if true will start with a <frameset>
                                  rather than <body>
+                 dicsussion   -> if true will get discussion from
+                                  lonxml::xmlend
+                                 (you can pass the target and parser arguments
+                                  through optional 'target' and 'parser' args
+                                  to this routine)
 
 =cut
 
@@ -4100,6 +4656,13 @@ sub simple_error_page {
 	$css_class = (join(' ',$css_class,$add_class));
 	return  '<tr class="'.$css_class.'">'."\n";;
     }
+    
+    sub continue_data_table_row {
+	my ($add_class) = @_;
+	my $css_class = ($row_count % 2)?'':'LC_even_row';
+	$css_class = (join(' ',$css_class,$add_class));
+	return  '<tr class="'.$css_class.'">'."\n";;
+    }
 
     sub end_data_table_row {
 	return '</tr>'."\n";;
@@ -4440,7 +5003,7 @@ sub get_course_users {
                     $usec = 'none';
                 }
                 if ($uname ne '' && $udom ne '') {
-                    if ($end < $now) {
+                    if ($end > 0 && $end < $now) {
                         $status = 'previous';
                     } elsif ($start > $now) {
                         $status = 'future';
@@ -4502,6 +5065,96 @@ sub get_user_info {
     return;
 }
 
+###############################################
+
+=pod
+
+=item * &get_user_quota()
+
+Retrieves quota assigned for storage of portfolio files for a user  
+
+Incoming parameters:
+1. user's username
+2. user's domain
+
+Returns:
+1. Disk quota (in Mb) assigned to student. 
+
+If a value has been stored in the user's environment, 
+it will return that, otherwise it returns the default
+for users in the domain.
+
+=cut
+
+###############################################
+
+
+sub get_user_quota {
+    my ($uname,$udom) = @_;
+    my $quota;
+    if (!defined($udom)) {
+        $udom = $env{'user.domain'};
+    }
+    if (!defined($uname)) {
+        $uname = $env{'user.name'};
+    }
+    if (($udom eq '' || $uname eq '') ||
+        ($udom eq 'public') && ($uname eq 'public')) {
+        $quota = 0;
+    } else {
+        if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
+            $quota = $env{'environment.portfolioquota'};
+        } else {
+            my %userenv = &Apache::lonnet::dump('environment',$udom,$uname);
+            my ($tmp) = keys(%userenv);
+            if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
+                $quota = $userenv{'portfolioquota'};
+            } else {
+                undef(%userenv);
+            }
+        }
+        if ($quota eq '') {
+            $quota = &default_quota($udom);
+        }
+    }
+    return $quota;
+}
+
+###############################################
+
+=pod
+
+=item * &default_quota()
+
+Retrieves default quota assigned for storage of user portfolio files
+
+Incoming parameters:
+1. domain
+
+Returns:
+1. Default disk quota (in Mb) for user portfolios in the domain.
+
+If a value has been stored in the domain's configuration db,
+it will return that, otherwise it returns 20 (for backwards 
+compatibility with domains which have not set up a configuration
+db file; the original statically defined portfolio quota was 20 Mb). 
+
+=cut
+
+###############################################
+
+
+sub default_quota {
+    my ($udom) = @_;
+    my %defaults = &Apache::lonnet::get_dom('configuration',
+                                            ['portfolioquota'],$udom);
+    if ($defaults{'portfolioquota'} ne '') {
+        return $defaults{'portfolioquota'};
+    } else {
+        return '20';
+    }
+}
+
 sub get_secgrprole_info {
     my ($cdom,$cnum,$needroles,$type)  = @_;
     my %sections_count = &get_sections($cdom,$cnum);
@@ -4676,7 +5329,7 @@ sub get_env_multiple {
 
 =pod
 
-=back 
+=back
 
 =head1 CSV Upload/Handling functions
 
@@ -4790,7 +5443,12 @@ sub record_sep {
             $i++;
         }
     } else {
-        my @allfields=split(/\,/,$record);
+        my @allfields;
+        if ($env{'form.upfiletype'} eq 'semisv') {
+            @allfields=split(/;/,$record,-1);
+        } else {
+            @allfields=split(/\,/,$record,-1);
+        }
         my $i=0;
         my $j;
         for ($j=0;$j<=$#allfields;$j++) {
@@ -4828,6 +5486,7 @@ the file type.
 sub upfile_select_html {
     my %Types = (
                  csv   => &mt('CSV (comma separated values, spreadsheet)'),
+                 semisv => &mt('Semicolon separated values'),
                  space => &mt('Space separated'),
                  tab   => &mt('Tabulator separated'),
 #                 xml   => &mt('HTML/XML'),
@@ -5482,16 +6141,19 @@ Returns: both routines return nothing
 #######################################################
 #######################################################
 sub store_course_settings {
+    return &store_settings($env{'request.course.id'},@_);
+}
+
+sub store_settings {
     # save to the environment
     # appenv the same items, just to be safe
-    my $courseid = $env{'request.course.id'};
     my $udom  = $env{'user.domain'};
     my $uname = $env{'user.name'};
-    my ($prefix,$Settings) = @_;
+    my ($context,$prefix,$Settings) = @_;
     my %SaveHash;
     my %AppHash;
     while (my ($setting,$type) = each(%$Settings)) {
-        my $basename = join('.','internal',$courseid,$prefix,$setting);
+        my $basename = join('.','internal',$context,$prefix,$setting);
         my $envname = 'environment.'.$basename;
         if (exists($env{'form.'.$setting})) {
             # Save this value away
@@ -5531,11 +6193,14 @@ sub store_course_settings {
 }
 
 sub restore_course_settings {
-    my $courseid = $env{'request.course.id'};
-    my ($prefix,$Settings) = @_;
+    return &restore_settings($env{'request.course.id'},@_);
+}
+
+sub restore_settings {
+    my ($context,$prefix,$Settings) = @_;
     while (my ($setting,$type) = each(%$Settings)) {
         next if (exists($env{'form.'.$setting}));
-        my $envname = 'environment.internal.'.$courseid.'.'.$prefix.
+        my $envname = 'environment.internal.'.$context.'.'.$prefix.
             '.'.$setting;
         if (exists($env{$envname})) {
             if ($type eq 'scalar') {
@@ -5667,7 +6332,7 @@ sub construct_course {
 #
 # Check if created correctly
 #
-    ($$crsudom,$$crsunum)=($$courseid=~/^\/(\w+)\/(\w+)$/);
+    ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
     my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
     $outcome .= &mt('Created on').': '.$crsuhome.'<br>';
 #
@@ -5676,7 +6341,7 @@ sub construct_course {
     my $cloneid='';
     if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
 	$cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
-        my ($clonecrsudom,$clonecrsunum)=($cloneid=~/^\/(\w+)\/(\w+)$/);
+        my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
 	my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
 	if ($clonehome eq 'no_host') {
 	    $outcome .=
@@ -5849,9 +6514,11 @@ sub construct_course {
 # if specified, key authority is not course, but user
 # only active if keyaccess is yes
     if ($args->{'keyauth'}) {
-	$args->{'keyauth'}=~s/[^\w\@]//g;
-	if ($args->{'keyauth'}) {
-	    $cenv{'keyauth'}=$args->{'keyauth'};
+	my ($user,$domain) = split(':',$args->{'keyauth'});
+	$user = &LONCAPA::clean_username($user);
+	$domain = &LONCAPA::clean_username($domain);
+	if ($user ne '' && $domain ne '') {
+	    $cenv{'keyauth'}=$user.':'.$domain;
 	}
     }
 
@@ -5948,7 +6615,7 @@ sub group_term {
 
 sub icon {
     my ($file)=@_;
-    my $curfext = (split(/\./,$file))[-1];
+    my $curfext = lc((split(/\./,$file))[-1]);
     my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
     my $embstyle = &Apache::loncommon::fileembstyle($curfext);
     if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
@@ -6001,6 +6668,171 @@ sub escape_url {
     my $lastitem = &escape(pop(@urlslices));
     return join('/',@urlslices).'/'.$lastitem;
 }
+
+# -------------------------------------------------------- Initliaze user login
+sub init_user_environment {
+    my ($r, $username, $domain, $authhost, $form, $args) = @_;
+    my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
+
+    my $public=($username eq 'public' && $domain eq 'public');
+
+# See if old ID present, if so, remove
+
+    my ($filename,$cookie,$userroles);
+    my $now=time;
+
+    if ($public) {
+	my $max_public=100;
+	my $oldest;
+	my $oldest_time=0;
+	for(my $next=1;$next<=$max_public;$next++) {
+	    if (-e $lonids."/publicuser_$next.id") {
+		my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
+		if ($mtime<$oldest_time || !$oldest_time) {
+		    $oldest_time=$mtime;
+		    $oldest=$next;
+		}
+	    } else {
+		$cookie="publicuser_$next";
+		last;
+	    }
+	}
+	if (!$cookie) { $cookie="publicuser_$oldest"; }
+    } else {
+	# if this isn't a robot, kill any existing non-robot sessions
+	if (!$args->{'robot'}) {
+	    opendir(DIR,$lonids);
+	    while ($filename=readdir(DIR)) {
+		if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
+		    unlink($lonids.'/'.$filename);
+		}
+	    }
+	    closedir(DIR);
+	}
+# Give them a new cookie
+	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
+		                   : $now);
+	$cookie="$username\_$id\_$domain\_$authhost";
+    
+# Initialize roles
+
+	$userroles=&Apache::lonnet::rolesinit($domain,$username,$authhost);
+    }
+# ------------------------------------ Check browser type and MathML capability
+
+    my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
+        $clientunicode,$clientos) = &decode_user_agent($r);
+
+# -------------------------------------- Any accessibility options to remember?
+    if (($form->{'interface'}) && ($form->{'remember'} eq 'true')) {
+	foreach my $option ('imagesuppress','appletsuppress',
+			    'embedsuppress','fontenhance','blackwhite') {
+	    if ($form->{$option} eq 'true') {
+		&Apache::lonnet::put('environment',{$option => 'on'},
+				     $domain,$username);
+	    } else {
+		&Apache::lonnet::del('environment',[$option],
+				     $domain,$username);
+	    }
+	}
+    }
+# ------------------------------------------------------------- Get environment
+
+    my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
+    my ($tmp) = keys(%userenv);
+    if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
+	# default remote control to off
+	if ($userenv{'remote'} ne 'on') { $userenv{'remote'} = 'off'; }
+    } else {
+	undef(%userenv);
+    }
+    if (($userenv{'interface'}) && (!$form->{'interface'})) {
+	$form->{'interface'}=$userenv{'interface'};
+    }
+    $env{'environment.remote'}=$userenv{'remote'};
+    if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
+
+# --------------- Do not trust query string to be put directly into environment
+    foreach my $option ('imagesuppress','appletsuppress',
+			'embedsuppress','fontenhance','blackwhite',
+			'interface','localpath','localres') {
+	$form->{$option}=~s/[\n\r\=]//gs;
+    }
+# --------------------------------------------------------- Write first profile
+
+    {
+	my %initial_env = 
+	    ("user.name"          => $username,
+	     "user.domain"        => $domain,
+	     "user.home"          => $authhost,
+	     "browser.type"       => $clientbrowser,
+	     "browser.version"    => $clientversion,
+	     "browser.mathml"     => $clientmathml,
+	     "browser.unicode"    => $clientunicode,
+	     "browser.os"         => $clientos,
+	     "server.domain"      => $Apache::lonnet::perlvar{'lonDefDomain'},
+	     "request.course.fn"  => '',
+	     "request.course.uri" => '',
+	     "request.course.sec" => '',
+	     "request.role"       => 'cm',
+	     "request.role.adv"   => $env{'user.adv'},
+	     "request.host"       => $ENV{'REMOTE_ADDR'},);
+
+        if ($form->{'localpath'}) {
+	    $initial_env{"browser.localpath"}  = $form->{'localpath'};
+	    $initial_env{"browser.localres"}   = $form->{'localres'};
+        }
+	
+	if ($public) {
+	    $initial_env{"environment.remote"} = "off";
+	}
+	if ($form->{'interface'}) {
+	    $form->{'interface'}=~s/\W//gs;
+	    $initial_env{"browser.interface"} = $form->{'interface'};
+	    $env{'browser.interface'}=$form->{'interface'};
+	    foreach my $option ('imagesuppress','appletsuppress',
+				'embedsuppress','fontenhance','blackwhite') {
+		if (($form->{$option} eq 'true') ||
+		    ($userenv{$option} eq 'on')) {
+		    $initial_env{"browser.$option"} = "on";
+		}
+	    }
+	}
+
+	$env{'user.environment'} = "$lonids/$cookie.id";
+	
+	if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
+		 &GDBM_WRCREAT(),0640)) {
+	    &_add_to_env(\%disk_env,\%initial_env);
+	    &_add_to_env(\%disk_env,\%userenv,'environment.');
+	    &_add_to_env(\%disk_env,$userroles);
+	    if (ref($args->{'extra_env'})) {
+		&_add_to_env(\%disk_env,$args->{'extra_env'});
+	    }
+	    untie(%disk_env);
+	} else {
+	    &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ".
+			   'Could not create environment storage in lonauth: '.$!.'</font>');
+	    return 'error: '.$!;
+	}
+    }
+    $env{'request.role'}='cm';
+    $env{'request.role.adv'}=$env{'user.adv'};
+    $env{'browser.type'}=$clientbrowser;
+
+    return $cookie;
+
+}
+
+sub _add_to_env {
+    my ($idf,$env_data,$prefix) = @_;
+    while (my ($key,$value) = each(%$env_data)) {
+	$idf->{$prefix.$key} = $value;
+	$env{$prefix.$key}   = $value;
+    }
+}
+
+
 =pod
 
 =back