Annotation of loncom/interface/loncourserespicker.pm, revision 1.18

1.1       raeburn     1: # The LearningOnline Network
                      2: #
1.18    ! raeburn     3: # $Id: loncourserespicker.pm,v 1.17 2024/11/22 22:42:27 raeburn Exp $
1.1       raeburn     4: #
                      5: # Copyright Michigan State University Board of Trustees
                      6: #
                      7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      8: #
                      9: # LON-CAPA is free software; you can redistribute it and/or modify
                     10: # it under the terms of the GNU General Public License as published by
                     11: # the Free Software Foundation; either version 2 of the License, or
                     12: # (at your option) any later version.
                     13: #
                     14: # LON-CAPA is distributed in the hope that it will be useful,
                     15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     17: # GNU General Public License for more details.
                     18: #
                     19: # You should have received a copy of the GNU General Public License
                     20: # along with LON-CAPA; if not, write to the Free Software
                     21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     22: #
                     23: # /home/httpd/html/adm/gpl.txt
                     24: #
                     25: # http://www.lon-capa.org/
                     26: #
                     27: 
                     28: =pod
                     29: 
                     30: =head1 NAME
                     31: 
                     32: loncourserespicker - Utilities to choose folders and resources in a course.
                     33: 
                     34: =head1 SYNOPSIS
                     35: 
                     36: loncourserespicker provides an interface for selecting which folders and/or
                     37: resources are to be either:
                     38: 
                     39: (a) exported to an IMS Content Package
1.17      raeburn    40: (b) subject to access blocking for the duration of an exam/quiz.
1.7       raeburn    41: (c) dumped to an Authoring Space
1.15      raeburn    42: (d) receive shortened URLs to be used when deep-linking into a course
1.1       raeburn    43: 
                     44: =head1 DESCRIPTION
                     45: 
                     46: This module provides routines to generate a hierarchical display of folders
                     47: and resources in a course which can be selected for specific actions.
                     48: 
                     49: The choice of items is copied back to the main window from which the pop-up
                     50: window used to display the Course Contents was opened.
                     51: 
                     52: =head1 OVERVIEW
                     53: 
                     54: The main subroutine: &create_picker() will display the hierarchy of folders,
1.6       raeburn    55: sub-folders, and resources in the Main Content area.  Items can be selected
                     56: using checkboxes, and/or a "Check All" button.  Selection of a folder
1.1       raeburn    57: causes the contents of the folder to also be selected automatically. The
                     58: propagation of check status is recursive into sub-folders.  Likewise, if an
                     59: item deep in a nested set of folders and sub-folders is unchecked, the 
                     60: uncheck will propagate up through the hierarchy causing any folders at
                     61: a higher level to become unchecked.
                     62: 
                     63: There is a submit button, which will be named differently according to the 
1.7       raeburn    64: context in which resource/folder selection is being made.
1.1       raeburn    65: 
1.17      raeburn    66: The four contexts currently supported are: IMS export, selection of
1.1       raeburn    67: content to be subject to access restructions for the duration of an
1.17      raeburn    68: exam, selection of items for dumping to an Authoring Space, and 
                     69: display or creation of shortened URLs for deep-linking,
1.1       raeburn    70: 
                     71: =head1 INTERNAL SUBROUTINES
                     72: 
                     73: =item &create_picker()
                     74: 
                     75: Created HTML mark up to display contents of course with checkboxes to
                     76: select items.  Checking a folder causes recursive checking of items
                     77: within the folder. Unchecking a resource causing unchecking of folders
                     78: containing the item back up to the top level.
                     79: 
1.14      raeburn    80: Inputs: 11.
1.1       raeburn    81:    - $navmap  -- Reference to LON-CAPA navmap object 
                     82:                 (encapsulates information about resources in the course). 
                     83: 
                     84:    - $context -- Context in which course resource selection is being made.
1.15      raeburn    85:                  Currently imsexport, examblock, dumpdocs, and shorturls
                     86:                  are supported.
1.1       raeburn    87: 
                     88:    - $formname  -- Name of the form in the window from which the pop-up
                     89:                    used to select course items was launched. 
                     90: 
                     91:    - $crstype  -- Course or Community
                     92: 
                     93:    - $blockedmaps -- Reference to hash of previously selected maps
                     94:                      (e.g., for a live exam block).   
                     95: 
                     96:    - $blockedresources  -- Reference to hash of resources selected
                     97:                            previously (e.g., for an exam block).  
                     98: 
                     99:    - $block  -- An internal ID (integer) used to track which exam
                    100:                 block currently being configured.
                    101: 
1.7       raeburn   102:    - $preamble -- HTML form elements used to select Authoring Space
                    103:                   if more than one available, and also set name of 'Folder 
                    104:                   in Authoring Space' where content will be dumped, when
                    105:                   context is 'dumpdocs'.
                    106: 
                    107:    - $numhome -- number of possible Authoring Spaces where content could
                    108:                  be dumped when context is 'dumpdocs'.
                    109: 
                    110:    - $uploadedfiles -- Reference to hash: keys are paths to files in
                    111:                        /home/httpd/lonUsers/$cdom/$1/$2/$3/$cnum/userfiles.
1.15      raeburn   112: 
                    113:    - $tiny -- Reference to hash: keys are symbs of course items for which
                    114:               shortened URLs have already been created.
                    115: 
1.14      raeburn   116:    - $readonly -- if true, no "check all" or "uncheck all" buttons will
                    117:                   be displayed, and checkboxes will be disabled, if this 
