File:  [LON-CAPA] / loncom / interface / loncourserespicker.pm
Revision 1.14: download - view: text, annotated - select for diffs
Sun Oct 16 21:49:51 2016 UTC (7 years, 7 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_11_X, version_2_11_4_uiuc, version_2_11_4, version_2_11_3_uiuc, version_2_11_3, version_2_11_2_uiuc, version_2_11_2_educog, version_2_11_2, HEAD
- vpa privilege in a course allows Content Settings to be displayed, but
  not modified (which requires opa priv).
- vcb privilege in a course allows "Exam" Blocking items to be displayed,
  but not modified (which requires dcm priv).

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

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