# The LearningOnline Network
#
# $Id: loncourserespicker.pm,v 1.3 2013/01/03 21:06:38 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
=pod
=head1 NAME
loncourserespicker - Utilities to choose folders and resources in a course.
=head1 SYNOPSIS
loncourserespicker provides an interface for selecting which folders and/or
resources are to be either:
(a) exported to an IMS Content Package
(b) subject to access blocking for the duriation of an exam/quiz.
=head1 DESCRIPTION
This module provides routines to generate a hierarchical display of folders
and resources in a course which can be selected for specific actions.
The choice of items is copied back to the main window from which the pop-up
window used to display the Course Contents was opened.
=head1 OVERVIEW
The main subroutine: &create_picker() will display the hierarchy of folders,
sub-folders, and resources in the Main Course Documents area. Items can be
selected using checkboxes, and/or a "Check All" button. Selection of a folder
causes the contents of the folder to also be selected automatically. The
propagation of check status is recursive into sub-folders. Likewise, if an
item deep in a nested set of folders and sub-folders is unchecked, the
uncheck will propagate up through the hierarchy causing any folders at
a higher level to become unchecked.
There is a submit button, which will be named differently according to the
content in which resource/folder selection is being made.
The two contexts currently supported are: IMS export and selection of
content to be subject to access restructions for the duration of an
exam.
=head1 INTERNAL SUBROUTINES
=item &create_picker()
Created HTML mark up to display contents of course with checkboxes to
select items. Checking a folder causes recursive checking of items
within the folder. Unchecking a resource causing unchecking of folders
containing the item back up to the top level.
Inputs: 7.
- $navmap -- Reference to LON-CAPA navmap object
(encapsulates information about resources in the course).
- $context -- Context in which course resource selection is being made.
Currently imsexport and examblock are supported.
- $formname -- Name of the form in the window from which the pop-up
used to select course items was launched.
- $crstype -- Course or Community
- $blockedmaps -- Reference to hash of previously selected maps
(e.g., for a live exam block).
- $blockedresources -- Reference to hash of resources selected
previously (e.g., for an exam block).
- $block -- An internal ID (integer) used to track which exam
block currently being configured.
Output: $output is the HTML mark-up for display/selection of content
items in the pop-up window.
=item &respicker_javascript()
Creates javascript functions for checking/unchecking all items, and
for recursive checking triggered by checking a folder, or recursive
(upeards) unchecking of an item within a folder.
Inputs: 7.
- $startcount -- Starting offset of form element numbering for items
- $numcount -- Total numer of folders and resources in course.
- $context -- Context in which resources are being displayed
(imsexport or examblock).
- $formname -- Name of form.
- $children -- Reference to hash of items contained within a folder.
- $hierarchy -- Reference to hierarchy of folders containing an item.
- $checked_maps -- Reference to array of folders currently checked.
Output: 1. Javascript (witthin <script></script> tags.
=item &get_navmap_object()
Instantiates a navmaps object, and generates an error message if
no object instantiated.
Inputs: 2.
- $crstype -- Container type: Course or Community
- $context -- Context: imsexport or examblock
=over
=back
=cut
package Apache::loncourserespicker;
use strict;
use Apache::lonnet;
use Apache::loncommon;
use Apache::lonhtmlcommon;
use Apache::lonnavmaps;
use Apache::londocs;
use Apache::lonlocal;
use LONCAPA qw(:DEFAULT :match);
sub create_picker {
my ($navmap,$context,$formname,$crstype,$blockedmaps,$blockedresources,$block) = @_;
return unless (ref($navmap));
my ($it,$output,$numdisc,%maps,%resources,%discussiontime,%currmaps,%currresources);
$it = $navmap->getIterator(undef,undef,undef,1,undef,undef);
if (ref($blockedmaps) eq 'HASH') {
%currmaps = %{$blockedmaps};
}
if (ref($blockedresources) eq 'HASH') {
%currresources = %{$blockedresources};
}
my @checked_maps;
my $curRes;
my $numprobs = 0;
my $depth = 0;
my $count = 0;
my $boards = 0;
my $startcount = 1;
my %parent = ();
my %children = ();
my %hierarchy = ();
my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons");
my $whitespace =
'<img src="'.$location.'/whitespace_21.gif" class="LC_docs_spacer" alt="" />';
my $onsubmit;
if ($context eq 'examblock') {
my $maps_elem = 'docs_maps_'.$block;
my $res_elem = 'docs_resources_'.$block;
$onsubmit = ' onsubmit="return writeToOpener('."'$maps_elem','$res_elem'".');"';
}
my $display =
'<form name="'.$formname.'" action="" method="post"'.$onsubmit.'>'."\n".
'<p>';
if ($context eq 'imsexport') {
$display .= &mt('Choose which items you wish to export from your '.
$crstype.'.');
$startcount = 5;
} elsif ($context eq 'examblock') {
$display .= &mt('Items in '.lc($crstype).' for which access will be blocked.');
}
$display .= '</p>';
if ($context eq 'imsexport') {
$display .= '<div class="LC_columnSection">'."\n".
'<fieldset>'.
'<legend>'.&mt('Content items').'</legend>'."\n";
}
$display .=
'<input type="button" value="'.&mt('check all').'" '.
'onclick="javascript:checkAll(document.'.$formname.'.archive)" />'.
' <input type="button" value="'.&mt('uncheck all').'"'.
' onclick="javascript:uncheckAll(document.'.$formname.'.archive)" />';
if ($context eq 'imsexport') {
$display .= '</fieldset>';
%discussiontime =
&Apache::lonnet::dump('discussiontimes',
$env{'course.'.$env{'request.course.id'}.'.domain'},
$env{'course.'.$env{'request.course.id'}.'.num'});
$numdisc = keys(%discussiontime);
if ($numdisc > 0) {
$display .=
'<fieldset>'.
'<legend>'.&mt('Discussion posts').'</legend>'.
'<input type="button" value="'.&mt('check all').'"'.
' onclick="javascript:checkAll(document.'.$formname.'.discussion)" />'.
' <input type="button" value="'.&mt('uncheck all').'"'.
' onclick="javascript:uncheckAll(document.'.$formname.'.discussion)" />'.
'</fieldset>';
}
$display .= '</div>';
}
my $lastcontainer = $startcount;
$display .= &Apache::loncommon::start_data_table()
.&Apache::loncommon::start_data_table_header_row();
if ($context eq 'imsexport') {
$display .= '<th>'.&mt('Export content item?').'</th>';
if ($numdisc > 0) {
$display .= '<th>'.&mt('Export discussion posts?').'</th>';
}
} elsif ($context eq 'examblock') {
$display .= '<th>'.&mt('Access blocked?').'</th>';
}
$display .= &Apache::loncommon::end_data_table_header_row();
while ($curRes = $it->next()) {
if (ref($curRes)) {
$count ++;
}
if ($curRes == $it->BEGIN_MAP()) {
$depth++;
$parent{$depth} = $lastcontainer;
}
if ($curRes == $it->END_MAP()) {
$depth--;
$lastcontainer = $parent{$depth};
}
if (ref($curRes)) {
my $symb = $curRes->symb();
my $ressymb = $symb;
if ($ressymb =~ m|adm/($match_domain)/($match_username)/(\d+)/bulletinboard$|) {
unless ($ressymb =~ m|adm/wrapper/adm|) {
$ressymb = 'bulletin___'.$3.'___adm/wrapper/adm/'.$1.'/'.$2.'/'.$3.'/bulletinboard';
}
}
my $currelem = $count+$boards+$startcount;
$display .= &Apache::loncommon::start_data_table_row().
'<td>'."\n".
'<input type="checkbox" name="archive" value="'.$count.'" ';
if (($curRes->is_sequence()) || ($curRes->is_page())) {
$lastcontainer = $currelem;
$display .= 'onclick="javascript:checkFolder(this.form,'."'$currelem'".')" ';
my $mapurl = (&Apache::lonnet::decode_symb($symb))[2];
if ($currmaps{$mapurl}) {
$display .= 'checked="checked"';
push(@checked_maps,$currelem);
}
} else {
if ($curRes->is_problem()) {
$numprobs ++;
}
$display .= 'onclick="javascript:checkResource(this.form,'."'$currelem'".')" ';
if ($currresources{$symb}) {
$display .= 'checked="checked"';
}
}
$display .= ' />'."\n";
for (my $i=0; $i<$depth; $i++) {
$display .= "$whitespace\n";
}
my $icon = 'src="'.$location.'/unknown.gif" alt=""';
if ($curRes->is_sequence()) {
$icon = 'src="'.$location.'/navmap.folder.open.gif" alt="'.&mt('"Folder').'"';
} elsif ($curRes->is_page()) {
$icon = 'src="'.$location.'/navmap.page.open.gif" alt="'.&mt('Composite Page').'"';
} elsif ($curRes->is_problem()) {
$icon = 'src="'.$location.'/problem.gif" alt="'.&mt('Problem').'"';
} elsif ($curRes->is_task()) {
$icon = 'src="'.$location.'/task.gif" alt="'.&mt('Task').'"';
} elsif ($curRes->src ne '') {
$icon = 'src="'.&Apache::loncommon::icon($curRes->src).'" alt=""';
}
$display .= '<img '.$icon.' /> '."\n";
$children{$parent{$depth}} .= $currelem.':';
if ($context eq 'examblock') {
if ($parent{$depth} > 1) {
if ($hierarchy{$parent{$depth}}) {
$hierarchy{$currelem} = $hierarchy{$parent{$depth}}.",'$parent{$depth}'";
} else {
$hierarchy{$currelem} = "'$parent{$depth}'";
}
}
}
$display .= ' '.$curRes->title().'</td>'."\n";
if ($context eq 'imsexport') {
# Existing discussion posts?
if ($discussiontime{$ressymb} > 0) {
$boards ++;
$display .= '<td align="right">'
.'<input type="checkbox" name="discussion" value="'.$count.'" />'
.'</td>'."\n";
} elsif ($numdisc > 0) {
$display .= '<td> </td>'."\n";
}
}
$display .= &Apache::loncommon::end_data_table_row();
}
}
$display .= &Apache::loncommon::end_data_table();
if ($context eq 'imsexport') {
if ($numprobs > 0) {
$display .= '<p><span class="LC_nobreak">'.
&mt('Export format for LON-CAPA problems:').
'<label><input type="radio" name="format" value="xml" checked="checked" />'.
' '.&mt('XML').'</label>'.(' ' x3).
'<label><input type="radio" name="format" value="html" />'.
' '.&mt('HTML').'</label>'.(' ' x3).
'<label><input type="radio" name="format" value="plaintext" />'.
' '.&mt('Text').'</label></span></p>';
}
}
$display .= '<p>';
if ($context eq 'imsexport') {
$display .=
'<input type="hidden" name="finishexport" value="1" />'.
'<input type="submit" name="exportcourse" value="'.
&mt('Export').'" />';
} elsif ($context eq 'examblock') {
$display .=
'<input type="submit" name="resourceblocks" value="'.
&mt('Copy Choices to Main Window ').'" />';
}
$display .= '</p></form>';
my $numcount = $count + $boards + $startcount;
my $scripttag =
&respicker_javascript($startcount,$numcount,$context,$formname,\%children,
\%hierarchy,\@checked_maps);
my ($title,$crumbs,$args);
if ($context eq 'imsexport') {
$title = 'Export '.$crstype.' to IMS Package';
} elsif ($context eq 'examblock') {
$title = 'Resources with Access blocked';
$args = {'only_body' => 1,
'add_entries' => { onload => 'javascript:recurseFolders();' },
};
}
$output = &Apache::loncommon::start_page($title,$scripttag,$args);
if ($context eq 'imsexport') {
$output .= &Apache::lonhtmlcommon::breadcrumbs('IMS Export').
&Apache::londocs::startContentScreen('tools');
}
$output .= $display;
if ($context eq 'examblock') {
$output .= &Apache::loncommon::end_page();
} elsif ($context eq 'imsexport') {
$output .= &Apache::londocs::endContentScreen();
}
return $output;
}
sub respicker_javascript {
my ($startcount,$numitems,$context,$formname,$children,$hierarchy,
$checked_maps) = @_;
return unless ((ref($children) eq 'HASH') && (ref($hierarchy) eq 'HASH')
&& (ref($checked_maps) eq 'ARRAY'));
my $scripttag = <<"START";
<script type="text/javascript">
// <![CDATA[
function checkAll(field) {
if (field.length > 0) {
for (i = 0; i < field.length; i++) {
field[i].checked = true ;
}
} else {
field.checked = true
}
}
function uncheckAll(field) {
if (field.length > 0) {
for (i = 0; i < field.length; i++) {
field[i].checked = false;
}
} else {
field.checked = false;
}
}
function checkFolder(form,item) {
if (form.elements[item].checked == true) {
containerCheck(form,item);
} else {
containerUncheck(form,item);
}
}
function checkResource(form,item) {
if (form.elements[item].checked == false) {
containerUncheck(form,item);
}
}
numitems = $numitems;
var parents = new Array(numitems);
var nesting = new Array(numitems);
var initial = new Array();
for (var i=$startcount; i<numitems; i++) {
parents[i] = new Array();
nesting[i] = new Array();
}
START
foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
my @contents = split(/:/,$children->{$container});
for (my $i=0; $i<@contents; $i ++) {
$scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
}
}
if ($context eq 'examblock') {
foreach my $item (sort { $a <=> $b } (keys(%{$hierarchy}))) {
$scripttag .= "nesting[$item] = new Array($hierarchy->{$item});\n";
}
my @sorted_maps = sort { $a <=> $b } (@{$checked_maps});
for (my $i=0; $i<@sorted_maps; $i++) {
$scripttag .= "initial[$i] = '$sorted_maps[$i]'\n";
}
$scripttag .= <<"EXTRA";
function recurseFolders() {
if (initial.length > 0) {
for (var i=0; i<initial.length; i++) {
containerCheck(document.$formname,initial[i]);
}
}
return;
}
EXTRA
}
$scripttag .= <<"END";
function containerCheck(form,item) {
form.elements[item].checked = true;
if(Object.prototype.toString.call(parents[item]) === '[object Array]') {
if (parents[item].length > 0) {
for (var j=0; j<parents[item].length; j++) {
containerCheck(form,parents[item][j]);
}
}
}
}
function containerUncheck(form,item) {
if(Object.prototype.toString.call(nesting[item]) === '[object Array]') {
if (nesting[item].length > 0) {
for (var i=0; i<nesting[item].length; i++) {
form.elements[nesting[item][i]].checked = false;
}
}
}
return;
}
END
if ($context eq 'examblock') {
$scripttag .= <<ENDEX;
function writeToOpener(maps,resources) {
var checkedmaps = '';
var checkedresources = '';
for (var i=0; i<document.$formname.archive.length; i++) {
if (document.$formname.archive[i].checked) {
var isResource = 1;
var include = 1;
var elemnum = i+1+$startcount;
if (Object.prototype.toString.call(parents[elemnum]) === '[object Array]') {
if (parents[elemnum].length > 0) {
isResource = 0;
}
}
if (isResource == 1) {
if (nesting[elemnum].length > 0) {
var lastelem = nesting[elemnum].length-1;
if (document.$formname.elements[nesting[elemnum][lastelem]].checked) {
include = 0;
}
}
}
if (include == 1) {
if (isResource == 1) {
checkedresources += document.$formname.archive[i].value+',';
} else {
checkedmaps += document.$formname.archive[i].value+',';
}
}
}
}
opener.document.getElementById(maps).value = checkedmaps;
opener.document.getElementById(resources).value = checkedresources;
window.close();
return false;
}
ENDEX
}
$scripttag .= '
// ]]>
</script>
';
return $scripttag;
}
sub get_navmap_object {
my ($crstype,$context) = @_;
my $navmap = Apache::lonnavmaps::navmap->new();
my $outcome;
if (!defined($navmap)) {
if ($context eq 'imsexport') {
$outcome = &Apache::loncommon::start_page('Export '.$crstype.' to IMS Package').
'<h2>'.&mt('IMS Export Failed').'</h2>';
} elsif ($context eq 'examblock') {
$outcome = &Apache::loncommon::start_page('Selection of Resources for Blocking',
undef,{'only_body' => 1,}).
'<h2>'.&mt('Resource Display Failed').'</h2>';
}
$outcome .= '<div class="LC_error">';
if ($crstype eq 'Community') {
$outcome .= &mt('Unable to retrieve information about community contents');
} else {
$outcome .= &mt('Unable to retrieve information about course contents');
}
$outcome .= '</div>';
if ($context eq 'imsexport') {
$outcome .= '<a href="/adm/coursedocs">';
if ($crstype eq 'Community') {
$outcome .= &mt('Return to Community Editor');
} else {
$outcome .= &mt('Return to Course Editor');
}
$outcome .= '</a>';
&logthis('IMS export failed - could not create navmap object in '.lc($crstype).':'.$env{'request.course.id'});
} elsif ($context eq 'examblock') {
$outcome .= '<href="javascript:window.close();">'.&mt('Close window').'</a>';
}
return (undef,$outcome);
} else {
return ($navmap);
}
}
1;
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>