1.15      raeburn   118:                   is for an exam block or for shortened URL creation.
1.7       raeburn   119: 
1.1       raeburn   120: 
                    121: Output: $output is the HTML mark-up for display/selection of content
                    122:         items in the pop-up window.
                    123: 
                    124: =item &respicker_javascript()
                    125: 
                    126: Creates javascript functions for checking/unchecking all items, and
                    127: for recursive checking triggered by checking a folder, or recursive
                    128: (upeards) unchecking of an item within a folder. 
                    129: 
                    130: Inputs: 7. 
                    131:    - $startcount -- Starting offset of form element numbering for items  
                    132: 
                    133:    - $numcount -- Total numer of folders and resources in course.
                    134: 
                    135:    - $context -- Context in which resources are being displayed
1.17      raeburn   136:                  (imsexport, examblock,  dumpdocs or shorturls). 
1.1       raeburn   137: 
                    138:    - $formname --  Name of form.
                    139: 
                    140:    - $children -- Reference to hash of items contained within a folder. 
                    141: 
                    142:    - $hierarchy -- Reference to hierarchy of folders containing an item.
                    143: 
                    144:    - $checked_maps -- Reference to array of folders currently checked.
                    145: 
1.7       raeburn   146: Output: 1. Javascript (within <script></script> tags.
1.1       raeburn   147: 
                    148: 
                    149: =item &get_navmap_object() 
                    150: 
                    151: Instantiates a navmaps object, and generates an error message if
                    152: no object instantiated.
                    153: 
                    154: Inputs: 2.
                    155:    - $crstype -- Container type: Course or Community
                    156: 
1.17      raeburn   157:    - $context -- Context: imsexport, examblock, dumpdocs, or shorturls
1.7       raeburn   158: 
                    159: 
                    160: =item &clean()
                    161:  
                    162: Takes incoming title and replaces non-alphanumeric characters with underscore,
                    163: so title can be used as suggested file name (with appended extension) for file
1.11      bisitz    164: copied from course to Authoring Space.
1.7       raeburn   165: 
                    166: 
                    167: =item &enumerate_course_contents()
                    168: 
                    169: Create hashes of maps (for folders/pages) and symbs (for resources) in
                    170: a course, where keys are numbers (starting with 1) and values are
                    171: map url, or symb, for an iteration through the course, as seen by
                    172: a Course Coordinator. Used to generate numerical IDs to facilitate
                    173: (a) storage of lists of maps or resources to be blocked during an exam,
1.18    ! raeburn   174: (b) processing selected form elements during dumping of selected course
1.11      bisitz    175:     content to Authoring Space.
1.18    ! raeburn   176: (c) processing of checked checkboxes for creation of shortened URLs for
        !           177:     deep-linking to course content.
1.7       raeburn   178: 
                    179: Inputs: 7 
                    180: 
                    181:       $navmap - navmaps object
                    182: 
                    183:       $map_url - reference to hash to contain URLs of maps in course
                    184: 
                    185:       $resource_symb - reference to hash to contain symbs for
                    186:                        resources in course
                    187: 
                    188:       $title_ref - reference to hash containing titles for items in
                    189:                    course
                    190: 
1.17      raeburn   191:       $context - examblock, dumpdocs or shorturls
1.7       raeburn   192: 
                    193:       $cdom - course's domain
                    194: 
1.18    ! raeburn   195:       $cnum - courseID
1.7       raeburn   196: 
                    197: Outputs: None
                    198: 
                    199: Side Effects: $map_url and $resource_symb hashrefs are populated.
                    200: 
1.1       raeburn   201: 
                    202: =over
                    203: 
                    204: =back
                    205: 
                    206: =cut
                    207: 
                    208: 
                    209: package Apache::loncourserespicker;
                    210: 
                    211: use strict;
                    212: use Apache::lonnet;
                    213: use Apache::loncommon;
                    214: use Apache::lonhtmlcommon;
                    215: use Apache::lonnavmaps;
1.2       raeburn   216: use Apache::londocs;
1.1       raeburn   217: use Apache::lonlocal;
                    218: use LONCAPA qw(:DEFAULT :match);
                    219: 
                    220: sub create_picker {
1.14      raeburn   221:     my ($navmap,$context,$formname,$crstype,$blockedmaps,$blockedresources,$block,$preamble,
1.15      raeburn   222:         $numhome,$uploadedfiles,$tiny,$readonly) = @_;
1.1       raeburn   223:     return unless (ref($navmap));
1.16      raeburn   224:     my ($it,$output,$numdisc,%discussiontime,%currmaps,%currresources,%files,
1.15      raeburn   225:         %shorturls,$chkname);
                    226:     $chkname = 'archive';
                    227:     if ($context eq 'shorturls') {
                    228:         $chkname = 'addtiny';
                    229:     }
1.1       raeburn   230:     $it = $navmap->getIterator(undef,undef,undef,1,undef,undef);
                    231:     if (ref($blockedmaps) eq 'HASH') {
                    232:         %currmaps = %{$blockedmaps};
                    233:     }
                    234:     if (ref($blockedresources) eq 'HASH') {
                    235:         %currresources = %{$blockedresources};
1.7       raeburn   236:     } elsif (ref($uploadedfiles) eq 'HASH') {
                    237:         %files = %{$uploadedfiles};
1.15      raeburn   238:     } elsif (ref($tiny) eq 'HASH') {
                    239:         %shorturls = %{$tiny}; 
1.1       raeburn   240:     }
                    241:     my @checked_maps;
                    242:     my $curRes;
                    243:     my $numprobs = 0;
                    244:     my $depth = 0;
                    245:     my $count = 0;
                    246:     my $boards = 0;
                    247:     my $startcount = 1;
                    248:     my %parent = ();
                    249:     my %children = ();
                    250:     my %hierarchy = ();
                    251:     my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons");
1.7       raeburn   252:     my $whitespace =
1.1       raeburn   253:         '<img src="'.$location.'/whitespace_21.gif" class="LC_docs_spacer" alt="" />';
                    254: 
1.7       raeburn   255:     my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                    256:     my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                    257:     my $crsprefix = &propath($cdom,$cnum).'/userfiles/';
                    258: 
1.14      raeburn   259:     my ($info,$display,$onsubmit,$togglebuttons,$disabled);
1.1       raeburn   260:     if ($context eq 'examblock') {
                    261:         my $maps_elem = 'docs_maps_'.$block;
                    262:         my $res_elem = 'docs_resources_'.$block;
                    263:         $onsubmit = ' onsubmit="return writeToOpener('."'$maps_elem','$res_elem'".');"';
1.7       raeburn   264:         $info = &mt('Items in '.lc($crstype).' for which access will be blocked.');
1.14      raeburn   265:         if ($readonly) {
                    266:             $disabled = ' disabled="disabled"';
                    267:         }
1.1       raeburn   268:     }
1.7       raeburn   269:     if ($context eq 'dumpdocs') {
                    270:         $info = '<span class="LC_fontsize_medium">'.
1.11      bisitz    271:                 &mt('Choose the uploaded course items and templated pages/problems to be copied to Authoring Space.').
1.7       raeburn   272:                 '</span><br /><br />';
                    273:         $startcount = 3 + $numhome;
1.15      raeburn   274:         $onsubmit = ' onsubmit="return checkUnique(document.'.$formname.',document.'.$formname.'.'.$chkname.');"';
                    275:     } elsif ($context eq 'shorturls') {
                    276:         $info = '<span class="LC_fontsize_medium">'.
                    277:                 &mt('Choose the resource(s) and/or folder(s) from Main Content for which shortened URL(s) are needed.').
                    278:                 '</span><br /><br />';
1.7       raeburn   279:     } elsif ($context eq 'imsexport') {
                    280:         $info = &mt('Choose which items you wish to export from your '.$crstype.'.');
1.1       raeburn   281:         $startcount = 5;
                    282:     }
1.14      raeburn   283:     if ($disabled) {
                    284:         $togglebuttons = '<br />';
                    285:     } else {
                    286:         $togglebuttons = '<input type="button" value="'.&mt('check all').'" '.
1.15      raeburn   287:                          'onclick="javascript:checkAll(document.'.$formname.'.'.$chkname.')" />'.
1.14      raeburn   288:                          '&nbsp;&nbsp;<input type="button" value="'.&mt('uncheck all').'"'.
1.15      raeburn   289:                          ' onclick="javascript:uncheckAll(document.'.$formname.'.'.$chkname.')" />';
1.14      raeburn   290:     }
1.7       raeburn   291:     $display = '<form name="'.$formname.'" action="" method="post"'.$onsubmit.'>'."\n";
1.1       raeburn   292:     if ($context eq 'imsexport') {
1.7       raeburn   293:         $display .= $info.
                    294:                     '<div class="LC_columnSection">'."\n".
1.1       raeburn   295:                     '<fieldset>'.
1.7       raeburn   296:                     '<legend>'.&mt('Content items').'</legend>'."\n".
                    297:                     $togglebuttons.
                    298:                     '</fieldset>';
                    299:         %discussiontime = &Apache::lonnet::dump('discussiontimes',$cdom,$cnum);
1.1       raeburn   300:         $numdisc = keys(%discussiontime);
                    301:         if ($numdisc > 0) {
                    302:             $display .= 
                    303:                 '<fieldset>'.
                    304:                 '<legend>'.&mt('Discussion posts').'</legend>'.
                    305:                 '<input type="button" value="'.&mt('check all').'"'.
                    306:                 ' onclick="javascript:checkAll(document.'.$formname.'.discussion)" />'.
                    307:                 '&nbsp;&nbsp;<input type="button" value="'.&mt('uncheck all').'"'.
                    308:                 ' onclick="javascript:uncheckAll(document.'.$formname.'.discussion)" />'.
1.2       raeburn   309:                 '</fieldset>';
1.1       raeburn   310:         }
1.7       raeburn   311:         $display .= '</div>';
1.15      raeburn   312:     } elsif (($context eq 'examblock') || ($context eq 'shorturls')) {
1.7       raeburn   313:         $display .= $info.$togglebuttons;
                    314:     } elsif ($context eq 'dumpdocs') {
                    315:         $display .= $preamble.
                    316:                     '<div class="LC_left_float">'.
                    317:                     '<fieldset>'.
                    318:                     '<legend>'.&mt('Content to copy').('&nbsp;'x4).$togglebuttons.'</legend>'.
                    319:                     $info;
1.1       raeburn   320:     }
                    321:     my $lastcontainer = $startcount;
                    322:     $display .= &Apache::loncommon::start_data_table()
                    323:                .&Apache::loncommon::start_data_table_header_row();
                    324:     if ($context eq 'imsexport') {
                    325:         $display .= '<th>'.&mt('Export content item?').'</th>';
                    326:         if ($numdisc > 0) {
                    327:             $display .= '<th>'.&mt('Export discussion posts?').'</th>';
                    328:         }
                    329:     } elsif ($context eq 'examblock') {
                    330:         $display .= '<th>'.&mt('Access blocked?').'</th>';
1.7       raeburn   331:     } elsif ($context eq 'dumpdocs') {
                    332:         $display .= '<th>'.&mt('Copy?').'</th>'.
1.15      raeburn   333:                     '<th>'.&mt("Title in $crstype").'</th>'.
1.7       raeburn   334:                     '<th>'.&mt('Internal Identifier').'</th>'.
                    335:                     '<th>'.&mt('Save as ...').'</th>';
1.15      raeburn   336:     } elsif ($context eq 'shorturls') {
                    337:         $display .= '<th colspan="2">'.&mt('Tiny URL').'</th>'.
                    338:                     '<th>'.&mt("Title in $crstype").'</th>';
1.1       raeburn   339:     }
                    340:     $display .= &Apache::loncommon::end_data_table_header_row();
                    341:     while ($curRes = $it->next()) {
                    342:         if ($curRes == $it->BEGIN_MAP()) {
                    343:             $depth++;
                    344:             $parent{$depth} = $lastcontainer;
                    345:         }
                    346:         if ($curRes == $it->END_MAP()) {
                    347:             $depth--;
                    348:             $lastcontainer = $parent{$depth};
                    349:         }
                    350:         if (ref($curRes)) {
                    351:             my $symb = $curRes->symb();
                    352:             my $ressymb = $symb;
1.7       raeburn   353:             if ($context eq 'dumpdocs') {
                    354:                 next unless (($curRes->src() =~ m{^\Q/uploaded/$cdom/$cnum/\E(docs|supplemental|simplepage)}) ||
                    355:                              ($curRes->src() =~ m{^\Q/uploaded/$cdom/$cnum/\E(default|supplemental)_\d+\.(sequence|page)}) ||
                    356:                              ($curRes->src() eq '/res/lib/templates/simpleproblem.problem') ||
                    357:                              ($curRes->src() =~ m{^/adm/$match_domain/$match_username/\d+/smppg}));
                    358:             } elsif ($ressymb =~ m|adm/($match_domain)/($match_username)/(\d+)/bulletinboard$|) {
1.1       raeburn   359:                 unless ($ressymb =~ m|adm/wrapper/adm|) {
                    360:                     $ressymb = 'bulletin___'.$3.'___adm/wrapper/adm/'.$1.'/'.$2.'/'.$3.'/bulletinboard';
                    361:                 }
                    362:             }
1.7       raeburn   363:             $count ++;
1.15      raeburn   364:             my ($currelem,$mapurl,$is_map);
1.7       raeburn   365:             if ($context eq 'imsexport') {
                    366:                 $currelem = $count+$boards+$startcount;
                    367:             } else {
                    368:                 $currelem = $count+$startcount;
                    369:             }
1.15      raeburn   370:             $display .= &Apache::loncommon::start_data_table_row()."\n";
1.1       raeburn   371:             if (($curRes->is_sequence()) || ($curRes->is_page())) {
                    372:                 $lastcontainer = $currelem;
1.15      raeburn   373:                 $mapurl = (&Apache::lonnet::decode_symb($symb))[2];
                    374:                 $is_map = 1;
                    375:             }
                    376:             if ($context eq 'shorturls') {
                    377:                 if ($shorturls{$symb}) {
                    378:                     $display .= '<td>&nbsp;</td><td align="right"><b>'."/tiny/$cdom/$shorturls{$symb}".'</b></td>'."\n";
                    379:                 } else {
                    380:                     $display .= '<td align="left"><label><input type="checkbox" name="'.$chkname.'" '.
                    381:                                 'value="'.$count.'"'.$disabled.' />'.&mt('Add').'</label></td>'.
                    382:                                 '<td>&nbsp;</td>'."\n";
1.1       raeburn   383:                 }
                    384:             } else {
1.15      raeburn   385:                 $display .= '<td><input type="checkbox" name="'.$chkname.'" value="'.$count.'" ';
                    386:                 if ($is_map) {
                    387:                     $display .= 'onclick="javascript:checkFolder(document.'.$formname.','."'$currelem'".')" ';
                    388:                     if ($currmaps{$mapurl}) {
                    389:                         $display .= 'checked="checked"';
                    390:                         push(@checked_maps,$currelem);
                    391:                     }
                    392:                 } else {
                    393:                     if ($curRes->is_problem()) {
                    394:                        $numprobs ++;
                    395:                     }
                    396:                     $display .= 'onclick="javascript:checkResource(document.'.$formname.','."'$currelem'".')" ';
                    397:                     if ($currresources{$symb}) {
                    398:                         $display .= 'checked="checked"';
                    399:                     }
1.1       raeburn   400:                 }
1.15      raeburn   401:                 $display .= $disabled.' />'."\n";
1.1       raeburn   402:             }
1.7       raeburn   403:             if ($context eq 'dumpdocs') {
                    404:                 $display .= '</td><td valign="top">';
1.15      raeburn   405:             } elsif ($context eq 'shorturls') {
                    406:                 $display .= '<td valign="top">';
1.7       raeburn   407:             }
1.1       raeburn   408:             for (my $i=0; $i<$depth; $i++) {
                    409:                 $display .= "$whitespace\n";
                    410:             }
                    411:             my $icon = 'src="'.$location.'/unknown.gif" alt=""';
                    412:             if ($curRes->is_sequence()) {
1.4       bisitz    413:                 $icon = 'src="'.$location.'/navmap.folder.open.gif" alt="'.&mt('Folder').'"';
1.1       raeburn   414:             } elsif ($curRes->is_page()) {
                    415:                 $icon = 'src="'.$location.'/navmap.page.open.gif" alt="'.&mt('Composite Page').'"';
                    416:             } elsif ($curRes->is_problem()) {
                    417:                 $icon = 'src="'.$location.'/problem.gif" alt="'.&mt('Problem').'"';
                    418:             } elsif ($curRes->is_task()) {
                    419:                 $icon = 'src="'.$location.'/task.gif" alt="'.&mt('Task').'"';
                    420:             } elsif ($curRes->src ne '') {
                    421:                 $icon = 'src="'.&Apache::loncommon::icon($curRes->src).'" alt=""';
                    422:             }
                    423:             $display .= '<img '.$icon.' />&nbsp;'."\n";
                    424:             $children{$parent{$depth}} .= $currelem.':';
                    425:             if ($context eq 'examblock') {
                    426:                 if ($parent{$depth} > 1) {
                    427:                     if ($hierarchy{$parent{$depth}}) {
                    428:                         $hierarchy{$currelem} = $hierarchy{$parent{$depth}}.",'$parent{$depth}'";
                    429:                     } else {
                    430:                         $hierarchy{$currelem} = "'$parent{$depth}'";
                    431:                     }
                    432:                 }
                    433:             }
1.7       raeburn   434:             $display .= '&nbsp;'.$curRes->title().$whitespace.'</td>'."\n";
1.1       raeburn   435: 
                    436:             if ($context eq 'imsexport') {
                    437: # Existing discussion posts?
                    438:                 if ($discussiontime{$ressymb} > 0) {
                    439:                     $boards ++;
                    440:                     $display .= '<td align="right">'
                    441:                                .'<input type="checkbox" name="discussion" value="'.$count.'" />'
                    442:                                .'</td>'."\n";
                    443:                 } elsif ($numdisc > 0) {
                    444:                     $display .= '<td>&nbsp;</td>'."\n";
                    445:                 }
1.7       raeburn   446:             } elsif ($context eq 'dumpdocs') {
                    447:                 my $src = $curRes->src();
                    448:                 my ($filepath,$title);
                    449:                 if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E}) {
                    450:                     $filepath = &Apache::lonnet::filelocation('',$src);
                    451:                     $filepath =~ s/\Q$crsprefix\E//;
                    452:                     if ($curRes->is_map()) {
                    453:                         $title = $files{$filepath};
                    454:                     } else {
                    455:                         $filepath =~ s{docs/}{}; 
                    456:                         $title = $filepath;
                    457:                         $title =~ s{^(default|\d+)/\d*/?}{};
                    458:                     }
                    459:                 } else {
                    460:                     $title = $curRes->title();
                    461:                     $title =~ s{/}{_}g;
                    462:                     $title = &clean($title);
                    463:                     if ($src eq '/res/lib/templates/simpleproblem.problem') {
                    464:                         my ($map,$id,$res) = &Apache::lonnet::decode_symb($symb);
                    465:                         $map =~ s{^uploaded/$cdom/$cnum/}{};
                    466:                         $filepath = $map.'_'.$id;
                    467:                         $title .= '.problem';
                    468:                     } elsif ($src =~ m{^/adm/$match_domain/$match_username/(\d+)/smppg}) {
                    469:                         $filepath = 'smppage_'.$1.'.db';
                    470:                         $title .= '.html';
                    471:                     }
                    472:                 }
                    473:                 $display .= '<td>'.$filepath.'</td>'.
                    474:                             '<td><input type="text" size="40" name="namefor_'.$count.'" id="namefor_'.$count.'" value="'.$title.'" /></td>'."\n";
1.1       raeburn   475:             }
                    476:             $display .= &Apache::loncommon::end_data_table_row();
                    477:         }
                    478:     }
                    479:     $display .= &Apache::loncommon::end_data_table();
                    480:     if ($context eq 'imsexport') {
                    481:         if ($numprobs > 0) {
                    482:             $display .= '<p><span class="LC_nobreak">'.
                    483:                         &mt('Export format for LON-CAPA problems:').
                    484:                         '<label><input type="radio" name="format" value="xml" checked="checked" />'.
                    485:                         '&nbsp;'.&mt('XML').'</label>'.('&nbsp;' x3).
                    486:                         '<label><input type="radio" name="format" value="html" />'.
                    487:                         '&nbsp;'.&mt('HTML').'</label>'.('&nbsp;' x3).
                    488:                         '<label><input type="radio" name="format" value="plaintext" />'.
                    489:                         '&nbsp;'.&mt('Text').'</label></span></p>';
                    490:         }
                    491:     }
