--- loncom/interface/coursecatalog.pm	2018/12/27 20:10:31	1.98
+++ loncom/interface/coursecatalog.pm	2019/07/23 01:30:35	1.101
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Handler for displaying the course catalog interface
 #
-# $Id: coursecatalog.pm,v 1.98 2018/12/27 20:10:31 raeburn Exp $
+# $Id: coursecatalog.pm,v 1.101 2019/07/23 01:30:35 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -91,24 +91,23 @@ sub handler {
         $r->print(&Apache::loncommon::end_page());
         return OK;
     }
-    my %domconfig =
-        &Apache::lonnet::get_dom('configuration',['coursecategories'],$codedom);
+    my $crscats = &Apache::lonnet::get_dom_cats($codedom);
+    my %domdefaults = &Apache::lonnet::get_domain_defaults($codedom);
     my $knownuser = &user_is_known();
     my $canviewall = &canview_all($knownuser,$codedom);
     
     my ($cathash,$cattype);
-    if (ref($domconfig{'coursecategories'}) eq 'HASH') {
-        $cathash = $domconfig{'coursecategories'}{'cats'};
-        if ($knownuser || $canviewall) {
-            $cattype = $domconfig{'coursecategories'}{'auth'};
-        } else {
-            $cattype = $domconfig{'coursecategories'}{'unauth'};
-        }
-        if ($cattype eq '') {
-            $cattype = 'std';
-        }
+    if (ref($crscats) eq 'HASH') {
+        %{$cathash} = %{$crscats};
     } else {
         $cathash = {};
+    }
+    if ($knownuser || $canviewall) {
+        $cattype = $domdefaults{'catauth'};
+    } else {
+        $cattype = $domdefaults{'catunauth'};
+    }
+    if ($cattype eq '') {
         $cattype = 'std';
     }
     if ($cattype eq 'none') {
@@ -120,7 +119,7 @@ sub handler {
         if ($knownuser || $canviewall) {
             $r->print('<div>'.&mt('No catalog of LON-CAPA courses/communities is provided for: [_1]',$domdesc).'</div>');
         } else {
-            if ($domconfig{'coursecategories'}{'auth'} eq 'none') {
+            if ($domdefaults{'catauth'} eq 'none') {
                 $r->print('<div>'.&mt('No catalog of LON-CAPA courses/communities is provided for: [_1]',$domdesc).'</div>');
             } else {
                 $r->print('<div>'.&mt('The catalog of LON-CAPA courses/communities provided for: "[_1]" is only available to users who are logged in.',$domdesc).'</div>');
@@ -130,7 +129,7 @@ sub handler {
         return OK;
     }
 
-    my $cnum; 
+    my $cnum;
     if ($cattype eq 'codesrch') {
         my ($uniquecode,$codemsg,$brtext);
         if ($env{'form.uniquecode'}) {
@@ -251,7 +250,7 @@ sub handler {
         $toplevelstr =~ s/,$//;
         $maxdepthstr =~ s/,$//;
     }
-    &validate_input(\@cats,\%maxd);
+    &validate_input($codedom,\@cats,\%maxd,$cathash);
     my ($numtitles,@codetitles);
     if (($env{'form.coursenum'} ne '') && ($knownuser)) {
         &course_details($r,$codedom,$formname,$domdesc,\@trails,\%allitems,\@codetitles);
@@ -393,7 +392,7 @@ ENDJS
 }
 
 sub validate_input {
-    my ($cats,$maxd) = @_;
+    my ($codedom,$cats,$maxd,$crscatsref) = @_;
     my $currcat = '';
     my $depth = 0;
     if ($env{'form.catalog_maxdepth'} ne '') {
@@ -450,9 +449,222 @@ sub validate_input {
     }
     $env{'form.currcat_0'} = $currcat;
     $env{'form.catalog_maxdepth'} = $depth;
+
+    my %possibles = (
+        backto => 1,
+        catalogfilter => 1,
+        catalog_maxdepth => 1,
+        courseid => 1,
+        coursenum => 1,
+        currcat_0 => 1,
+        numtitles => 1,
+        Year => 1,
+        Semester => 1,
+        Department => 1,
+        Number => 1,
+        showdom => 1,
+        sortby => 1,
+        showcounts => 1,
+        showdetails => 1,
+        showhidden => 1,
+        showselfenroll => 1,
+        state => 1,
+        uniquecode => 1,
+        wasactive => 1,
+        wasactiveon_day => 1,
+        wasactiveon_month => 1,
+        wasactiveon_year => 1,
+        withsubcats => 1,
+    );
+    my %currcats;
+    if ($env{'form.catalog_maxdepth'} > 0) {
+        for (my $i=1; $i<=$env{'form.catalog_maxdepth'}; $i++) {
+            $currcats{'currcat_'.$i} = 1;
+            $possibles{'currcat_'.$i} = 1;
+        }
+    }
+
+    foreach my $key (sort(keys(%env))) {
+        if ($key =~ /^form\.(.+)$/) {
+            unless ($possibles{$1}) {
+                delete($env{$key});
+            }
+        }
+    }
+    if (exists($env{'form.backto'})) {
+        unless ($env{'form.backto'} eq 'coursecatalog') {
+            delete($env{'form.backto'});
+        }
+    }
+    if (exists($env{'form.catalogfilter'})) {
+        unless (($env{'form.catalogfilter'} eq &mt('Display courses')) ||
+                ($env{'form.catalogfilter'} eq &mt('Display communities')) ||
+                ($env{'form.catalogfilter'} eq &mt('Display placement tests'))) {
+            delete($env{'form.catalogfilter'});
+        }
+    }
+    if (exists($env{'form.courseid'})) {
+        if ($env{'form.courseid'} ne '') {
+            unless ($env{'form.courseid'} =~ /^\Q$codedom\E_$LONCAPA::match_courseid$/) {
+                $env{'form.courseid'} = '';
+            }
+        }
+    }
+    if (exists($env{'form.coursenum'})) {
+        unless ($env{'form.coursenum'} =~ /^$LONCAPA::match_courseid$/) {
+            $env{'form.coursenum'} = '';
+        }
+    }
+    if (exists($env{'form.currcat_0'})) {
+        unless ($env{'form.currcat_0'} =~ /^(instcode|communities|placement)\Q::0\E$/) {
+            if ($env{'form.currcat_0'} =~ /^.+\Q::0\E$/) {
+                if (ref($crscatsref) eq 'HASH') {
+                    unless (exists($crscatsref->{$env{'form.currcat_0'}})) {
+                        delete($env{'form.currcat_0'});
+                    }
+                } else {
+                    delete($env{'form.currcat_0'});
+                }
+            } else {
+                delete($env{'form.currcat_0'});
+            }
+        }
+    }
+    if (exists($env{'form.numtitles'})) {
+        unless ($env{'form.numtitles'} =~ /^\d+$/) {
+            delete($env{'form.numtitles'});
+        }
+    }
+    my ($gotcodes,%possvals);
+    foreach my $item ('Year','Semester','Department','Number') {
+        if (exists($env{'form.'.$item})) {
+            unless ($env{'form.'.$item} eq '0') {
+                unless ($gotcodes) {
+                    $gotcodes = &get_instcode_allowable($codedom,\%possvals);
+                }
+                if (ref($possvals{$item}) eq 'HASH') {
+                    unless (exists($possvals{$item}{$env{'form.'.$item}})) {
+                        delete($env{'form.'.$item});
+                    }
+                } else {
+                    delete($env{'form.'.$item});
+                }
+            }
+        }
+    }
+    if (exists($env{'form.showdom'})) {
+        unless ($env{'form.showdom'} =~ /^$LONCAPA::match_domain$/) {
+            delete($env{'form.showdom'});
+        }
+    }
+    if (exists($env{'form.sortby'})) {
+        unless ($env{'form.sortby'} =~ /^(title|code|owner|cats)$/) {
+            delete($env{'form.sortby'});
+        }
+    }
+    if (exists($env{'form.showcounts'})) {
+        if (ref($env{'form.showcounts'}) eq 'ARRAY') {
+            foreach my $item (@{$env{'form.showcounts'}}) {
+                unless ($item =~ /^(Active|Future|Previous)$/) {
+                    delete($env{'form.showcounts'});
+                    last;
+                }
+            }
+        } else {
+            unless ($env{'form.showcounts'} =~ /^(Active|Future|Previous)$/) {
+                delete($env{'form.showcounts'});
+            }
+        }
+    }
+    foreach my $item ('showhidden','showdetails','showselfenroll','withsubcats') {
+        if (exists($env{'form.'.$item})) {
+            unless ($env{'form.'.$item} eq '1') {
+                delete($env{'form.'.$item});
+            }
+        }
+    }
+    if (exists($env{'form.state'})) {
+        unless ($env{'form.state'} eq 'listing') {
+            delete($env{'form.state'});
+        }
+    }
+    if (exists($env{'form.uniquecode'})) {
+        unless ($env{'form.uniquecode'} =~ /^\w{6}$/) {
+            delete($env{'form.uniquecode'});
+        }
+    }
+    if (exists($env{'form.wasactive'})) {
+        unless (($env{'form.wasactive'} eq 'accessend') || ($env{'form.wasactive'} eq 'enrollend')) {
+            delete($env{'form.wasactive'});
+        }
+    }
+    if (exists($env{'form.wasactiveon_day'})) {
+        my $tmpday = $env{'form.wasactiveon_day'};
+        unless (($tmpday =~ /^\d+$/) && ($tmpday > 0) && ($tmpday < 32)) {
+            delete($env{'form.wasactiveon_day'});
+        }
+    }
+    if (exists($env{'form.wasactiveon_month'})) {
+        my $tmpmonth = $env{'form.wasactiveon_month'};
+        unless (($tmpmonth =~ /^\d+$/) && ($tmpmonth > 0) && ($tmpmonth < 13)) {
+            delete($env{'form.wasactiveon_month'});
+        }
+    }
+    if (exists($env{'form.wasactiveon_year'})) {
+        my $tmpyear = $env{'form.wasactiveon_year'};
+        unless (($tmpyear =~ /^\d+$/) && ($tmpyear >= 1970)) {
+            delete($env{'form.wasactiveon_year'});
+        }
+    }
+    if (keys(%currcats)) {
+        foreach my $key (keys(%currcats)) {
+            if ($env{'form.'.$key} eq '') {
+                delete($env{'form.'.$key});
+            } elsif (ref($crscatsref) eq 'HASH') {
+                unless (exists($crscatsref->{$env{'form.'.$key}})) {
+                    delete($env{'form.'.$key});
+                } else {
+                    delete($env{'form.'.$key});
+                }
+            } else {
+                delete($env{'form.'.$key});
+            }
+        }
+    }
     return;
 }
 
+sub get_instcode_allowable {
+    my ($codedom,$possvalref) = @_;
+    return 1 unless (ref($possvalref) eq 'HASH');
+    my $caller = 'global';
+    my %coursecodes = ();
+    my %codes = ();
+    my @codetitles = ();
+    my %cat_titles = ();
+    my %cat_order = ();
+    my $totcodes = &Apache::courseclassifier::retrieve_instcodes(\%coursecodes,$codedom);
+    if ($totcodes > 0) {
+        if (&Apache::lonnet::auto_instcode_format($caller,$codedom,\%coursecodes,
+                           \%codes,\@codetitles,\%cat_titles,\%cat_order) eq 'ok') {
+            my @standardnames = &Apache::loncommon::get_standard_codeitems();
+            my %local_to_standard;
+            for (my $i=0; $i<@codetitles; $i++) {
+                $local_to_standard{$codetitles[$i]} = $standardnames[$i];
+                $possvalref->{$standardnames[$i]} = {};
+            }
+            foreach my $cid (sort(keys(%codes))) {
+                if (ref($codes{$cid}) eq 'HASH') {
+                    foreach my $item (keys(%{$codes{$cid}})) {
+                        $possvalref->{$local_to_standard{$item}}{$codes{$cid}{$item}} = 1;
+                    }
+                }
+            }
+        }
+    }
+    return 1;
+}
+
 sub course_details {
     my ($r,$codedom,$formname,$domdesc,$trails,$allitems,$codetitles) = @_;
     my $output;