Annotation of loncom/interface/coursecatalog.pm, revision 1.5

1.1       raeburn     1: #
                      2: # Copyright Michigan State University Board of Trustees
                      3: #
                      4: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      5: #
                      6: # LON-CAPA is free software; you can redistribute it and/or modify
                      7: # it under the terms of the GNU General Public License as published by
                      8: # the Free Software Foundation; either version 2 of the License, or
                      9: # (at your option) any later version.
                     10: #
                     11: # LON-CAPA is distributed in the hope that it will be useful,
                     12: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     13: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     14: # GNU General Public License for more details.
                     15: #
                     16: # You should have received a copy of the GNU General Public License
                     17: # along with LON-CAPA; if not, write to the Free Software
                     18: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     19: #
                     20: # /home/httpd/html/adm/gpl.txt
                     21: #
                     22: # http://www.lon-capa.org/
                     23: #
                     24: 
                     25: package Apache::coursecatalog;
                     26: 
                     27: use strict;
                     28: use lib qw(/home/httpd/lib/perl);
                     29: use Apache::Constants qw(:common);
                     30: use Apache::loncommon;
                     31: use Apache::lonnet;
                     32: use Apache::lonlocal;
                     33: use Apache::lonsupportreq;
                     34: use Apache::lonacc;
                     35: use lib '/home/httpd/lib/perl/';
                     36: use LONCAPA;
                     37: 
                     38: sub handler {
                     39:     my ($r) = @_;
                     40:     &Apache::loncommon::content_type($r,'text/html');
                     41:     $r->send_http_header;
                     42:     if ($r->header_only) {
                     43:         return OK;
                     44:     }
                     45:     &Apache::lonacc::get_posted_cgi($r);
                     46:     &Apache::lonlocal::get_language_handle($r);
                     47:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['sortby']);
                     48:     my $codedom = $Apache::lonnet::perlvar{'lonDefDomain'};
                     49:     my $ccode = '';
                     50:     my %coursecodes = ();
                     51:     my %codes = ();
                     52:     my @codetitles = ();
                     53:     my %cat_titles = ();
                     54:     my %cat_order = ();
                     55:     my %idlist = ();
                     56:     my %idnums = ();
                     57:     my %idlist_titles = ();
                     58:     my $caller = 'global';
                     59:     my $format_reply;
                     60:     my $totcodes = 0;
                     61:     my $jscript = '';
                     62:     my $formname = 'coursecatalog';