1.7       raeburn   492:     my $numcount;
1.1       raeburn   493:     if ($context eq 'imsexport') {
                    494:         $display .= 
1.7       raeburn   495:            '<p>'.
1.1       raeburn   496:            '<input type="hidden" name="finishexport" value="1" />'.
                    497:            '<input type="submit" name="exportcourse" value="'.
1.7       raeburn   498:            &mt('Export').'" /></p>';
                    499:         $numcount = $count + $boards + $startcount;
1.1       raeburn   500:     } elsif ($context eq 'examblock') {
1.14      raeburn   501:         unless ($readonly) {
                    502:             $display .=
                    503:                 '<p>'.
                    504:                 '<input type="submit" name="resourceblocks" value="'.
                    505:                 &mt('Copy Choices to Main Window').'" /></p>';
                    506:         }
1.7       raeburn   507:         $numcount = $count + $startcount;
                    508:     } elsif ($context eq 'dumpdocs') {
                    509:         $display .= '</fieldset>'.
                    510:                     '</div><div style="padding:0;clear:both;margin:0;border:0"></div>'.
                    511:                     '<div>'.
1.8       raeburn   512:                     '<input type="submit" name="dumpcourse" value="'.&mt("Copy $crstype Content").'" />'.
1.7       raeburn   513:                     '</div>';
                    514:         $numcount = $count + $startcount;
1.15      raeburn   515:     } elsif ($context eq 'shorturls') {
                    516:         unless ($readonly) {
                    517:             $display .=
                    518:                 '<p>'.
                    519:                 '<input type="submit" name="shorturls" value="'.
                    520:                 &mt('Create Tiny URL(s)').'" /></p>';
                    521:         }
1.1       raeburn   522:     }
1.7       raeburn   523:     $display .= '</form>';
1.17      raeburn   524:     my $scripttag =
1.1       raeburn   525:         &respicker_javascript($startcount,$numcount,$context,$formname,\%children,
1.15      raeburn   526:                               \%hierarchy,\@checked_maps,$numhome,$chkname);
                    527:     if (($context eq 'dumpdocs') || ($context eq 'shorturls')) {
1.7       raeburn   528:         return $scripttag.$display; 
                    529:     }
