File:  [LON-CAPA] / loncom / interface / loncourserespicker.pm
Revision 1.15: download - view: text, annotated - select for diffs
Fri Jan 12 13:33:38 2018 UTC (6 years, 3 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_11_4_msu, version_2_11_3_msu, version_2_11_2_msu, HEAD
- Bug 6400 Tiny URL for deep-linking.
  Requires Short::URL and String::CRC32 modules from CPAN. Dependency on
  perl-Short-URL needs to be added to LONCAPA-prerequisites.

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

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