1.5     ! raeburn    63:     my $domdesc = $Apache::lonnet::domaindescription{$codedom};
1.1       raeburn    64:     $totcodes = &Apache::lonsupportreq::retrieve_instcodes(\%coursecodes,$codedom,$totcodes);
                     65:     if ($totcodes > 0) {
                     66:         if ($ccode eq '') {
                     67:             $format_reply = &Apache::lonnet::auto_instcode_format($caller,$codedom,\%coursecodes,\%codes,\@codetitles,\%cat_titles,\%cat_order);
                     68:             if ($format_reply eq 'ok') {
                     69:                 my $numtypes = @codetitles;
                     70:                 &Apache::lonsupportreq::build_code_selections(\%codes,\@codetitles,\%cat_titles,\%cat_order,\%idlist,\%idnums,\%idlist_titles);
                     71:                 &Apache::lonsupportreq::javascript_code_selections($formname,$numtypes,\%cat_titles,\$jscript,\%idlist,\%idnums,\%idlist_titles,\@codetitles);
                     72:             }
                     73:         }
                     74:         if ($env{'form.state'} eq 'listing') {
                     75:             $jscript .= '
                     76: function setElements() {
                     77: ';
                     78:             for (my $i=0; $i<@codetitles; $i++) {
                     79:                 if ($env{'form.'.$codetitles[$i]} != -1) {
                     80:                     $jscript .= '
                     81:     for (var j=0; j<document.'.$formname.'.'.$codetitles[$i].'.length; j++) {
                     82:         if (document.'.$formname.'.'.$codetitles[$i].'[j].value == "'.$env{'form.'.$codetitles[$i]}.'") {
                     83:             document.'.$formname.'.'.$codetitles[$i].'.selectedIndex = j;
                     84:         }
                     85:     }
                     86: ';
                     87:                     $jscript .= '  courseSet('."'$codetitles[$i]'".');'."\n";
                     88:                 } else {
                     89:                     last;
                     90:                 }
                     91:             }
                     92:             $jscript .= '}';
                     93:             $jscript .= qq|
                     94: function changeSort(caller) {
                     95:     document.coursecatalog.sortby.value = caller;
                     96:     document.coursecatalog.submit();
                     97: }\n|;
                     98:         }
                     99:         my $js = '<script type"text/javascript">'."\n$jscript\n".
                    100:                  '</script>';
                    101:         my %add_entries = (topmargin    => "0",
                    102:                            marginheight => "0",
                    103:                            onLoad       =>"setElements()",);
                    104:         my $start_page =
                    105:             &Apache::loncommon::start_page('Course Catalog',$js,
                    106:                                            { 
                    107:                                              'add_entries' => \%add_entries,
                    108:                                              'no_inline_link'   => 1,});
                    109:         $r->print($start_page);
                    110: 
                    111:         my $numtitles = @codetitles;
1.2       raeburn   112:         $r->print('<h3>'.&mt('Display information about official [_1] classes for which LON-CAPA courses have been created:',$domdesc).'</h3>');
1.1       raeburn   113:         $r->print(&mt('<b>Choose which course(s) to list.</b><br />'));
                    114:         $r->print('<form name="coursecatalog" method="post">'); 
                    115:         if ($numtitles > 0) {
                    116:             my $lasttitle = $numtitles;
                    117:             if ($numtitles > 4) {
                    118:                 $lasttitle = 4;
                    119:             }
                    120:             $r->print('<table><tr><td>'.$codetitles[0].'<br />'."\n".
                    121:                   '<select name="'.$codetitles[0].'" onChange="courseSet('."'$codetitles[0]'".')">'."\n".
                    122:                   ' <option value="-1" />Select'."\n");
                    123:             my @items = ();
                    124:             my @longitems = ();
                    125:             if ($idlist{$codetitles[0]} =~ /","/) {
1.4       albertel  126:                 @items = split(/","/,$idlist{$codetitles[0]});
1.1       raeburn   127:             } else {
                    128:                 $items[0] = $idlist{$codetitles[0]};
                    129:             }
                    130:             if (defined($idlist_titles{$codetitles[0]})) {
                    131:                 if ($idlist_titles{$codetitles[0]} =~ /","/) {
1.4       albertel  132:                     @longitems = split(/","/,$idlist_titles{$codetitles[0]});
1.1       raeburn   133:                 } else {
                    134:                     $longitems[0] = $idlist_titles{$codetitles[0]};
                    135:                 }
                    136:                 for (my $i=0; $i<@longitems; $i++) {
                    137:                     if ($longitems[$i] eq '') {
                    138:                         $longitems[$i] = $items[$i];
                    139:                     }
                    140:                 }
                    141:             } else {
                    142:                 @longitems = @items;
                    143:             }
                    144:             for (my $i=0; $i<@items; $i++) {
                    145:                 $r->print(' <option value="'.$items[$i].'">'.$longitems[$i].'</option>');
                    146:             }
                    147:             $r->print('</select></td>');
                    148:             for (my $i=1; $i<$numtitles; $i++) {
                    149:                 $r->print('<td>'.$codetitles[$i].'<br />'."\n".
                    150:                  '<select name="'.$codetitles[$i].'" onChange="courseSet('."'$codetitles[$i]'".')">'."\n".
                    151:                  '<option value="-1">&lt;-Pick '.$codetitles[$i-1].'</option>'."\n".
                    152:                  '</select>'."\n".
                    153:                  '</td>'
                    154:                 );
                    155:             }
                    156:             $r->print('</tr></table>');
                    157:             if ($numtitles > 4) {
                    158:                 $r->print('<br /><br />'.$codetitles[$numtitles].'<br />'."\n".
                    159:                     '<select name="'.$codetitles[$numtitles].
                    160:                     '" onChange="courseSet('."'$codetitles[$numtitles]'".')">'."\n".
                    161:                     '<option value="-1">&lt;-Pick '.$codetitles[$numtitles-1].
                    162:                     '</option>'."\n".'</select>'."\n");
                    163:             }
                    164:         }
                    165:         $r->print('<br /><input type="hidden" name="state" value="listing" /><input type="hidden" name="sortby" value="" /><input type="submit" name="catalogfilter" value="'.&mt('Display courses').'" /></form>');
1.5     ! raeburn   166:     } else {
        !           167:         $r->print(&Apache::loncommon::start_page('Course Catalog','',
        !           168:                   {
        !           169:                    'no_inline_link'   => 1,}));
        !           170:         $r->print('<br />'.&mt('No official courses to display for [_1].',$domdesc));
1.1       raeburn   171:     }
                    172:     if ($env{'form.state'} eq 'listing') {
1.4       albertel  173:         $r->print('<br /><br />'.&print_course_listing($codedom));
1.1       raeburn   174:     }
                    175:     $r->print(&Apache::loncommon::end_page());
1.5     ! raeburn   176:     return OK;
1.1       raeburn   177: }
                    178: 
                    179: sub print_course_listing {
                    180:     my ($domain) = @_;
                    181:     my $output;
                    182:     my $year = $env{'form.Year'};
                    183:     my $sem = $env{'form.Semester'};
                    184:     my $dept = $env{'form.Department'};
                    185:     my $coursenum = $env{'form.Number'};
                    186:     my $instcode;
                    187:     if ($sem != -1) {
                    188:         $instcode .= $sem; 
                    189:     }
                    190:     if ($year != -1) {
                    191:         $instcode .= $year; 
                    192:     }
                    193:     if ($dept != -1) {
                    194:         $instcode .= $dept;
                    195:     }
                    196:     if ($coursenum != -1) {
                    197:         $instcode .= $coursenum; 
                    198:     }
                    199:     my %courses = &Apache::lonnet::courseiddump($domain,'.',1,$instcode,'.','.',
                    200:                                                 undef,undef,'Course');
                    201:     if (keys(%courses) == 0) {
1.2       raeburn   202:         $output = &mt('No courses match the criteria you selected.');
1.1       raeburn   203:         return $output;
                    204:     }
1.2       raeburn   205:     $output = &mt('<b>Note for students:</b> If you are officially enrolled in a course but there is no student role for the course in your LON-CAPA roles screen, check the default access dates and/or auto-enrollment settings for the course below.  Your roles screen displays only currently accessible roles.<br /><br />');
1.1       raeburn   206:     $output .= &Apache::loncommon::start_data_table().
                    207:               &Apache::loncommon::start_data_table_header_row().
                    208:               '<th><a href="javascript:changeSort('."'code'".')">'.&mt('Code').'</a></th>'.
                    209:               '<th>'.&mt('Sections').'</th>'.
1.2       raeburn   210:               '<th>'.&mt('Crosslisted').'</th>'.
1.1       raeburn   211:               '<th><a href="javascript:changeSort('."'title'".')">'.&mt('Title').'</a></th>'.
                    212:               '<th><a href="javascript:changeSort('."'owner'".')">'.&mt('Owner').'</a></th>'.
1.2       raeburn   213:               '<th>'.&mt('Student Status').'</th>'.
1.1       raeburn   214:               '<th>'.&mt('Default Access Dates').'</th>'.
1.2       raeburn   215:               '<th>'.&mt('Auto-enrollment').'</th>'.
1.1       raeburn   216:               &Apache::loncommon::end_data_table_header_row();
                    217:     my %courseinfo;
                    218:     foreach my $course (keys(%courses)) {
                    219:         my $descr;
                    220:         if ($courses{$course} =~ m/^([^:]*):/i) {
                    221:             $descr = &unescape($1);
                    222:         } else {
                    223:             $descr = &unescape($courses{$course});
                    224:         }
                    225:         my $cleandesc=&HTML::Entities::encode($descr,'<>&"');
                    226:         $cleandesc=~s/'/\\'/g;
                    227:         my ($cdom,$cnum)=split(/\_/,$course);
1.2       raeburn   228:        
1.4       albertel  229:         my ($desc,$instcode,$owner,$ttype) = split(/:/,$courses{$course});
1.1       raeburn   230:         $owner = &unescape($owner);
                    231:         my ($ownername,$ownerdom);
                    232:         if ($owner =~ /:/) {
                    233:             ($ownername,$ownerdom) = split(/:/,$owner);
                    234:         } else {
                    235:             $ownername = $owner;
                    236:             if ($owner ne '') {
                    237:                 $ownerdom = $cdom;
                    238:             }
                    239:         }
                    240:         my %ownernames;
                    241:         if ($ownername ne '' && $ownerdom ne '') {
                    242:             %ownernames = &Apache::loncommon::getnames($ownername,$ownerdom);
                    243:         }
                    244:         $courseinfo{$course}{'cdom'} = $cdom;
                    245:         $courseinfo{$course}{'cnum'} = $cnum;
                    246:         $courseinfo{$course}{'code'} = $instcode;
                    247:         $courseinfo{$course}{'ownerlastname'} = $ownernames{'lastname'};
                    248:         $courseinfo{$course}{'title'} = $cleandesc;
1.2       raeburn   249:         $courseinfo{$course}{'owner'} = $owner; 
1.1       raeburn   250:     }
                    251:     my %Sortby;
                    252:     foreach my $course (sort(keys(%courses))) {
                    253:         if ($env{'form.sortby'} eq 'code') {
                    254:             push(@{$Sortby{$courseinfo{$course}{'code'}}},$course);
                    255:         } elsif ($env{'form.sortby'} eq 'owner') {
                    256:             push(@{$Sortby{$courseinfo{$course}{'ownerlastname'}}},$course);
                    257:         } else {
                    258:             push(@{$Sortby{$courseinfo{$course}{'title'}}},$course);
                    259:         }
                    260:     }
                    261:     my @sorted_courses;
                    262:     if (($env{'form.sortby'} eq 'code') || ($env{'form.sortby'} eq 'owner')) {
                    263:         @sorted_courses = sort(keys(%Sortby));
                    264:     } else {
                    265:         @sorted_courses = sort { lc($a) cmp lc($b) } (keys(%Sortby));
                    266:     }
                    267:     foreach my $item (@sorted_courses) {
                    268:         foreach my $course (@{$Sortby{$item}}) {
                    269:             $output.=&Apache::loncommon::start_data_table_row(); 
                    270:             $output.=&courseinfo_row($courseinfo{$course});
                    271:             $output.=&Apache::loncommon::end_data_table_row();
                    272:         }
                    273:     }
                    274:     $output .= &Apache::loncommon::end_data_table();
                    275:     return $output;
                    276: }
                    277: 
                    278: sub courseinfo_row {
                    279:     my ($info) = @_;
1.2       raeburn   280:     my ($cdom,$cnum,$title,$ownerlast,$code,$owner,$output);
1.1       raeburn   281:     if (ref($info) eq 'HASH') {
                    282:         $cdom = $info->{'cdom'};
                    283:         $cnum = $info->{'cnum'};
                    284:         $title = $info->{'title'};
1.2       raeburn   285:         $ownerlast = $info->{'ownerlastname'};
                    286:         $code = $info->{'code'};
                    287:         $owner = $info->{'owner'};
1.1       raeburn   288:     } else {
1.2       raeburn   289:         $output = '<td colspan="8">'.&mt('No information available for [_1].',
                    290:                                          $code).'</td>';
1.1       raeburn   291:         return $output;
                    292:     }
                    293:     my %coursehash = &Apache::lonnet::dump('environment',$cdom,$cnum);
                    294:     my $classlist = &Apache::loncoursedata::get_classlist($cdom,$cnum);
                    295:     my %idx;
1.2       raeburn   296:     my @classids;
                    297:     my @crosslistings;
1.1       raeburn   298:     $idx{'status'} = &Apache::loncoursedata::CL_STATUS();
1.4       albertel  299:     my %status_title = &Apache::lonlocal::texthash(
1.1       raeburn   300:                            Expired => 'Previous access',
                    301:                            Active => 'Current access',
                    302:                            Future => 'Future access',
                    303:                        );
                    304:     my %student_count = (
                    305:                            Expired => 0,
                    306:                            Active => 0,
                    307:                            Future => 0,
                    308:                        );
1.4       albertel  309:     while (my ($student,$data) = each(%$classlist)) {
1.1       raeburn   310:         $student_count{$data->[$idx{'status'}]} ++;
                    311:     }
                    312:     my $seclist = &identify_sections($coursehash{'internal.sectionnums'});
1.2       raeburn   313:     my $xlist_items = &identify_sections($coursehash{'internal.crosslistings'});
1.1       raeburn   314:     my $countslist;
                    315:     my $startaccess = '';
                    316:     my $endaccess = '';
1.2       raeburn   317:     my $now;
                    318:     my ($accessdates,$autoenrolldates,$showsyllabus);
1.1       raeburn   319:     if ( defined($coursehash{'default_enrollment_start_date'}) ) {
                    320:         $startaccess = &Apache::lonlocal::locallocaltime($coursehash{'default_enrollment_start_date'});
                    321:     }
                    322:     if ( defined($coursehash{'default_enrollment_end_date'}) ) {
                    323:         $endaccess = &Apache::lonlocal::locallocaltime($coursehash{'default_enrollment_end_date'});
                    324:         if ($coursehash{'default_enrollment_end_date'} == 0) {
                    325:             $endaccess = "No ending date";
                    326:         }
                    327:     }
                    328:     if ($startaccess) {
                    329:         $accessdates .= &mt('From: ').$startaccess.'<br />';
                    330:     }
                    331:     if ($endaccess) {
                    332:         $accessdates .= &mt('To: ').$endaccess.'<br />';
                    333:     }
1.2       raeburn   334:     $autoenrolldates = &mt('Not enabled');
                    335:     if (defined($coursehash{'internal.autoadds'}) && $coursehash{'internal.autoadds'} == 1) {
1.1       raeburn   336:         my ($autostart,$autoend);
                    337:         if ( defined($coursehash{'internal.autostart'}) ) {
                    338:             $autostart = &Apache::lonlocal::locallocaltime($coursehash{'internal.autostart'});
                    339:         }
                    340:         if ( defined($coursehash{'internal.autoend'}) ) {
                    341:             $autoend = &Apache::lonlocal::locallocaltime($coursehash{'internal.autoend'});
1.2       raeburn   342:         }
                    343:         if ($coursehash{'internal.autostart'} > $now) {
                    344:             if ($coursehash{'internal.autoend'} && $coursehash{'internal.autoend'} < $now) {
                    345:                 $autoenrolldates = &mt('Not enabled');
                    346:             } else {
                    347:                 my $valid_classes = &get_valid_classes($seclist,$xlist_items,
                    348:                                                        $code,$owner,$cdom,$cnum);
                    349:                 if ($valid_classes ne '') {
                    350:                     $autoenrolldates = &mt('Not enabled<br />Starts: ').
                    351:                                        $autostart.'<br />'.$valid_classes;
                    352:                 }
                    353:             }
                    354:         } else {
                    355:             if ($coursehash{'internal.autoend'} && $coursehash{'internal.autoend'} < $now) {
                    356:                 $autoenrolldates = &mt('Not enabled<br />Ended: ').$autoend;
                    357:             } else {
                    358:                 my $valid_classes = &get_valid_classes($seclist,$xlist_items,
                    359:                                                        $code,$owner,$cdom,$cnum);
                    360:                 if ($valid_classes ne '') {
                    361:                     $autoenrolldates = &mt('Currently enabled<br />').
                    362:                                        $valid_classes;
                    363:                 }
1.1       raeburn   364:             }
                    365:         }
                    366:     }
1.2       raeburn   367:     if (defined($coursehash{'showsyllabus'})) {
                    368:         $showsyllabus = $coursehash{'showsyllabus'};
                    369:     } 
1.1       raeburn   370:     foreach my $status ('Active','Future','Expired') {
                    371:         $countslist .= '<nobr>'.$status_title{$status}.': '.
                    372:                        $student_count{$status}.'</nobr><br />';
                    373:     }
1.2       raeburn   374:     if ($xlist_items eq '') {
                    375:         $xlist_items = &mt('No');
                    376:     }
1.1       raeburn   377:     $output = '<td>'.$coursehash{'internal.coursecode'}.'</td>'.
                    378:               '<td>'.$seclist.'</td>'.
1.2       raeburn   379:               '<td>'.$xlist_items.'</td>'.
                    380:               '<td>'.$title.'&nbsp;<font size="-2">';
                    381:     if ($showsyllabus) {
                    382:         $output .= &Apache::loncommon::syllabuswrapper(&mt('Syllabus'),$cnum,$cdom);
                    383:     }
                    384:     $output .= '</font></td>'.
                    385:                '<td>'.$ownerlast.'</td>'.
                    386:                '<td>'.$countslist.'</td>'.
                    387:                '<td>'.$accessdates.'</td>'.
                    388:                '<td>'.$autoenrolldates.'</td>'; 
1.1       raeburn   389:     return $output;
                    390: }
                    391: 
                    392: sub identify_sections {
                    393:     my ($seclist) = @_;
                    394:     my @secnums;
                    395:     if ($seclist =~ /,/) {
1.4       albertel  396:         my @sections = split(/,/,$seclist);
1.1       raeburn   397:         foreach my $sec (@sections) {
                    398:             $sec =~ s/:[^:]*$//;
                    399:             push(@secnums,$sec);
                    400:         }
                    401:     } else {
                    402:         if ($seclist =~ m/^([^:]+):/) {
                    403:             my $sec = $1;
1.4       albertel  404:             if (!grep(/^\Q$sec\E$/,@secnums)) {
                    405:                 push(@secnums,$sec);
1.1       raeburn   406:             }
                    407:         }
                    408:     }
                    409:     @secnums = sort {$a <=> $b} @secnums;
                    410:     my $seclist = join(', ',@secnums);
                    411:     return $seclist;
                    412: }
                    413: 