1.1       raeburn   530:     my ($title,$crumbs,$args);
1.7       raeburn   531:     if ($context eq 'imsexport') { 
1.1       raeburn   532:         $title = 'Export '.$crstype.' to IMS Package';
                    533:     } elsif ($context eq 'examblock') {
                    534:         $title = 'Resources with Access blocked';
                    535:         $args = {'only_body'      => 1,
                    536:                  'add_entries' => { onload => 'javascript:recurseFolders();' }, 
                    537:                 };
                    538:     }
1.3       raeburn   539:     $output = &Apache::loncommon::start_page($title,$scripttag,$args);
1.1       raeburn   540:     if ($context eq 'imsexport') {
1.2       raeburn   541:         $output .= &Apache::lonhtmlcommon::breadcrumbs('IMS Export').
                    542:                    &Apache::londocs::startContentScreen('tools');
1.7       raeburn   543:     } elsif ($context eq 'dumpdocs') {
1.15      raeburn   544:         $output .= &Apache::lonhtmlcommon::breadcrumbs('Copying to Authoring Space').
                    545:                    &Apache::londocs::startContentScreen('tools');
1.1       raeburn   546:     }
                    547:     $output .= $display;
                    548:     if ($context eq 'examblock') {
                    549:         $output .= &Apache::loncommon::end_page();
1.2       raeburn   550:     } elsif ($context eq 'imsexport') {
                    551:         $output .= &Apache::londocs::endContentScreen();
1.1       raeburn   552:     }
                    553:     return $output;
                    554: }
                    555: 
                    556: sub respicker_javascript {
                    557:     my ($startcount,$numitems,$context,$formname,$children,$hierarchy,
1.15      raeburn   558:         $checked_maps,$numhome,$chkname) = @_;
                    559:     my $check_uncheck = <<"FIRST";
1.1       raeburn   560: function checkAll(field) {
                    561:     if (field.length > 0) {
                    562:         for (i = 0; i < field.length; i++) {
                    563:             field[i].checked = true ;
                    564:         }
                    565:     } else {
                    566:         field.checked = true
                    567:     }
                    568: }
                    569: 
                    570: function uncheckAll(field) {
                    571:     if (field.length > 0) {
                    572:         for (i = 0; i < field.length; i++) {
                    573:             field[i].checked = false;
                    574:         }
                    575:     } else {
                    576:         field.checked = false;
                    577:     }
                    578: }
1.15      raeburn   579: FIRST
                    580:     if ($context eq 'shorturls') {
                    581:         return <<"END";
                    582: <script type="text/javascript">
                    583: // <![CDATA[
                    584: $check_uncheck
                    585: // ]]>
                    586: </script>
                    587: END
                    588:     }
                    589:     return unless ((ref($children) eq 'HASH') && (ref($hierarchy) eq 'HASH')
                    590:                    && (ref($checked_maps) eq 'ARRAY'));
                    591:     my ($elem,$nested,$nameforelem);
                    592:     if ($context eq 'dumpdocs') {
                    593:         $elem='((parseInt(item)-'.$startcount.')*2)+'.$startcount;
                    594:         $nested='((parseInt(nesting[item][i])-'.$startcount.')*2)+'.$startcount;
                    595:         $nameforelem=$elem+1;
                    596:     } else {
                    597:         $elem='parseInt(item)';
                    598:         $nested='parseInt(nesting[item][i])';
                    599:     }
                    600:     my $scripttag = <<"START";
                    601: <script type="text/javascript">
                    602: // <![CDATA[
                    603: $check_uncheck
1.1       raeburn   604: 
                    605: function checkFolder(form,item) {
1.7       raeburn   606:     var elem = $elem;
                    607:     if (form.elements[elem].checked == true) {
1.1       raeburn   608:         containerCheck(form,item);
                    609:     } else {
                    610:         containerUncheck(form,item);
                    611:     }
                    612: }
                    613: 
                    614: function checkResource(form,item) {
1.7       raeburn   615:     var elem = $elem;
                    616:     if (form.elements[elem].checked == false) {
1.1       raeburn   617:         containerUncheck(form,item);
                    618:     }
                    619: }
                    620: 
                    621: numitems = $numitems;
                    622: var parents = new Array(numitems);
                    623: var nesting = new Array(numitems);
                    624: var initial = new Array();
                    625: for (var i=$startcount; i<numitems; i++) {
                    626:     parents[i] = new Array();
                    627:     nesting[i] = new Array();
                    628: }
                    629: 
                    630: START
                    631: 
                    632:     foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
                    633:         my @contents = split(/:/,$children->{$container});
                    634:         for (my $i=0; $i<@contents; $i ++) {
                    635:             $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
                    636:         }
                    637:     }
                    638: 
                    639:     if ($context eq 'examblock') {
                    640:         foreach my $item (sort { $a <=> $b } (keys(%{$hierarchy}))) {
                    641:             $scripttag .= "nesting[$item] = new Array($hierarchy->{$item});\n";
                    642:         }
                    643:          
                    644:         my @sorted_maps = sort { $a <=> $b } (@{$checked_maps});
                    645:         for (my $i=0; $i<@sorted_maps; $i++) {
                    646:             $scripttag .= "initial[$i] = '$sorted_maps[$i]'\n";
                    647:         }
                    648:         $scripttag .= <<"EXTRA";
                    649: 
                    650: function recurseFolders() {
                    651:     if (initial.length > 0) {
                    652:         for (var i=0; i<initial.length; i++) {
                    653:             containerCheck(document.$formname,initial[i]);
                    654:         }
                    655:     }
                    656:     return;
                    657: }
                    658: 
                    659: EXTRA
1.7       raeburn   660:     } elsif ($context eq 'dumpdocs') {
                    661:         my $blankmsg = &mt('An item selected has no filename set in the "Save as ..." column.');
                    662:         my $dupmsg = &mt('Items selected for copying need unique filenames in the "Save as ..." column.');
1.11      bisitz    663:         my $homemsg = &mt('An Authoring Space needs to be selected.');
1.13      damieng   664:         &js_escape(\$blankmsg);
                    665:         &js_escape(\$dupmsg);
                    666:         &js_escape(\$homemsg);
1.7       raeburn   667:         $scripttag .= <<"EXTRA";
                    668: 
                    669: function checkUnique(form,field) {
                    670:     var duplicate = 0;
                    671:     var blank = 0;
1.9       raeburn   672:     var numhome = '$numhome';
1.7       raeburn   673:     if (field.length > 0) {
                    674:         for (i=0; i<field.length; i++) {
                    675:             if (field[i].checked) {
                    676:                 var item = field[i].value;
                    677:                 var namefor = document.getElementById('namefor_'+item);
                    678:                 if (namefor) {
                    679:                     var possval = namefor.value;
                    680:                     if (!possval) {
                    681:                         blank = item;
                    682:                         break;
                    683:                     }
                    684:                     for (j=i+1; j<field.length; j++) {
                    685:                         if (field[j].checked) {
                    686:                             var curritem = field[j].value;
                    687:                             var currnamefor = document.getElementById('namefor_'+curritem);
                    688:                             if (currnamefor) {
                    689:                                 var currval = currnamefor.value;
                    690:                                 if (currval == possval) {
                    691:                                     duplicate = curritem;
                    692:                                     break;
                    693:                                 }
                    694:                             }
                    695:                         }
                    696:                     }
                    697:                     if (duplicate) {
                    698:                         break;
                    699:                     }
                    700:                 }
                    701:             }
                    702:         }
                    703:     }
                    704:     if (blank) {
                    705:         alert('$blankmsg');
                    706:         return false;
                    707:     }
                    708:     if (duplicate) {
                    709:         alert('$dupmsg');
                    710:         return false;
                    711:     }
1.9       raeburn   712:     if (numhome > 1) {
                    713:         if (!form.authorspace.options[form.authorspace.selectedIndex].value) {
                    714:             alert('$homemsg');
                    715:             return false;
                    716:         }
                    717:     }
1.7       raeburn   718:     return true;
                    719: }
                    720: 
                    721: EXTRA
                    722: 
1.1       raeburn   723:     }
                    724: 
                    725:     $scripttag .= <<"END";
                    726: 
                    727: function containerCheck(form,item) {
1.7       raeburn   728:     var elem = $elem;
                    729:     form.elements[elem].checked = true;
1.1       raeburn   730:     if(Object.prototype.toString.call(parents[item]) === '[object Array]') {
                    731:         if (parents[item].length > 0) {
                    732:             for (var j=0; j<parents[item].length; j++) {
                    733:                 containerCheck(form,parents[item][j]);
                    734:             }
                    735:         }
                    736:     }
                    737: }
                    738: 
                    739: function containerUncheck(form,item) {
                    740:     if(Object.prototype.toString.call(nesting[item]) === '[object Array]') {
                    741:         if (nesting[item].length > 0) {
                    742:             for (var i=0; i<nesting[item].length; i++) {
1.7       raeburn   743:                 var nested = $nested;
1.1       raeburn   744:             }
                    745:         }
                    746:     }
                    747:     return;
                    748: }
                    749: 
                    750: END
                    751: 
                    752:     if ($context eq 'examblock') {
                    753:         $scripttag .= <<ENDEX;
                    754: function writeToOpener(maps,resources) {
                    755:     var checkedmaps = '';
                    756:     var checkedresources = '';
1.15      raeburn   757:     for (var i=0; i<document.$formname.${chkname}.length; i++) {
                    758:         if (document.$formname.${chkname}[i].checked) {
1.1       raeburn   759:             var isResource = 1;
                    760:             var include = 1;
                    761:             var elemnum = i+1+$startcount;
                    762:             if (Object.prototype.toString.call(parents[elemnum]) === '[object Array]') {
                    763:                 if (parents[elemnum].length > 0) {
                    764:                     isResource = 0;
                    765:                 }
                    766:             }
                    767:             if (isResource == 1) {
1.12      raeburn   768:                 if (nesting[elemnum] != null) {
                    769:                     if (nesting[elemnum].length > 0) {
                    770:                         var lastelem = nesting[elemnum].length-1;
                    771:                         if (document.$formname.elements[nesting[elemnum][lastelem]].checked) {
                    772:                             include = 0;
                    773:                         }
1.1       raeburn   774:                     }
                    775:                 }
                    776:             }
                    777:             if (include == 1) {
                    778:                 if (isResource == 1) {
1.15      raeburn   779:                     checkedresources += document.$formname.${chkname}[i].value+',';
1.1       raeburn   780:                 } else {
1.15      raeburn   781:                     checkedmaps += document.$formname.${chkname}[i].value+',';
1.1       raeburn   782:                 }
                    783:             }
                    784:         }
                    785:     }
                    786:     opener.document.getElementById(maps).value = checkedmaps;
                    787:     opener.document.getElementById(resources).value = checkedresources;
                    788:     window.close();
                    789:     return false;
                    790: }
                    791: 
                    792: ENDEX
                    793:     }
                    794: 
                    795:     $scripttag .= '
                    796: // ]]>
                    797: </script>
                    798: ';
                    799:     return $scripttag;
                    800: }
                    801: 
                    802: sub get_navmap_object {
                    803:     my ($crstype,$context) = @_;
                    804:     my $navmap = Apache::lonnavmaps::navmap->new();
                    805:     my $outcome;
                    806:     if (!defined($navmap)) {
                    807:         if ($context eq 'imsexport') {
                    808:             $outcome = &Apache::loncommon::start_page('Export '.$crstype.' to IMS Package').
                    809:                       '<h2>'.&mt('IMS Export Failed').'</h2>';
                    810:         } elsif ($context eq 'examblock') {
                    811:             $outcome = &Apache::loncommon::start_page('Selection of Resources for Blocking',
                    812:                                                        undef,{'only_body' => 1,}).
                    813:                       '<h2>'.&mt('Resource Display Failed').'</h2>';  
1.7       raeburn   814:         } elsif ($context eq 'dumpdocs') {
1.15      raeburn   815:             $outcome = '<h2>'.&mt('Copying to Authoring Space unavailable').'</h2>';
                    816:         } elsif ($context eq 'shorturls') {
                    817:             $outcome = '<h2>'.&mt('Display/Setting of shortened URLs unavailable').'</h2>';
1.7       raeburn   818:         }
1.1       raeburn   819:         $outcome .= '<div class="LC_error">';
                    820:         if ($crstype eq 'Community') {
                    821:             $outcome .= &mt('Unable to retrieve information about community contents');
                    822:         } else {
                    823:             $outcome .= &mt('Unable to retrieve information about course contents');
                    824:         }
                    825:         $outcome .= '</div>';
1.15      raeburn   826:         if (($context eq 'imsexport') || ($context eq 'dumpdocs') || ($context eq 'shorturls') ) {
1.1       raeburn   827:             $outcome .= '<a href="/adm/coursedocs">';
                    828:             if ($crstype eq 'Community') {
                    829:                 $outcome .= &mt('Return to Community Editor');
                    830:             } else {
                    831:                 $outcome .= &mt('Return to Course Editor');
                    832:             }
                    833:             $outcome .= '</a>';
1.7       raeburn   834:             if ($context eq 'imsexport') {
                    835:                 &Apache::lonnet::logthis('IMS export failed - could not create navmap object in '.lc($crstype).':'.$env{'request.course.id'});
1.17      raeburn   836:             } elsif ($context eq 'dumpdocs') {
1.8       raeburn   837:                 &Apache::lonnet::logthis('Copying to Authoring Space failed - could not create navmap object in '.lc($crstype).':'.$env{'request.course.id'});
1.17      raeburn   838:             } elsif ($context eq 'shorturls') {
                    839:                 &Apache::lonnet::logthis('Displaying and/or saving URL shortcuts failed - could not create navmap object in '.lc($crstype).':'.$env{'request.course.id'});
1.7       raeburn   840:             }
1.1       raeburn   841:         } elsif ($context eq 'examblock') {
                    842:             $outcome .=  '<href="javascript:window.close();">'.&mt('Close window').'</a>';         
                    843:         }
                    844:         return (undef,$outcome);
                    845:     } else {
                    846:         return ($navmap);
                    847:     }
                    848: }
                    849: 
