--- loncom/interface/loncommon.pm	2008/07/06 05:01:52	1.664
+++ loncom/interface/loncommon.pm	2008/10/02 14:05:45	1.689
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.664 2008/07/06 05:01:52 raeburn Exp $
+# $Id: loncommon.pm,v 1.689 2008/10/02 14:05:45 bisitz Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -61,6 +61,7 @@ use POSIX qw(strftime mktime);
 use Apache::lonmenu();
 use Apache::lonenc();
 use Apache::lonlocal;
+use Apache::lonnet();
 use HTML::Entities;
 use Apache::lonhtmlcommon();
 use Apache::loncoursedata();
@@ -68,6 +69,7 @@ use Apache::lontexconvert();
 use Apache::lonclonecourse();
 use LONCAPA qw(:DEFAULT :match);
 use DateTime::TimeZone;
+use DateTime::Locale::Catalog;
 
 # ---------------------------------------------- Designs
 use vars qw(%defaultdesign);
@@ -657,6 +659,57 @@ sub select_timezone {
    return $output;
 }
 
+sub select_datelocale {
+    my ($name,$selected,$onchange,$includeempty)=@_;
+    my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
+    if ($includeempty) {
+        $output .= '<option value=""';
+        if ($selected eq '') {
+            $output .= ' selected="selected" ';
+        }
+        $output .= '> </option>';
+    }
+    my (@possibles,%locale_names);
+    my @locales = DateTime::Locale::Catalog::Locales;
+    foreach my $locale (@locales) {
+        if (ref($locale) eq 'HASH') {
+            my $id = $locale->{'id'};
+            if ($id ne '') {
+                my $en_terr = $locale->{'en_territory'};
+                my $native_terr = $locale->{'native_territory'};
+                my @languages = &preferred_languages();
+                if (grep(/^en$/,@languages) || !@languages) {
+                    if ($en_terr ne '') {
+                        $locale_names{$id} = '('.$en_terr.')';
+                    } elsif ($native_terr ne '') {
+                        $locale_names{$id} = $native_terr;
+                    }
+                } else {
+                    if ($native_terr ne '') {
+                        $locale_names{$id} = $native_terr.' ';
+                    } elsif ($en_terr ne '') {
+                        $locale_names{$id} = '('.$en_terr.')';
+                    }
+                }
+                push (@possibles,$id);
+            }
+        }
+    }
+    foreach my $item (sort(@possibles)) {
+        $output.= '<option value="'.$item.'"';
+        if ($item eq $selected) {
+            $output.=' selected="selected"';
+        }
+        $output.=">$item";
+        if ($locale_names{$item} ne '') {
+            $output.="  $locale_names{$item}</option>\n";
+        }
+        $output.="</option>\n";
+    }
+    $output.="</select>";
+    return $output;
+}
+
 =pod
 
 =item * &linked_select_forms(...)
@@ -878,7 +931,7 @@ sub help_open_topic {
 
     # Add the graphic
     my $title = &mt('Online Help');
-    my $helpicon=&lonhttpdurl("/res/adm/pages/help.png");
+    my $helpicon=&lonhttpdurl("/adm/help/help.png");
     $template .= <<"ENDTEMPLATE";
  <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a>
 ENDTEMPLATE
@@ -904,6 +957,9 @@ sub helpLatexCheatsheet {
 	.'</td><td>'.
 	&Apache::loncommon::help_open_topic("Other_Symbols",&mt('Other Symbols'),
 					    undef,undef,600)
+	.'</td><td>'.
+	&Apache::loncommon::help_open_topic("Authoring_Output_Tags",&mt('Output Tags'),
+	                                    undef,undef,600)
 	.'</td></tr></table>';
 }
 
@@ -913,6 +969,8 @@ sub general_help {
 	$helptopic='Authoring_Intro';
     } elsif ($env{'request.role'}=~/^cc/) {
 	$helptopic='Course_Coordination_Intro';
+    } elsif ($env{'request.role'}=~/^dc/) {
+        $helptopic='Domain_Coordination_Intro';
     }
     return $helptopic;
 }
@@ -1502,9 +1560,9 @@ sub create_text_file {
     $fh = Apache::File->new('>/home/httpd'.$filename);
     if (! defined($fh)) {
         $r->log_error("Couldn't open $filename for output $!");
-        $r->print("Problems occured in creating the output file.  ".
-                  "This error has been logged.  ".
-                  "Please alert your LON-CAPA administrator.");
+        $r->print(&mt('Problems occurred in creating the output file. '
+                     .'This error has been logged. '
+                     .'Please alert your LON-CAPA administrator.'));
     }
     return ($fh,$filename)
 }
