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: ' <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: ' <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').(' '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> </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> </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.' /> '."\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 .= ' '.$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> </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: ' '.&mt('XML').'</label>'.(' ' x3).
483: '<label><input type="radio" name="format" value="html" />'.
484: ' '.&mt('HTML').'</label>'.(' ' x3).
485: '<label><input type="radio" name="format" value="plaintext" />'.
486: ' '.&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>