1.7       raeburn   850: sub clean {
                    851:     my ($title)=@_;
                    852:     $title=~s/[^\w\/\!\$\%\^\*\-\_\=\+\;\:\,\\\|\`\~]+/\_/gs;
                    853:     return $title;
                    854: }
                    855: 
                    856: sub enumerate_course_contents {
                    857:     my ($navmap,$map_url,$resource_symb,$titleref,$context,$cdom,$cnum) = @_;
                    858:     if ((ref($navmap)) && (ref($map_url) eq 'HASH') &&
                    859:         (ref($resource_symb) eq 'HASH') && (ref($titleref) eq 'HASH')) {
                    860:         my $it = $navmap->getIterator(undef,undef,undef,1,undef,undef);
                    861:         my $count = 0;
                    862:         while (my $curRes = $it->next()) {
                    863:             if (ref($curRes)) {
                    864:                 my $symb = $curRes->symb();
                    865:                 my $ressymb = $symb;
                    866:                 if ($context eq 'dumpdocs') {
                    867:                     next unless (($curRes->src() =~ m{^\Q/uploaded/$cdom/$cnum/\E(docs|supplemental|simplepage)/}) ||
                    868:                                  ($curRes->src() =~ m{^\Q/uploaded/$cdom/$cnum/\E(default|supplemental)_\d+\.(sequence|page)}) ||
                    869:                                  ($curRes->src() eq '/res/lib/templates/simpleproblem.problem') ||
                    870:                                  ($curRes->src() =~ m{^/adm/$match_domain/$match_username/\d+/smppg}));
                    871:                 } elsif ($ressymb =~ m{adm/($match_domain)/($match_username)/(\d+)/bulletinboard$}) {
                    872:                     unless ($ressymb =~ m{adm/wrapper/adm}) {
                    873:                         $ressymb = 'bulletin___'.$3.'___adm/wrapper/adm/'.$1.'/'.$2.'/'.$3.
                    874:                                    '/bulletinboard';
                    875:                     }
                    876:                 }
                    877:                 $count ++;
1.15      raeburn   878:                 if ($context eq 'shorturls') {
                    879:                     $resource_symb->{$count} = $ressymb;
1.7       raeburn   880:                 } else {
1.15      raeburn   881:                     if (($curRes->is_sequence()) || ($curRes->is_page())) {
                    882:                         $map_url->{$count} = (&Apache::lonnet::decode_symb($symb))[2];
                    883:                     } else {
                    884:                         $resource_symb->{$count} = $ressymb;
                    885:                     }
1.7       raeburn   886:                 }
                    887:                 $titleref->{$count} = $curRes->title();
                    888:             }
                    889:         }
                    890:     }
                    891:     return;
                    892: }
                    893: 
1.1       raeburn   894: 1;

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