File:  [LON-CAPA] / loncom / interface / loncourserespicker.pm
Revision 1.3: download - view: text, annotated - select for diffs
Thu Jan 3 21:06:38 2013 UTC (11 years, 3 months ago) by raeburn
Branches: MAIN
CVS tags: HEAD
- Eliminate duplicate declarations.

# 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)" />'.
        '&nbsp;&nbsp;<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)" />'.
                '&nbsp;&nbsp;<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.' />&nbsp;'."\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 .= '&nbsp;'.$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>&nbsp;</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" />'.
                        '&nbsp;'.&mt('XML').'</label>'.('&nbsp;' x3).
                        '<label><input type="radio" name="format" value="html" />'.
                        '&nbsp;'.&mt('HTML').'</label>'.('&nbsp;' x3).
                        '<label><input type="radio" name="format" value="plaintext" />'.
                        '&nbsp;'.&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>