File:  [LON-CAPA] / loncom / interface / coursecatalog.pm
Revision 1.18: download - view: text, annotated - select for diffs
Fri Jan 12 23:37:22 2007 UTC (17 years, 4 months ago) by raeburn
Branches: MAIN
CVS tags: HEAD
bug 5135.  Addresses case where a domain has assigned institutional codes to at least one course, but has not customized the instcode_format() subroutine in localenroll.pm (provided as a stub).

If no category titles have been defined in localenroll::instcode_format(), coursecatalog now displays a listing of all courses in the domain which have
institutional codes.

In the absence of defined categories, there is no "Choose which course(s) to list.", no select boxes and no "Display courses" button.  Sorting by code, title or owner no longer generates javascript errors in this case.

The prior behavior - display of "No official courses to display for <Domain Name>" - when there are no courses with institutional codes, is still the same.

# The LearningOnline Network with CAPA
# Handler for displaying the course catalog interface
#
# $Id: coursecatalog.pm,v 1.18 2007/01/12 23:37:22 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#

package Apache::coursecatalog;

use strict;
use lib qw(/home/httpd/lib/perl);
use Apache::Constants qw(:common);
use Apache::loncommon;
use Apache::lonhtmlcommon;
use Apache::lonnet;
use Apache::lonlocal;
use Apache::courseclassifier;
use Apache::lonacc;
use LONCAPA;

sub handler {
    my ($r) = @_;
    &Apache::loncommon::content_type($r,'text/html');
    $r->send_http_header;
    if ($r->header_only) {
        return OK;
    }
    my %cookies=CGI::Cookie->parse($r->header_in('Cookie'));
    my $lonid=$cookies{'lonID'};
    my $lonidsdir=$r->dir_config('lonIDsDir');
    my $handle;
    if ($lonid) {
	$handle=&LONCAPA::clean_handle($lonid->value);
    }
    if ((-e "$lonidsdir/$handle.id") && ($handle ne '')) {
        &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle);
    }
    &Apache::lonacc::get_posted_cgi($r);
    &Apache::lonlocal::get_language_handle($r);
    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['sortby']);
    my $codedom = $Apache::lonnet::perlvar{'lonDefDomain'};
    my $formname = 'coursecatalog';
    my $domdesc = $Apache::lonnet::domaindescription{$codedom};

    &Apache::lonhtmlcommon::clear_breadcrumbs();
    if ($env{'form.coursenum'} ne '' && &user_is_known()) {
        &course_details($r,$codedom,$formname,$domdesc);
    } else {
        my $numtitles = &course_selector($r,$codedom,$formname,$domdesc);
        if ($env{'form.state'} eq 'listing') {
            $r->print(&print_course_listing($codedom,$numtitles).'<br />');
        }
    }
    $r->print(&Apache::loncommon::end_page());
    return OK;
}

sub course_details {
    my ($r,$codedom,$formname,$domdesc) = @_;
    my $output;
    my %add_entries = (topmargin    => "0",
                       marginheight => "0",);
    my $start_page =
        &Apache::loncommon::start_page('Course Catalog','',
                                           {
                                             'add_entries' => \%add_entries,
                                             'no_inline_link'   => 1,});
    $r->print($start_page);
    &Apache::lonhtmlcommon::add_breadcrumb
            ({href=>"/adm/coursecatalog",
              text=>"Select courses"},
             {href=>"javascript:document.$formname.submit()",
              text=>"Course listing"},
             {text=>"Course details"});
    $r->print(&Apache::lonhtmlcommon::breadcrumbs('Course Details'));
    $r->print('<br />'.&mt('Detailed course information:').'<br /><br />'.
              '<form name="coursecatalog" method="post">'.
              &print_course_listing($codedom).'<br /><br />');
    $r->print('<a href = "javascript:document.coursecatalog.submit()">'.
              &mt('Back to course listing').'</a>'.
              '<input type="hidden" name="sortby" value="'.
              $env{'form.sortby'}.'" />'.
              '<input type="hidden" name="state" value="listing" /></form>');
}