@@ -2982,14 +3040,19 @@ sub preferred_languages {
             }
         }
     }
+    return &get_genlanguages(@languages);
+}
+
+sub get_genlanguages {
+    my (@languages) = @_;
 # turn "en-ca" into "en-ca,en"
     my @genlanguages;
     foreach my $lang (@languages) {
-	unless ($lang=~/\w/) { next; }
-	push(@genlanguages,$lang);
-	if ($lang=~/(\-|\_)/) {
-	    push(@genlanguages,(split(/(\-|\_)/,$lang))[0]);
-	}
+        unless ($lang=~/\w/) { next; }
+        push(@genlanguages,$lang);
+        if ($lang=~/(\-|\_)/) {
+            push(@genlanguages,(split(/(\-|\_)/,$lang))[0]);
+        }
     }
     #uniqueify the languages list
     my %count;
@@ -3343,16 +3406,21 @@ sub pprmlink {
 
 
 sub timehash {
-    my @ltime=localtime(shift);
-    return ( 'seconds' => $ltime[0],
-             'minutes' => $ltime[1],
-             'hours'   => $ltime[2],
-             'day'     => $ltime[3],
-             'month'   => $ltime[4]+1,
-             'year'    => $ltime[5]+1900,
-             'weekday' => $ltime[6],
-             'dayyear' => $ltime[7]+1,
-             'dlsav'   => $ltime[8] );
+    my ($thistime) = @_;
+    my $timezone = &Apache::lonlocal::gettimezone();
+    my $dt = DateTime->from_epoch(epoch => $thistime)
+                     ->set_time_zone($timezone);
+    my $wday = $dt->day_of_week();
+    if ($wday == 7) { $wday = 0; }
+    return ( 'second' => $dt->second(),
+             'minute' => $dt->minute(),
+             'hour'   => $dt->hour(),
+             'day'     => $dt->day_of_month(),
+             'month'   => $dt->month(),
+             'year'    => $dt->year(),
+             'weekday' => $wday,
+             'dayyear' => $dt->day_of_year(),
+             'dlsav'   => $dt->is_dst() );
 }
 
 sub utc_string {
@@ -3362,6 +3430,24 @@ sub utc_string {
 
 sub maketime {
     my %th=@_;
+    my ($epoch_time,$timezone,$dt);
+    $timezone = &Apache::lonlocal::gettimezone();
+    eval {
+        $dt = DateTime->new( year   => $th{'year'},
+                             month  => $th{'month'},
+                             day    => $th{'day'},
+                             hour   => $th{'hour'},
+                             minute => $th{'minute'},
+                             second => $th{'second'},
+                             time_zone => $timezone,
+                         );
+    };
+    if (!$@) {
+        $epoch_time = $dt->epoch;
+        if ($epoch_time) {
+            return $epoch_time;
+        }
+    }
     return POSIX::mktime(
         ($th{'seconds'},$th{'minutes'},$th{'hours'},
          $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
@@ -3742,6 +3828,60 @@ sub blocking_status {
 
 ###############################################
 
+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{'request.host'} || $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;
+}
+
+###############################################
+
 =pod
 
 =head1 Domain Template Functions
@@ -4488,7 +4628,6 @@ table.LC_docs_path td.LC_docs_path_compo
 td.LC_table_cell_checkbox {
   text-align: center;
 }
-
 table#LC_mainmenu td.LC_mainmenu_column {
     vertical-align: top;
 }
@@ -4502,7 +4641,7 @@ table#LC_mainmenu td.LC_mainmenu_column
 .LC_menubuttons_link {
   text-decoration: none;
 }
-
+#2008--9-5: new menu style sheet.Changed category
 .LC_menubuttons_category {
   color: $font;
   background: $pgbg;
@@ -4569,6 +4708,10 @@ td.LC_menubuttons_img {
   text-align: right;
 }
 
+.LC_roleslog_note {
+  font-size: smaller;
+}
+
 table.LC_aboutme_port {
   border: 0px;
   border-collapse: collapse;
@@ -5351,6 +5494,32 @@ hr.LC_edit_problem_divide {
   height: 3px;
   border: 0px;
 }
+img.stift{
+  border-width:0;
+  vertical-align:middle;
+}
+
+table#LC_mainmenu{
+ margin-top:10px;
+ width:80%;
+
+}
+
+table#LC_mainmenu td.LC_mainmenu_col_fieldset{
+  vertical-align: top;
+  width: 45%;
+}
+.LC_mainmenu_fieldset_category {
+  color: $font;
+  background: $pgbg;
+  font-family: $sans;
+  font-size: small;
+  font-weight: bold;
+}
+fieldset#LC_mainmenu_fieldset {
+  margin:0px 10px 10px 0px;
+
+}
 END
 }
 
@@ -6799,12 +6968,16 @@ sub instrule_disallow_msg {
             $text{'action'} = 'IDs';
         }
     }
-    $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for <span class=\"LC_cusr_emph\">[_1]</span>, but the $text{'item'} $text{'do'} not exist in the institutional directory.",$domdesc).'<br />';
+    $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",'<span class="LC_cusr_emph">'.$domdesc.'</span>').'<br />';
     if ($mode eq 'upload') {
         if ($checkitem eq 'username') {
             $response .= &mt("You will need to modify your upload file so it will include $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
         } elsif ($checkitem eq 'id') {
-            $response .= &mt("Either upload a file which includes $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the ID/Student Number field.");
+            $response .= &mt("Either upload a file which includes $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the Student/Employee ID field.");
+        }
+    } elsif ($mode eq 'selfcreate') {
+        if ($checkitem eq 'id') {
+            $response .= &mt("You must either choose $text{'action'} with a different format --  $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
         }
     } else {
         if ($checkitem eq 'username') {
@@ -6834,7 +7007,7 @@ sub sorted_inst_types {
     my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
     my $othertitle = &mt('All users');
     if ($env{'request.course.id'}) {
-        $othertitle  = 'any';
+        $othertitle  = &mt('Any users');
     }
     my @types;
     if (ref($order) eq 'ARRAY') {
@@ -6847,9 +7020,6 @@ sub sorted_inst_types {
     }
     if (keys(%{$usertypes}) > 0) {
         $othertitle = &mt('Other users');
-        if ($env{'request.course.id'}) {
-            $othertitle = 'other';
-        }
     }
     return ($othertitle,$usertypes,\@types);
 }
@@ -7238,7 +7408,6 @@ sub check_for_upload {
             }
         }
     }
-    my $getpropath = 1;
     if (($current_disk_usage + $filesize) > $disk_quota){
         my $msg = '<span class="LC_error">'.
                 &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'.
@@ -7538,7 +7707,7 @@ sub csv_print_select_table {
               &end_data_table_header_row()."\n");
     foreach my $array_ref (@$d) {
 	my ($value,$display,$defaultcol)=@{ $array_ref };
-	$r->print(&start_data_table_row().'<tr><td>'.$display.'</td>');
+	$r->print(&start_data_table_row().'<td>'.$display.'</td>');
 
 	$r->print('<td><select name=f'.$i.
 		  ' onchange="javascript:flip(this.form,'.$i.');">');
@@ -8235,8 +8404,10 @@ sub build_recipient_list {
     } elsif ($origmail ne '') {
         push(@recipients,$origmail);
     }
-    if ($defmail ne '') {
-        push(@recipients,$defmail);
+    if (defined($defmail)) {
+        if ($defmail ne '') {
+            push(@recipients,$defmail);
+        }
     }
     if ($otheremails) {
         my @others;
@@ -8340,6 +8511,9 @@ idx (reference to hash of counters used
 jsarray (reference to array of categories used to create Javascript arrays for
          Domain Coordinator interface for editing Course Categories).
 
+subcats (reference to hash of arrays containing all subcategories within each 
+         category, -recursive)
+
 Returns: nothing
 
 Side effects: populates trails and allitems hash references.
@@ -8347,7 +8521,7 @@ Side effects: populates trails and allit
 =cut
 
 sub extract_categories {
-    my ($categories,$cats,$trails,$allitems,$idx,$jsarray) = @_;
+    my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
     if (ref($categories) eq 'HASH') {
         &gather_categories($categories,$cats,$idx,$jsarray);
         if (ref($cats->[0]) eq 'ARRAY') {
@@ -8368,7 +8542,14 @@ sub extract_categories {
                 if (ref($cats->[1]{$name}) eq 'ARRAY') {
                     for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
                         my $category = $cats->[1]{$name}[$j];
-                        &recurse_categories($cats,2,$category,$trails,$allitems,\@parents);
+                        if (ref($subcats) eq 'HASH') {
+                            push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
+                        }
+                        &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
+                    }
+                } else {
+                    if (ref($subcats) eq 'HASH') {
+                        $subcats->{$item} = [];
                     }
                 }
             }
@@ -8407,7 +8588,7 @@ Side effects: populates trails and allit
 =cut
 
 sub recurse_categories {
-    my ($cats,$depth,$category,$trails,$allitems,$parents) = @_;
+    my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
     my $shallower = $depth - 1;
     if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
@@ -8420,7 +8601,21 @@ sub recurse_categories {
             }
             my $deeper = $depth+1;
             push(@{$parents},$category);
-            &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents);
+            if (ref($subcats) eq 'HASH') {
+                my $subcat = &escape($name).':'.$category.':'.$depth;
+                for (my $j=@{$parents}; $j>=0; $j--) {
+                    my $higher;
+                    if ($j > 0) {
+                        $higher = &escape($parents->[$j]).':'.
+                                  &escape($parents->[$j-1]).':'.$j;
+                    } else {
+                        $higher = &escape($parents->[$j]).'::'.$j;
+                    }
+                    push(@{$subcats->{$higher}},$subcat);
+                }
+            }
+            &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
+                                $subcats);
             pop(@{$parents});
         }
     } else {
@@ -8478,9 +8673,10 @@ sub assign_categories_table {
                             $checked = ' checked="checked" ';
                         }
                     }
-                    $output .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
-                               .'<input type="checkbox" name="usecategory" value="'.
-                               $item.'"'.$checked.' />'.&escape($parent).'</span></td>';
+                    $output .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
+                               '<input type="checkbox" name="usecategory" value="'.
+                               $item.'"'.$checked.' />'.$parent.'</span>'.
+                               '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
                     my $depth = 1;
                     push(@path,$parent);
                     $output .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
@@ -8546,7 +8742,9 @@ sub assign_category_rows {
                     }
                     $text .= '<tr><td><span class="LC_nobreak"><label>'.
                              '<input type="checkbox" name="usecategory" value="'.
-                             $item.'"'.$checked.' />'.$name.'</label></span></td><td>';
+                             $item.'"'.$checked.' />'.$name.'</label></span>'.
+                             '<input type="hidden" name="catname" value="'.$name.'" />'.
+                             '</td><td>';
                     if (ref($path) eq 'ARRAY') {
                         push(@{$path},$name);
                         $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
@@ -9229,7 +9427,7 @@ sub init_user_environment {
 	}
 # Give them a new cookie
 	my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
-		                   : $now);
+		                   : $now.$$.int(rand(10000)));
 	$cookie="$username\_$id\_$domain\_$authhost";
     
 # Initialize roles
@@ -9344,12 +9542,52 @@ sub init_user_environment {
 
 sub _add_to_env {
     my ($idf,$env_data,$prefix) = @_;
-    while (my ($key,$value) = each(%$env_data)) {
-	$idf->{$prefix.$key} = $value;
-	$env{$prefix.$key}   = $value;
+    if (ref($env_data) eq 'HASH') {
+        while (my ($key,$value) = each(%$env_data)) {
+	    $idf->{$prefix.$key} = $value;
+	    $env{$prefix.$key}   = $value;
+        }
+    }
+}
+
+# --- Get the symbolic name of a problem and the url
+sub get_symb {
+    my ($request,$silent) = @_;
+    (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
+    my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
+    if ($symb eq '') {
+        if (!$silent) {
+            $request->print("Unable to handle ambiguous references:$url:.");
+            return ();
+        }
     }
+    &Apache::lonenc::check_decrypt(\$symb);
+    return ($symb);
 }
 
+# --------------------------------------------------------------Get annotation
+
+sub get_annotation {
+    my ($symb,$enc) = @_;
+
+    my $key = $symb;
+    if (!$enc) {
+        $key =
+            &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
+    }
+    my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
+    return $annotation{$key};
+}
+
+sub clean_symb {
+    my ($symb) = @_;
+
+    &Apache::lonenc::check_decrypt(\$symb);
+    my $enc = $env{'request.enc'};
+    delete($env{'request.enc'});
+
+    return ($symb,$enc);
+}
 
 =pod