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