sub course_selector {
    my ($r,$codedom,$formname,$domdesc) = @_;
    my %coursecodes = ();
    my %codes = ();
    my @codetitles = ();
    my %cat_titles = ();
    my %cat_order = ();
    my %idlist = ();
    my %idnums = ();
    my %idlist_titles = ();
    my %by_year;
    my %by_sem;
    my %by_dept;
    my %cat_items;
    my $caller = 'global';
    my $format_reply;
    my $totcodes = 0;
    my $jscript = '';
    my ($numtitles,$lasttitle);
    $totcodes = &Apache::courseclassifier::retrieve_instcodes(\%coursecodes,$codedom,$totcodes);
    if ($totcodes > 0) {
        $format_reply = &Apache::lonnet::auto_instcode_format($caller,$codedom,\%coursecodes,\%codes,\@codetitles,\%cat_titles,\%cat_order);
        if ($format_reply eq 'ok') {
            my $numtypes = @codetitles;
            &Apache::courseclassifier::build_code_selections(\%codes,\@codetitles,\%cat_titles,\%cat_order,\%idlist,\%idnums,\%idlist_titles);
            my ($scripttext,$longtitles) = &Apache::courseclassifier::javascript_definitions(\@codetitles,\%idlist,\%idlist_titles,\%idnums,\%cat_titles);
            my $longtitles_str = join('","',@{$longtitles});
            my $allidlist = $idlist{$codetitles[0]};
            $numtitles = @codetitles;
            $lasttitle = $numtitles;
            if ($numtitles > 4) {
                $lasttitle = 4;
            }
            if ($numtitles == 0) {
                if (!defined($env{'form.state'})) {
                    $env{'form.state'} = 'listing';
                }
            } else {
                my @data = ('top');
                for (my $k=0; $k<$lasttitle; $k++) {
                    my $cat = $codetitles[$k];
                    my $level = 1;
                    $level = &recurse_options($codetitles[$k],$idlist{$codetitles[$k]},$level,$cat,\%cat_items,\@data,\%by_year,\%by_sem,\%by_dept);
                }
                $scripttext .= &build_javascript(\%by_year,\%by_sem,\%by_dept,\%cat_order,\@codetitles);
                $jscript .= &javascript_select_filler($formname,$scripttext,\@codetitles,$longtitles_str,$allidlist);
                if ($env{'form.state'} eq 'listing') {
                    $jscript .= '
function setElements() {
';
                    for (my $i=0; $i<@codetitles-1; $i++) {
                        if ($env{'form.'.$codetitles[$i]} != -1) {
                            $jscript .= '
    for (var j=0; j<document.'.$formname.'.'.$codetitles[$i].'.length; j++) {
        if (document.'.$formname.'.'.$codetitles[$i].'[j].value == "'.$env{'form.'.$codetitles[$i]}.'") {
            document.'.$formname.'.'.$codetitles[$i].'.selectedIndex = j;
        }
    }
';
                        }
                    }
                    $jscript .= '   courseSet()'."\n";
                    if ($env{'form.'.$codetitles[-1]} != -1) {
                        $jscript .= '
    for (var j=0; j<document.'.$formname.'.'.$codetitles[-1].'.length; j++) {
        if (document.'.$formname.'.'.$codetitles[-1].'[j].value == "'.$env{'form.'.$codetitles[-1]}.'") {
            document.'.$formname.'.'.$codetitles[-1].'.selectedIndex = j;
        }
    }
';
                    }
                    $jscript .= '}';
                }
            }
            if ($env{'form.state'} eq 'listing') {
                $jscript .= qq|
function changeSort(caller) {
    document.$formname.sortby.value = caller;
    document.$formname.submit();
}
function setCourseId(caller) {
   document.$formname.coursenum.value = caller;
   document.$formname.submit(); 
}\n|;
            }
        }
        my $js = '<script type"text/javascript">'."\n$jscript\n".
                 '</script>';
        my %add_entries = (topmargin    => "0",
                           marginheight => "0",);
        if (($env{'form.state'} eq 'listing') && ($numtitles > 0)) {
            $add_entries{'onLoad'} = 'setElements()';
        }
        my $start_page =
            &Apache::loncommon::start_page('Course Catalog',$js,
                                           {
                                             'add_entries' => \%add_entries,
                                             'no_inline_link'   => 1,});
        $r->print($start_page);
        if ($env{'form.state'} eq 'listing') {
            &Apache::lonhtmlcommon::add_breadcrumb
            ({href=>"/adm/coursecatalog",
              text=>"Select courses"},
             {text=>"Course listing"});
             $r->print(&Apache::lonhtmlcommon::breadcrumbs('Course Listing'));
        } else {
            &Apache::lonhtmlcommon::add_breadcrumb
            ({href=>"/adm/coursecatalog",
              text=>"Select courses"});
            $r->print(&Apache::lonhtmlcommon::breadcrumbs('Select courses'));
        }
        $r->print('<h3>'.&mt('Display information about official [_1] classes for which LON-CAPA courses have been created:',$domdesc).'</h3>');
        $r->print('<form name="coursecatalog" method="post">');
        if ($numtitles > 0) {
            $r->print(&mt('<b>Choose which course(s) to list.</b><br />'));
            $r->print('<table><tr>');
            for (my $k=0; $k<$lasttitle-1; $k++) {
                my @unsorted = @{$cat_items{$codetitles[$k]}};
                my @items;
                &Apache::courseclassifier::sort_cats($k,\%cat_order,\@codetitles,\@unsorted,\@items);
                my @longitems;
                if (defined($cat_titles{$codetitles[$k]})) {
                    foreach my $item (@items) {
                        push(@longitems,$cat_titles{$codetitles[$k]}{$item});
                    }
                } else {
                    @longitems = @items;
                }
                $r->print('<td align="center">'.$codetitles[$k].'<br />'."\n".
                          '<select name="'.$codetitles[$k].'" onChange="courseSet()"');
                $r->print('>'."\n".'<option value="0" />All'."\n");
                for (my $i=0; $i<@items; $i++) {
                    if ($longitems[$i] eq '') {
                        $longitems[$i] = $items[$i];
                    }
                    $r->print(' <option value="'.$items[$i].'">'.$longitems[$i].'</option>');
                }
                $r->print('</select></td>');
            }
            $r->print('<td align="center">'.$codetitles[$lasttitle-1].'<br />'."\n".
                      '<select name="'.$codetitles[$lasttitle-1].'">'."\n".
                      '<option value="0">All'."\n".
                      '</option>'."\n".'</select>'."\n".
                 '</td>'
                );
            if (&user_is_dc($codedom)) {
                my $showdetails_status;
                if ($env{'form.showdetails'}) {
                    $showdetails_status = 'checked="checked" ';
                } 
                $r->print('<td></td><td><input type="checkbox" name="showdetails" value="1" '.$showdetails_status.'/>'.&mt('Show full details for each course (DC only)').'</td>');
            }
            $r->print('</tr></table>');
            if ($numtitles > 4) {
                $r->print('<br /><br />'.$codetitles[$numtitles-1].'<br />'."\n".
                '<input type="text" name="'.$codetitles[$numtitles-1].'" /><br />'."\n");
            }
            $r->print('<br />');
        }
        $r->print('<input type="hidden" name="coursenum" value="" /><input type="hidden" name="sortby" value="" /><input type="hidden" name="state" value="listing" />');
        if ($numtitles > 0) {
            $r->print('<input type="submit" name="catalogfilter" value="'.&mt('Display courses').'" />');
        }
        $r->print('</form>');
        if (($numtitles > 0) && ($env{'form.state'} eq 'listing')) {
            $r->print('<br /><br />');
        }
    } else {
        $r->print(&Apache::loncommon::start_page('Course Catalog','',
                  {
                   'no_inline_link'   => 1,}));
        $r->print('<br />'.&mt('No official courses to display for [_1].',$domdesc));
    }
    return $numtitles;
}

sub user_is_dc {
    my ($codedom) = @_;
    if (exists($env{'user.role.dc./'.$codedom.'/'})) {
        my $livedc = 1;
        my $now = time;
        my ($start,$end)=split(/\./,$env{'user.role.dc./'.$codedom.'/'});
        if ($start && $start>$now) { $livedc = 0; }
        if ($end   && $end  <$now) { $livedc = 0; }
        return $livedc;
    }
    return;
}

sub recurse_options {
    my ($currkey,$currlist,$level,$cat,$cat_options,$data,$by_year,$by_sem,$by_dept) = @_;
    if (ref($currlist) eq 'HASH') {
        $level ++;
        foreach my $key (sort(keys(%{$currlist}))) {
            $$data[$level-1]= $key;
            &recurse_options($key,$currlist->{$key},$level,$cat,$cat_options,$data,$by_year,$by_sem,$by_dept);
        }
    } else {
        $level --;
        my @contents = split(/","/,$currlist);
        foreach my $item (@contents) {
            if (!grep(/^\Q$item\E$/,@{$cat_options->{$cat}})) {
                push(@{$cat_options->{$cat}},$item);
            }
            if ($level == 3) {
                if (!grep/^\Q$item\E$/,@{$by_year->{$data->[1]}->{$currkey}}) {
                    push(@{$by_year->{$data->[1]}->{$currkey}},$item);                 
                }
                if (!grep/^\Q$item\E$/,@{$by_sem->{$data->[2]}->{$currkey}}) {
                    push(@{$by_sem->{$data->[2]}->{$currkey}},$item);
                }
                if (!grep/^\Q$item\E$/,@{$by_dept->{$currkey}}) {
                    push(@{$by_dept->{$currkey}},$item);
                }

            }
        }
    }
    return $level;
}

sub build_javascript {
    my ($by_year,$by_sem,$by_dept,$cat_order,$codetitles) = @_;
    my @unsorted = keys(%{$by_year});
    my @sorted_yrs; 
    &Apache::courseclassifier::sort_cats('0',$cat_order,$codetitles,\@unsorted,\@sorted_yrs);
    my $output = 'var idcse_by_yr_year = new Array("'.join('","',@sorted_yrs).'");'."\n".
                 'var idcse_by_yr_dept = new Array('.scalar(@sorted_yrs).');'."\n".
                 'var idcse_by_yr_num = new Array('.scalar(@sorted_yrs).');'."\n";
    for (my $i=0; $i<@sorted_yrs; $i++) {
        my $numkeys = keys(%{$by_year->{$sorted_yrs[$i]}});
        $output .= " idcse_by_yr_num[$i] = new Array($numkeys);\n";
        if (ref($by_year->{$sorted_yrs[$i]}) eq 'HASH') {
            @unsorted = keys(%{$by_year->{$sorted_yrs[$i]}});
            my @sorted_depts;
            &Apache::courseclassifier::sort_cats('2',$cat_order,$codetitles,\@unsorted,\@sorted_depts);
            $output .= qq| idcse_by_yr_dept[$i] = new Array ("|.join('","',@sorted_depts).'");'."\n";
            for (my $j=0; $j<@sorted_depts; $j++) {
                $output .= qq| idcse_by_yr_num[$i][$j] = new Array ("|;
                $output .= join('","',sort(@{$by_year->{$sorted_yrs[$i]}->{$sorted_depts[$j]}})).'");'."\n";
            }
        }
    }
    @unsorted = keys(%{$by_sem});
    my @sorted_sems;
    &Apache::courseclassifier::sort_cats('1',$cat_order,$codetitles,\@unsorted,\@sorted_sems);
    $output .=  'idcse_by_sem_sems = new Array("'.join('","',@sorted_sems).'");'."\n".
                'idcse_by_sem_dept = new Array('.scalar(@sorted_sems).');'."\n".
                'idcse_by_sem_num = new Array('.scalar(@sorted_sems).');'."\n";
    for (my $i=0; $i<@sorted_sems; $i++) {
        my $numkeys = keys(%{$by_sem->{$sorted_sems[$i]}});
        $output .= " idcse_by_sem_num[$i] = new Array($numkeys);\n";
        if (ref($by_sem->{$sorted_sems[$i]}) eq 'HASH') {
            @unsorted = keys(%{$by_sem->{$sorted_sems[$i]}});
            my @sorted_depts;
            &Apache::courseclassifier::sort_cats('2',$cat_order,$codetitles,\@unsorted,\@sorted_depts);
            $output .= qq| idcse_by_sem_dept[$i] = new Array("|.join('","',@sorted_depts).'");'."\n";
            for (my $j=0; $j<@sorted_depts; $j++) {
                $output .= qq| idcse_by_sem_num[$i][$j] = new Array ("|.join('","',sort(@{$by_sem->{$sorted_sems[$i]}->{$sorted_depts[$j]}})).'");'."\n";
            }
        }
    }
    @unsorted = keys(%{$by_dept});
    my @sorted_deps;
    &Apache::courseclassifier::sort_cats('2',$cat_order,$codetitles,\@unsorted,\@sorted_deps);
    $output .= 'idcse_by_dep = new Array('.scalar(@sorted_deps).');'."\n"; 
    for (my $k=0; $k<@sorted_deps; $k++) {
        $output .= qq| idcse_by_dep[$k] = new Array ("|.join('","',sort(@{$by_dept->{$sorted_deps[$k]}})).'");'."\n";
    }
    return $output;
}

sub search_courselist {
    my ($domain,$numtitles) = @_;
    my $instcode;
    if (defined($numtitles) && $numtitles == 0) {
        $instcode = '.+';
    } else {
        my (%codedefaults,@code_order);
        my $defaults_result = 
            &Apache::lonnet::auto_instcode_defaults($domain,\%codedefaults,
                                                    \@code_order);
        if ($defaults_result eq 'ok') {
            $instcode ='^';
            foreach my $item (@code_order) {
                if ($env{'form.'.$item} eq '0' ) {
                    $instcode .= $codedefaults{$item}; 
                } else {
                    $instcode .= $env{'form.'.$item};
                }
            }
            $instcode .= '$';
        } else {
            $instcode = '.';
        }
    }
    my %courses = &Apache::lonnet::courseiddump($domain,'.',1,$instcode,'.','.',
                                                undef,undef,'Course',1);
    return %courses;
}


sub print_course_listing {
    my ($domain,$numtitles) = @_;
    my $output;
    my %courses;
    my $knownuser = &user_is_known();
    my $details = $env{'form.coursenum'};
    if (&user_is_dc($domain)) {
        if ($env{'form.showdetails'}) {
            $details = 1;
        }
    }
    if ($env{'form.coursenum'} ne '') {
        %courses = &Apache::lonnet::courseiddump($domain,'.',1,'.','.',
                                                 $env{'form.coursenum'},
                                                 undef,undef,'Course');
        if (keys(%courses) == 0) {
            $output .= &mt('The courseID provided does not match a course in this domain.');
            return $output;
        }
    } else {
        %courses = &search_courselist($domain,$numtitles);
        if (keys(%courses) == 0) {
            $output = &mt('No courses match the criteria you selected.');
            return $output;
        }
        if ($knownuser && !$env{'form.showdetails'}) {
            $output = &mt('<b>Note for students:</b> If you are officially enrolled in a course but the course is not listed in your LON-CAPA courses, click the "Show more details" link for the specific course and check the default access dates and/or automated enrollment settings.<br /><br />');
        }
    }
    $output .= &construct_data_table($knownuser,\%courses,$details);
    $output .= &Apache::lonhtmlcommon::echo_form_input(['coursenum','state','catalogfilter','sortby','showdetails']);
    return $output;
}

sub construct_data_table {
    my ($knownuser,$courses,$details,$usersections) = @_;
    my %sortname;
    if (($details eq '') || ($env{'form.showdetails'})) {
        $sortname{'Code'} = 'code';
        $sortname{'Title'} = 'title';
        $sortname{'Owner'} = 'owner';
    }
    my $output = &Apache::loncommon::start_data_table().
                 &Apache::loncommon::start_data_table_header_row();
    my @coltitles = ('Code','Sections','Crosslisted','Title','Owner');
    if (ref($usersections) eq 'HASH') {
       $coltitles[1] = 'Your Section';
    }
    foreach my $item (@coltitles) {
        $output .= '<th>';
        if (defined($sortname{$item})) {
            $output .= '<a href="javascript:changeSort('."'$sortname{$item}'".')">'.&mt($item).'</a>';
        } else {
            $output .= &mt($item);
        }
        $output .= '</th>';
    }
    if ($knownuser) {
        if ($details) {
            $output .=
              '<th>'.&mt('Default Access Dates for Students').'</th>'.
              '<th>'.&mt('Student Counts').'</th>'.
              '<th>'.&mt('Auto-enrollment of <br />registered students').'</th>';
        } else {
            $output .= '<th>&nbsp;</th>';
        }
    }
    &Apache::loncommon::end_data_table_header_row();
    my %courseinfo = &build_courseinfo_hash($courses,$knownuser,$details,
                                            $usersections);
    my %Sortby;
    foreach my $course (sort(keys(%{$courses}))) {
        if ($env{'form.sortby'} eq 'code') {
            push(@{$Sortby{$courseinfo{$course}{'code'}}},$course);
        } elsif ($env{'form.sortby'} eq 'owner') {
            push(@{$Sortby{$courseinfo{$course}{'ownerlastname'}}},$course);
        } else {
            push(@{$Sortby{$courseinfo{$course}{'title'}}},$course);
        }
    }
    my @sorted_courses;
    if (($env{'form.sortby'} eq 'code') || ($env{'form.sortby'} eq 'owner')) {
        @sorted_courses = sort(keys(%Sortby));
    } else {
        @sorted_courses = sort { lc($a) cmp lc($b) } (keys(%Sortby));
    }
    foreach my $item (@sorted_courses) {
        foreach my $course (@{$Sortby{$item}}) {
            $output.=&Apache::loncommon::start_data_table_row(); 
            $output.=&courseinfo_row($courseinfo{$course},$knownuser,$details);
            $output.=&Apache::loncommon::end_data_table_row();
        }
    }
    $output .= &Apache::loncommon::end_data_table();
    return $output;
}

sub build_courseinfo_hash {
    my ($courses,$knownuser,$details,$usersections) = @_;
    my %courseinfo;
    my $now = time;
    foreach my $course (keys(%{$courses})) {
        my $descr;
        if ($courses->{$course} =~ m/^([^:]*):/i) {
            $descr = &unescape($1);
        } else {
            $descr = &unescape($courses->{$course});
        }
        my $cleandesc=&HTML::Entities::encode($descr,'<>&"');
        $cleandesc=~s/'/\\'/g;
        $cleandesc =~ s/^\s+//;
        my ($cdom,$cnum)=split(/\_/,$course);

        my ($desc,$instcode,$owner,$ttype) = split(/:/,$courses->{$course});
        $owner = &unescape($owner);
        my ($ownername,$ownerdom);
        if ($owner =~ /:/) {
            ($ownername,$ownerdom) = split(/:/,$owner);
        } else {
            $ownername = $owner;
            if ($owner ne '') {
                $ownerdom = $cdom;
            }
        }
        my %ownernames;
        if ($ownername ne '' && $ownerdom ne '') {
            %ownernames = &Apache::loncommon::getnames($ownername,$ownerdom);
        }
        $courseinfo{$course}{'cdom'} = $cdom;
        $courseinfo{$course}{'cnum'} = $cnum;
        $courseinfo{$course}{'code'} = $instcode;
        $courseinfo{$course}{'ownerlastname'} = $ownernames{'lastname'};
        $courseinfo{$course}{'title'} = $cleandesc;
        $courseinfo{$course}{'owner'} = $owner;

        my %coursehash = &Apache::lonnet::dump('environment',$cdom,$cnum);
        my @classids;
        my @crosslistings;
        my ($seclist,$numsec) = 
            &identify_sections($coursehash{'internal.sectionnums'});
        if (ref($usersections) eq 'HASH') {
            if (ref($usersections->{$course}) eq 'ARRAY') {
                $seclist = join(', ',@{$usersections->{$course}});
            }
        }
        $courseinfo{$course}{'seclist'} = $seclist;
        my ($xlist_items,$numxlist) = 
            &identify_sections($coursehash{'internal.crosslistings'});
        my $showsyllabus = 1; # default is to include a syllabus link
        if (defined($coursehash{'showsyllabus'})) {
            $showsyllabus = $coursehash{'showsyllabus'};
        }
        $courseinfo{$course}{'showsyllabus'} = $showsyllabus;
        if (((defined($env{'form.coursenum'}) && ($cnum eq $env{'form.coursenum'}))) ||
            ($knownuser && ($details == 1))) {  
            $courseinfo{$course}{'counts'} =  &count_students($cdom,$cnum,$numsec);
            $courseinfo{$course}{'autoenrollment'} =
                &autoenroll_info(\%coursehash,$now,$seclist,$xlist_items,
                                 $instcode,$owner,$cdom,$cnum);

            my $startaccess = '';
            my $endaccess = '';
            my $accessdates;
            if ( defined($coursehash{'default_enrollment_start_date'}) ) {
                $startaccess = &Apache::lonlocal::locallocaltime($coursehash{'default_enrollment_start_date'});
            }
            if ( defined($coursehash{'default_enrollment_end_date'}) ) {
                $endaccess = &Apache::lonlocal::locallocaltime($coursehash{'default_enrollment_end_date'});
                if ($coursehash{'default_enrollment_end_date'} == 0) {
                    $endaccess = "No ending date";
                }
            }
            if ($startaccess) {
                $accessdates .= &mt('<i>From:</i> ').$startaccess.'<br />';
            }
            if ($endaccess) {
                $accessdates .= &mt('<i>To:</i> ').$endaccess.'<br />';
            }
            $courseinfo{$course}{'access'} = $accessdates;
        }
        if ($xlist_items eq '') {
            $xlist_items = &mt('No');
        }
        $courseinfo{$course}{'xlist'} = $xlist_items;
    }
    return %courseinfo;
}

sub count_students {
    my ($cdom,$cnum,$numsec) = @_;
    my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cnum);
    my %student_count = (
                           Active => 0,
                           Future => 0,
                           Expired => 0,
                       );
    my %idx;
    $idx{'status'} = &Apache::loncoursedata::CL_STATUS();
    my %status_title = &Apache::lonlocal::texthash(
                           Expired => 'Previous access',
                           Active => 'Current access',
                           Future => 'Future access',
                       );

    while (my ($student,$data) = each(%$classlist)) {
        $student_count{$data->[$idx{'status'}]} ++;
    }

    my $countslist = &mt('[quant,_1,section]',$numsec).':<br />';
    foreach my $status ('Active','Future') {
        $countslist .= '<nobr>'.$status_title{$status}.': '.
                       $student_count{$status}.'</nobr><br />';
    }
    return $countslist;
}

sub courseinfo_row {
    my ($info,$knownuser,$details) = @_;
    my ($cdom,$cnum,$title,$ownerlast,$code,$owner,$seclist,$xlist_items,
        $accessdates,$showsyllabus,$counts,$autoenrollment,$output);
    if (ref($info) eq 'HASH') {
        $cdom = $info->{'cdom'};
        $cnum = $info->{'cnum'};
        $title = $info->{'title'};
        $ownerlast = $info->{'ownerlastname'};
        $code = $info->{'code'};
        $owner = $info->{'owner'};
        $seclist = $info->{'seclist'};
        $xlist_items = $info->{'xlist'};
        $accessdates = $info->{'access'};
        $counts = $info->{'counts'};
        $autoenrollment = $info->{'autoenrollment'};
        $showsyllabus = $info->{'showsyllabus'};
    } else {
        $output = '<td colspan="8">'.&mt('No information available for [_1].',
                                         $code).'</td>';
        return $output;
    }
    $output .= '<td>'.$code.'</td>'.
               '<td>'.$seclist.'</td>'.
               '<td>'.$xlist_items.'</td>'.
               '<td>'.$title.'&nbsp;<font size="-2">';
    if ($showsyllabus) {
        $output .= &Apache::loncommon::syllabuswrapper(&mt('Syllabus'),$cnum,$cdom);
    } else {
        $output .= '&nbsp;';
    }
    $output .= '</font></td>'.
               '<td>'.$ownerlast.'</td>';
    if ($knownuser) {
        if ($details) {
            $output .=
               '<td>'.$accessdates.'</td>'. 
               '<td>'.$counts.'</td>'.
               '<td>'.$autoenrollment.'</td>';
        } else {
            $output .= "<td><a href=\"javascript:setCourseId('$cnum')\">".&mt('Show more details').'</a></td>';
        }
    }
    return $output;
}

sub identify_sections {
    my ($seclist) = @_;
    my @secnums;
    if ($seclist =~ /,/) {
        my @sections = split(/,/,$seclist);
        foreach my $sec (@sections) {
            $sec =~ s/:[^:]*$//;
            push(@secnums,$sec);
        }
    } else {
        if ($seclist =~ m/^([^:]+):/) {
            my $sec = $1;
            if (!grep(/^\Q$sec\E$/,@secnums)) {
                push(@secnums,$sec);
            }
        }
    }
    @secnums = sort {$a <=> $b} @secnums;
    my $seclist = join(', ',@secnums);
    my $numsec = @secnums;
    return ($seclist,$numsec);
}

sub get_valid_classes {
    my ($seclist,$xlist_items,$crscode,$owner,$cdom,$cnum) = @_;
    my $response;
    my %validations;
    @{$validations{'sections'}} = ();
    @{$validations{'xlists'}} = ();
    my $totalitems = 0;
    if ($seclist) {
        foreach my $sec (split(/, /,$seclist)) {
            my $class = $crscode.$sec;
            if (&Apache::lonnet::auto_validate_class_sec($cdom,$cnum,$owner,
							 $class) eq 'ok') {
                if (!grep(/^\Q$sec$\E/,@{$validations{'sections'}})) {
                    push(@{$validations{'sections'}},$sec);
                    $totalitems ++;
                }
            }
        }
    }
    if ($xlist_items) {
        foreach my $item (split(/, /,$xlist_items)) {
            if (&Apache::lonnet::auto_validate_class_sec($cdom,$cnum,$owner,
							 $item) eq 'ok') {
                if (!grep(/^\Q$item$\E/,@{$validations{'xlists'}})) {
                    push(@{$validations{'xlists'}},$item);
                    $totalitems ++;
                }
            }
        }
    }
    if ($totalitems > 0) {
        if (@{$validations{'sections'}}) {
            $response = &mt('Sections: ').
                        join(', ',@{$validations{'sections'}}).'<br />';
        }
        if (@{$validations{'xlists'}}) {
            $response .= &mt('Courses: ').
                        join(', ',@{$validations{'xlists'}});
        }
    }
    return $response;
}

sub javascript_select_filler {
    my ($formname,$scripttext,$codetitles,$longtitles_str,$allidlist) = @_;
    my $output = <<END;
function courseSet() {
    var longtitles = new Array ("$longtitles_str");
    var valyr = document.$formname.Year.options[document.$formname.Year.selectedIndex].value
    var valsem  = document.$formname.Semester.options[document.$formname.Semester.selectedIndex].value
    var valdept = document.$formname.Department.options[document.$formname.Department.selectedIndex].value
    var valclass = document.$formname.Number.options[document.$formname.Number.selectedIndex].value
    var idyears = new Array("$allidlist");
    var idyr = -1;
    var idsem = -1;
    var iddept = -1;
    document.$formname.Number.length = 0;

    $scripttext

    selYear = document.$formname.Year.selectedIndex-1;
    selSemester = document.$formname.Semester.selectedIndex-1;
    selDepartment = document.$formname.Department.selectedIndex-1;
    if (selYear == -1) {
        if (selSemester == -1) {
            if (selDepartment > -1) {
                document.$formname.Number.options[0] =  new Option('All','0',false,false);
                for (var k=0; k<idcse_by_dep[selDepartment].length; k++) {
                    document.$formname.Number.options[k+1] = new Option(idcse_by_dep[selDepartment][k],idcse_by_dep[selDepartment][k],false,false);

                }
            } 
            else {
                document.$formname.Number.options[0] = new Option("All","0",true,true);
            }
        }
        else {
            if (selDepartment > -1) {
                for (var i=0; i<idcse_by_sem_sems.length; i++) {
                    if (idcse_by_sem_sems[i] == valsem) {
                        idsem = i;
                    }
                }
                if (idsem != -1) {
                    for (var i=0; i<idcse_by_sem_dept[idsem].length; i++) {
                        if (idcse_by_sem_dept[idsem][i] == valdept) {
                            iddept = i;
                        }
                    }
                }
                if (iddept != -1) {
                    document.$formname.Number.options[0] =  new Option('All','0',false,false);
                    for (var k=0; k<idcse_by_sem_num[idsem][iddept].length; k++) {
                        document.$formname.Number.options[k+1] = new Option(idcse_by_sem_num[idsem][iddept][k],idcse_by_sem_num[idsem][iddept][k],false,false);
                    }
                }
                else {
                    document.$formname.Number.options[0] =  new Option('No courses','0',true,true);
                }
            }
            else {
                document.$formname.Number.options[0] = new Option("All","0",true,true);
            }
        }
    }
    else {
        if (selSemester == -1) {
            if (selDepartment > -1) {
                for (var i=0; i<idcse_by_yr_year.length; i++) {
                    if (idcse_by_yr_year[i] == valyr) {
                        idyr = i;
                    }
                }
                if (idyr != -1) {      
                    for (var i=0; i<idcse_by_yr_dept[idyr].length; i++) {
                        if (idcse_by_yr_dept[idyr][i] == valdept) {
                            iddept = i;
                        }
                    }
                }
                if (iddept != -1) {
                    document.$formname.Number.options[0] =  new Option('All','0',false,false);
                    for (var k=0; k<idcse_by_yr_num[idyr][iddept].length; k++) {
                        document.$formname.Number.options[k+1] = new Option(idcse_by_yr_num[idyr][iddept][k],idcse_by_yr_num[idyr][iddept][k],false,false);
                    }
                } 
                else {
                    document.$formname.Number.options[0] =  new Option('No courses','0',true,true);
                }
            }
            else {
                document.$formname.Number.options[0] = new Option("All","0",true,true);
            }
        }
        else {
            if (selDepartment > -1) {
                for (var k=0; k<idyears.length; k++) {
                    if (idyears[k] == valyr) {
                        idyr = k;
                    }
                }
                if (idyr != -1) {
                    for (var k=0; k<idsems[idyr].length; k++) {
                        if (idsems[idyr][k] == valsem) {
                            idsem = k;
                        }
                    }
                }
                if (idsem != -1) {
                    for (var k=0; k<idcodes[idyr][idsem].length; k++) {
                        if (idcodes[idyr][idsem][k] == valdept) {
                            iddept = k;
                        }
                    }
                }
                if (iddept != -1) {
                    document.$formname.Number.options[0] =  new Option('All','0',false,false);
                    for (var i=0; i<idcourses[idyr][idsem][iddept].length; i++) {
                        var display = idcourses[idyr][idsem][iddept][i];
                        if (longtitles[3] == 1) {
                            if (idcourseslongs[idyr][idsem][iddept][i] != "") {
                                display = idcourseslongs[idyr][idsem][iddept][i]
                            }
                        }
                        document.$formname.Number.options[i+1] = new Option(display,idcourses[idyr][idsem][iddept][i],false,false)
                    }
                } 
                else {
                    document.$formname.Number.options[0] =  new Option('No courses','0',true,true);
                }
            } 
            else {
                document.$formname.Number.options[0] =  new Option('All','0',true,true);
            }
        }
        document.$formname.Number.selectedIndex = 0
    }
}
END
    return $output;
}

sub autoenroll_info {
    my ($coursehash,$now,$seclist,$xlist_items,$code,$owner,$cdom,$cnum) = @_;
    my $autoenrolldates = &mt('Not enabled');
    if (defined($coursehash->{'internal.autoadds'}) && $coursehash->{'internal.autoadds'} == 1) {
        my ($autostart,$autoend);
        if ( defined($coursehash->{'internal.autostart'}) ) {
            $autostart = &Apache::lonlocal::locallocaltime($coursehash->{'internal.autostart'});
        }
        if ( defined($coursehash->{'internal.autoend'}) ) {
            $autoend = &Apache::lonlocal::locallocaltime($coursehash->{'internal.autoend'});
        }
        if ($coursehash->{'internal.autostart'} > $now) {
            if ($coursehash->{'internal.autoend'} && $coursehash->{'internal.autoend'} < $now) {
                $autoenrolldates = &mt('Not enabled');
            } else {
                my $valid_classes = 
                   &get_valid_classes($seclist,$xlist_items,$code,
                                      $owner,$cdom,$cnum);
                if ($valid_classes ne '') {
                    $autoenrolldates = &mt('Not enabled<br />Starts: ').
                                       $autostart.'<br />'.$valid_classes;                }
            }
        } else {
            if ($coursehash->{'internal.autoend'} && $coursehash->{'internal.autoend'} < $now) {
                $autoenrolldates = &mt('Not enabled<br />Ended: ').$autoend;
            } else {
                my $valid_classes = &get_valid_classes($seclist,$xlist_items,
                                                       $code,$owner,$cdom,$cnum);
                if ($valid_classes ne '') {
                    $autoenrolldates = &mt('Currently enabled<br />').
                                       $valid_classes;
                }
            }
        }
    }
    return $autoenrolldates;
}

sub user_is_known {
    my $known = 0;
    if ($env{'user.name'} ne '' && $env{'user.name'} ne 'public' 
        && $env{'user.domain'} ne '' && $env{'user.domain'} ne 'public') {
        $known = 1;
    }
    return $known;
}

1;

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>