1.2       raeburn   414: sub get_valid_classes {
                    415:     my ($seclist,$xlist_items,$crscode,$owner,$cdom,$cnum) = @_;
                    416:     my $response;
                    417:     my %validations;
                    418:     @{$validations{'sections'}} = ();
                    419:     @{$validations{'xlists'}} = ();
                    420:     my $totalitems = 0;
                    421:     if ($seclist) {
                    422:         foreach my $sec (split(',',$seclist)) {
                    423:             my $class = $crscode.$sec;
1.3       albertel  424:             if (&Apache::lonnet::auto_validate_class_sec($cdom,$cnum,$owner,
                    425: 							 $class) eq 'ok') {
1.2       raeburn   426:                 if (!grep(/^\Q$sec$\E/,@{$validations{'sections'}})) {
1.4       albertel  427:                     push(@{$validations{'sections'}},$sec);
1.2       raeburn   428:                     $totalitems ++;
                    429:                 }
                    430:             }
                    431:         }
                    432:     }
                    433:     if ($xlist_items) {
                    434:         foreach my $item (split(',',$xlist_items)) {
1.3       albertel  435:             if (&Apache::lonnet::auto_validate_class_sec($cdom,$cnum,$owner,
                    436: 							 $item) eq 'ok') {
1.2       raeburn   437:                 if (!grep(/^\Q$item$\E/,@{$validations{'xlists'}})) {
1.4       albertel  438:                     push(@{$validations{'xlists'}},$item);
1.2       raeburn   439:                     $totalitems ++;
                    440:                 }
                    441:             }
                    442:         }
                    443:     }
                    444:     if ($totalitems > 0) {
                    445:         if (@{$validations{'sections'}}) {
                    446:             $response = &mt('Sections: ').
                    447:                         join(',',@{$validations{'sections'}}).'<br />';
                    448:         }
                    449:         if (@{$validations{'xlists'}}) {
                    450:             $response .= &mt('Courses: ').
                    451:                         join(',',@{$validations{'xlists'}});
                    452:         }
                    453:     }
                    454:     return $response;
                    455: }
                    456: 
1.1       raeburn   457: 
                    458: 1;
                    459: 

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