--- loncom/interface/lonblockingmenu.pm	2011/12/28 22:41:02	1.3
+++ loncom/interface/lonblockingmenu.pm	2025/01/17 14:01:50	1.14.2.3.6.3
@@ -1,8 +1,8 @@
 # The LearningOnline Network with CAPA
-# Routines for configuring blocking to collaborative functions, and specific
-# resources during an exam 
+# Routines for configuring blocking of access to collaborative functions, 
+# and specific resources during an exam
 #
-# $Id: lonblockingmenu.pm,v 1.3 2011/12/28 22:41:02 raeburn Exp $
+# $Id: lonblockingmenu.pm,v 1.14.2.3.6.3 2025/01/17 14:01:50 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -26,7 +26,7 @@
 #
 # http://www.lon-capa.org/
 #
-###############################################################
+##############################################################
 ##############################################################
 
 =pod
@@ -44,25 +44,401 @@ lonblockingmenu provides an interface fo
 This module is used to configure blocking of access to collaborative tools
 and/or resources during an exam.
 
+=head1 OVERVIEW
+
+To support high-stakes testing, LON-CAPA provides Coordinators with the
+ability to disable communication and collaborative features within the
+system for the duration of an exam.
+
+Features which can be disabled include:
+(a) those which a student could use to communicate with another student.
+Messaging, discussion, chat, blogs, and some functionality in groups fall 
+into this category.
+(b) those which a student could use to access materials prepared by the
+student in advance of an exam, (e.g., for use during an online exam, to
+gain an unfair advantage). Blogs and portfolio fall into this category.
+(c) those which a student could use to display or save content within
+the course itself (outside the exam folder). Printouts and resources
+fall into this category.
+
+For communication blocking to be truly effective in preventing unwanted
+communication, or access to online materials, online testing needs to
+take place in a lab setting where use of tools outside LON-CAPA, and use
+of web sites beyond LON-CAPA are unavailable.
+
+Access to specified folder(s) and/or resources in the course contents 
+can be restricted for the duration of an exam.
+
+Exam blocks are of two types:
+(a) Blocks with a defined start and end date.
+(b) Blocks associated with a timed interval set for a specific folder,
+or resource.
+
+When a student attempts to use a collaboration or communication feature
+which is currently blocked, information will be available about the
+duration of the block, and the identity of the Course Coordinator who
+set the block.
+
+Although LON-CAPA communication can be blocked during an exam, course
+personnel with the 'evb' (evade blocking) privilege will continue to
+receive LON-CAPA messages sent from students in a course with an active
+block on messaging. Students will not be able to view messages sent by
+other students in the same course for the duration of the blocking event.
+
+Because students may be enrolled in more than one LON-CAPA course at a time
+it is important to use reasonable time windows for blocking events, or, in
+the case of blocks triggered by clicking a button to start a timed quiz, 
+quiz durations that are of limited duration. This is especially important
+when blocking prtfolio access, as other courses may require students to use
+the portfolio as a mechanism for submitting assignments.
+
+Information about blocks in a course will be cached for 10 minutes, so,
+as with parameters set for resources, it can take up to 10 minutes for
+new blocks, or changes to existing blocks, to propagate to other servers.
+
+Changes to existing blocks on the server hosting your current session
+are available immediately, as cached data on blocks is devalidated
+automatically on the current server whenever a change is made to a 
+block (including deletion), or when a new block is added. 
+
 =head1 INTERNAL SUBROUTINES
 
 =over
 
+=item &get_permission()
+
+Returns information about permission user has to set/modify exam
+blocking events.
+
+Inputs: None
+
+Outputs: 2
+    $readonly - true if modification of blocking events is prohibited.
+
+    $allowed  - true if blocking events information can be shown.
+
+
+=item &get_timed_items()
+
+Provides perl data structure with information about timed interval
+parameters set in a course.
+
+Inputs: 2 (optional)
+       $cdom - course's domain
+
+       $cnum - course's ID
+
+Output: 1 Hash 
+       nested hashes containing information about timed interval
+       parameters in course). Top level keys are type: course,
+       map, resource. Next inner keys are map or symb. Next
+       inner keys are scope (all, section, group, users).
+       Values are interval (in seconds).
+
 =item &blockstore()
 
+Stores changes to exam blocks in comm_block.db file for course.
+Processes deletions, modifications and additions.
+
+Inputs: 4
+      $r = request object
+
+      $crstype - Container type: Course or Community.
+
+      $blockcount - Total number of blocking events in course.
+
+      $currblockrecs - Ref to hash of current blocks in course.
+
+Outputs: 2
+      $changestotal - Total number of changes made.
+
+      $output - Information about changes made.
+
+
 =item &get_dates_from_form()
 
+Extract start and end dates from web form input for blocks with
+defined start/end time.
+
+Inputs: 1 - $item - numeric ID of current block.
+
+Outputs: 2 - $startdate, $enddate (UNIX times for start and end times
+             for blocks with defined start/end   
+
+
 =item &get_blockdates()
 
+Retrieves contents of comm_block.db file for a course.
+
+Inputs: 1 - $records - reference to hash to contain blocks 
+
+Outputs: 1 - $blockcount - number of blocks
+
+Side Effects: populates records hashref.
+
+
 =item &get_block_choices()
 
+Extract information from web form about which communication/
+collaboration features are to be blocked, for a particular event,
+and also which content areas will have access blocked for the
+duration of the block.
+
+Inputs: 3 
+    - $item - numeric ID of current block 
+
+    - $map_ref - reference to hash mapping numeric IDs to map urls 
+
+    - $symb_ref - reference to hash mapping numeric IDs to symbs
+
+Outputs: 2
+    - $blocktypes - reference to hash of features to be blocked
+
+    - $blockdocs - boolean - 0 if no blocking of content, 1 if blocking 
+                             of content access 
+
+
+=item &check_release_required()
+
+Update LON-CAPA version requirements for course if blocked items
+(content) or blocking type (triggered by student starting timer)
+require specific LON-CAPA version (i.e., 2.11).
+
+Inputs: 3 - $value - type of constraint (currently: 'docs', 'printout' or 'timer'),
+            $chomemajor - course's home server LON-CAPA major version number.
+            $chomeminor - course's home server LON-CAPA minor version number.
+
+Outputs: 2 - status ('ok' or 'fail') and LON-CAPA version needed.
+
+=over
+
+             A status of 'fail' will be returned if the 
+             LON-CAPA version installed on the course's 
+             home server is older than the version 
+             requirement for the blocking type.
+             For a trigger type event, the requested
+             blocking event will not be added if 
+             the course's home server version is old to
+             support that type of block.
+
+=back
+
+Side Effects: &update_released_required() called in lonnet, if
+              course's home server version is requied version or 
+              newer; will update version requirements for course to
+              a more recent version requirement than currently in
+              effect.
+
+
 =item &display_blocker_status()
 
+Generates web form elements used to display, cancel, or modify 
+existing blocking events. 
+
+Inputs: 8 
+      - $r - Apache request object
+
+      - $records - Reference to hash of current blocks
+
+      - $ltext - Reference to hash of phrases (localized)
+
+      - $intervals - Reference to hash of parameters for timed intervals
+
+      - $navmap - navmaps object.
+
+      - $errormsg - error message for display, if navmaps object
+                    could not be instantiated
+
+      - $blockcount - number of existing blocking events in course
+
+      - $readonly - if true, modification not allowed.
+
+
+Output: None
+
+Side Effects: prints web form elements (in a table) for current blocks. 
+
+=item &convlim()
+
+Convert a time interval used for a timed quiz (in seconds) to
+days, hours. minutes and seconds.
+
+Inputs: 1 - $timelimit  - time interval in seconds
+
+Outputs: 1 - $output - time in format: DD days, HH hours, MM minutes, SS seconds  
+
+
 =item &display_addblocker_table()
 
+Generate web form elements used to define a new blocking event. 
+
+Inputs: 6
+    - $r - Apache resource object
+
+    - $parmcount - current ID for block (same as number of current blocks,
+                   block IDs in web form have zero-based index)
+
+    - $ltext - reference to hash of phrases (localized)
+
+    - $intervals - Reference to hash of parameters for timed intervals
+
+    - $navmap - navmaps object
+
+    - $errormsg - error message for display, if navmaps object
+                  could not be instantiated
+
+Outputs: None
+ 
+Side Effects: prints web form elements (in a table) for adding a new block.
+
+
+=item &blocker_checkboxes()
+
+Generates web form elements in a table for checkboxes used to indicate
+which types of communication/collaboration and/or content should be blocked.
+
+Inputs: 4 
+    - $parmcount - numeric ID of current block
+
+    - $blocks - reference to hash of functionalities to block 
+
+    - $jschg - text of javascript call to execute when checkbox clicked  
+               use within a box via 'onclick="$jchg"'
+ 
+    - $lookups - reference to hash to map urls or symbs to numeric IDs
+                 used to populate hodden form elements containing list
+                 of resources and folders with access blocking currently set.
+
+Output: 1 - HTML for table of checkboxes for current block  
+
+
+=item &create_interval_form()
+
+Creates web form elements used to select one of the defined timed interval 
+items in the course for use in an exam block of type: "Triggered by 
+Activating Timer".
+
+Inputs: 8 (four required, last four optional)
+   - $intervals - Reference to hash of parameters for timed intervals
+
+   - $parmcount - numeric ID of current block
+
+   - $navmap - navmaps object
+
+   - $context - this will be "accesstimes" if called by lonaccesstimes.pm,
+                or "blocking" if called internally by lonblockingmenu.pm
+
+   - $currkey - current interval (where this is a block already using
+                an interval-based trigger).  
+
+   - $jschg - text of javascript call to execute when radiobutton clicked
+              use within a box via 'onclick="$jchg"'
+
+   - $itemname - name/scope of current interval used for this block 
+
+   - $iteminfo - Expandable/collapsible block showing which users are
+                 able to activate the timer using the current trigger item.
+
+Outputs: 1 - $intervalform - web form elements used to select a time interval
+
+
+=item &interval_details()
+
+Creates name/scope of current interval and expandable/collapsible
+showing which interval parameters apply to the current folder/resource
+
+Inputs: 6
+
+    - $item - course, map url, or resource symb
+
+    - $type - course, map, or resource
+
+    - $url  - url of item (null if item is course).
+
+    - $navmap - navmaps object
+
+    - $intervals - Reference to hash of parameters for timed intervals
+
+    - $parmcount - unique ID for current element.
+
+
+Outputs: 2
+
+   - $itemname - name/scope of interval (timer) parameter
+
+   - $iteminfo - Expandable/collapsible block showing which interval
+                 (timer) parameters affect the current folder or resource.
+
+
+=item &trigger_details_toggle()
+ 
+Creates link used to expand item showing information about timer for current
+trigger for exam block. 
+
+Inputs: 1 - $parmcount - numericID of exam block in web form. 
+
+Outputs: 1 - returns HTML for link to display contents of information item 
+
+
+=item &show_timer_path()
+
+Display hierarchy of names of folders/sub-folders containing the current
+item identified as an item with an interval timer set.
+
+Inputs: 3
+    - $type - map or resource
+
+    - $item - map URL or resource symb
+
+    - $navmap - navmaps object
+
+Outputs: 1 - HTML containing hierarchy of folders/subfolders (raquo entity separated).  
+
+
 =item &blocktype_text()
 
-=back  
+Inputs: None
+
+Output: 2 
+     - $typeorder - reference to array of blockable communication/collaboration/content
+
+     - $types -reference to hash of descriptions (localized) of blockable types.
+ 
+
+=item &blockingmenu_javascript()
+
+Create Javascript used to launch pop-up used for content selection, and to
+toggle visibility of a number of expandable/collapsible divs.
+
+Inputs: 1 - $blockcount - Total number of blocks in course's comm_block.db
+                          database file. 
+
+Output: 1 - Javascript (with <script></script> tags) for functions used to:
+            (a) launch pop-up window for selection of course content to which
+            access could be blocked. 
+            (b) toggle visibility of a number of divs:
+
+=over 
+
+=item *  for block type - defined dates or timer activated
+
+=item *  for action to take -- add or modify block
+
+=back
+
+
+=item &details_javascript()
+
+Create Javascript to toggle visibility of unordered list item
+containing details about item with timed interval parameter.
+
+Inputs: none
+
+Output: 1 Javascript (with <script></script> tags) for functions used to:
+          toggle visibility of unordered list for display of detailed
+          information about intervals.
+
+=back
 
 =cut
 
@@ -73,11 +449,16 @@ use Apache::lonnet;
 use Apache::Constants qw(:common :http);
 use Apache::loncommon();
 use Apache::lonhtmlcommon();
+use Apache::lonparmset();
+use Apache::loncourserespicker();
 use HTML::Entities();
 use Apache::lonlocal;
 use lib '/home/httpd/lib/perl/';
 use LONCAPA qw(:DEFAULT :match);
 
+my $registered_cleanup;
+my $modified_courses;
+
 sub handler {
     my $r=shift;
 
@@ -98,20 +479,74 @@ sub handler {
 
 # ----------------------------------------------------------- Permissions check
 
-    unless ((&Apache::lonnet::allowed('dcm',$env{'request.course.id'})) ||
-            (&Apache::lonnet::allowed('dcm',$env{'request.course.id'}.
-                                      '/'.$env{'request.course.sec'}))) {
+    my ($readonly,$allowed) = &get_permission();
+    unless ($allowed) {
         $env{'user.error.msg'}=
-     "/adm/setblock:dcm:0:0:Cannot set blocking of communications in a course";
+     "/adm/setblock:dcm:0:0:Cannot view/set blocking of communications in a course";
         return HTTP_NOT_ACCEPTABLE;
     }
 
 # -----------------------------Get action and calling context from query string
 
-    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','caller']);
+    $registered_cleanup=0;
+    @{$modified_courses}=();
+
+    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+                                            ['action','caller','block']);
+
+    my $crstype = &Apache::loncommon::course_type();
+    my $action = $env{'form.action'};
+    my %records = ();
+    my $blockcount = 0;
+
+# ------------------------------------------------------ Retrieve current blocks
+    $blockcount = &get_blockdates(\%records);
+
+# -------------------- Generate display for pop-up of Maps and Resources blocked   
+    if ($action eq 'showdocs') {
+        my ($navmap,$errormsg) = 
+            &Apache::loncourserespicker::get_navmap_object($crstype,'examblock');
+        if (ref($navmap)) {
+            my (%blockedmaps,%blockedresources);
+            if ($env{'form.block'} =~ /^\d+$/) {
+                my @currblocks = sort(keys(%records));
+                my $block = $currblocks[$env{'form.block'}];
+                if (($block ne '') && (ref($records{$block}) eq 'HASH')) {
+                    if (ref($records{$block}{'blocks'}) eq 'HASH') {
+                        if (ref($records{$block}{'blocks'}{'docs'}) eq 'HASH') {
+                            if (ref($records{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') {
+                                %blockedmaps = %{$records{$block}{'blocks'}{'docs'}{'maps'}};
+                            }
+                            if (ref($records{$block}{'blocks'}{'docs'}{'resources'}) eq 'HASH') {
+                                %blockedresources = %{$records{$block}{'blocks'}{'docs'}{'resources'}};
+                            }
+                        }
+                    }
+                }
+            }
+            $r->print(&Apache::loncourserespicker::create_picker($navmap,
+                                     'examblock','resourceblocks',$crstype,
+                                     \%blockedmaps,\%blockedresources,
+                                     $env{'form.block'},'','',undef,undef,undef,
+                                     $readonly));
+        } else {
+            $r->print($errormsg);
+        }
+        return OK;
+    }
 
-# ----------------------------------------------------------------- Breadcrumbs
+# -------------------------- Store changes and retrieve latest block information
+    my $storeresult;
+    unless ($readonly) {
+        if ($env{'form.action'} eq 'store') {
+            (my $numchanges,$storeresult) = &blockstore($r,$crstype,$blockcount,\%records);
+            if ($numchanges > 0) {
+                $blockcount = &get_blockdates(\%records);
+            }
+        }
+    }
 
+# ------------------------------------------------------------------ Breadcrumbs
     &Apache::lonhtmlcommon::clear_breadcrumbs();
     if ($env{'form.caller'} eq 'email') {  
         &Apache::lonhtmlcommon::add_breadcrumb
@@ -125,78 +560,211 @@ sub handler {
     }
     &Apache::lonhtmlcommon::add_breadcrumb
         ({href=>'/adm/setblock',
-          text=>'Blocking communication/resource access'});
+          text=>'Blocking communication/content access'});
 
-    $r->print(&Apache::loncommon::start_page('Blocking communication/resource access').
-              &Apache::lonhtmlcommon::breadcrumbs('Blocking communication/resource access'));
+    my $js = &blockingmenu_javascript($blockcount).
+             &details_javascript();
 
-# ----------------------------------------------------------- Permissions check
+    $r->print(
+        &Apache::loncommon::start_page('Blocking communication/content access',$js).
+        &Apache::lonhtmlcommon::breadcrumbs('Blocking communication/content access'));
 
     my $usertype;
-    my $crstype = &Apache::loncommon::course_type();
     if ($crstype eq 'Community') {
         $usertype = 'members';
     } else {
         $usertype = 'students';
     }
     my $lctype = lc($crstype);
-    my %lt=&Apache::lonlocal::texthash(
-            'cbds' => 'Communication blocking during scheduled exams',
-            'desc' => "You can use communication blocking to prevent $usertype enrolled in this $lctype from displaying LON-CAPA messages sent by other $usertype during an online exam. As blocking of communication could potentially interrupt legitimate communication between $usertype who are also both enrolled in a different LON-CAPA course or community, please be careful that you select the correct start and end times for your scheduled exam when setting or modifying these parameters.",
-             'mecb' => 'Modify existing communication blocking periods',
-             'ncbc' => 'No communication blocks currently saved',
-             'stor' => 'Save',
+    my %lt=&Apache::lonlocal::texthash (
+            'cbds' => 'Blocking communication and/or content access during exams',
+            'prev' => "For the duration of an exam, or a timed quiz, students in this course can be prevented from:",
+            'flow' => "For the duration of an exam, or a timed quiz, event-driven interruptions to a student's workflow can be suppressed:",
+            'blca' => "Blocks can potentially interrupt legitimate communication between $usertype who are also both enrolled in a different LON-CAPA $lctype.",
+            'pobl' => "Portfolio blocking can impact a student's ability to complete assignments in courses besides your own. Please use this feature wisely.",
+            'actt' => "Action to take:",
+            'addn' => 'Add new blocking event',
+            'mexb' => 'Modify existing blocking event(s)', 
+            'ncbc' => 'There are no blocking events currently saved.',
+            'stor' => 'Save',
     );
 
     my %ltext = &Apache::lonlocal::texthash(
-            'dura' => 'Duration',
+            'type' => 'Type',
+            'defs' => 'Defined Start/End',
+            'trig' => 'Triggered by Activating Timer', 
             'setb' => 'Set by',
             'even' => 'Event',
             'blck' => 'Blocked?',
-            'actn' => 'Action',
             'star' => 'Start',
-            'endd' => 'End'
+            'endd' => 'End',
+            'chda' => 'Choose dates',
+            'chtr' => 'Choose trigger',
+            'when' => 'When using defined start/end times for an event, please set dates carefully.',
+            'yes'  => 'Yes',
+            'no'   => 'No',
     );
 
     $r->print('<h3>'.$lt{'cbds'}.'</h3>');
 
+# ---------------------------------------------------- Get Time Limit parameters
+    my %intervals = &get_timed_items();
+
+# -------------------------------------------- Display information about changes 
     if ($env{'form.action'} eq 'store') {
-        &blockstore($r);
+        $r->print($storeresult);
+    } else {
+        $r->print(
+            $lt{'prev'}.
+            '<ul>'."\n".
+            '<li>'.&mt("displaying LON-CAPA messages sent by other $usertype in the $lctype").'</li>'."\n".
+            '<li>'.&mt("displaying or posting to LON-CAPA discussion boards or live chat in the $lctype").'</li>'."\n".
+            '<li>'.&mt('accessing content in LON-CAPA portfolios, blogs, or user information pages').'</li>'."\n".
+            '<li>'.&mt("generating printouts of $lctype content").'</li>'.
+            '<li>'.&mt("displaying the LON-CAPA gradebook in the $lctype").'</li>'.
+            '<li>'.&mt("searching $lctype content by keyword").'</li>'.
+            '<li>'.&mt("accessing $lctype content in specified folders or resources").'</li>'.
+            '<li>'.&mt("changing user's own password").'</li>'.
+            '</ul>'.
+            $lt{'flow'}.
+            '<ul>'."\n".
+            '<li>'.&mt("re-initialization of cached course structure, when a change has been made to $lctype content by a Coordinator").'</li>'.
+            '<li>'.&mt('display of Critical Messages when navigation arrows used to move to the adjacent resource').'</li>'.
+            '</ul>'.
+            '<p class="LC_warning">'.$lt{'blca'}.'<br />'.$lt{'pobl'}.'</p>'
+        );
     }
 
-    $r->print($lt{'desc'}.'<br /><br />
-               <form name="blockform" method="post" action="/adm/setblock?action=store">
-             ');
+# ------------------------ Choose between modifying existing block or adding new
+    $r->print('<form name="blockform" method="post" action="/adm/setblock?action=store">');
 
-    $r->print('<h4>'.$lt{'mecb'}.'</h4>');
-    my %records = ();
-    my $blockcount = 0;
-    my $parmcount = 0;
-    &get_blockdates(\%records,\$blockcount);
-    if ($blockcount > 0) {
-        $parmcount = &display_blocker_status($r,\%records,\%ltext);
+    unless ($readonly) {
+        if ($blockcount > 0) {
+            $r->print(<<"END");
+<div class="LC_left_float">
+<fieldset><legend>$lt{'actt'}</legend>
+<span class="LC_nobreak">
+<label><input type="radio" name="blockaction" value="modify" id="modifyaction" 
+onclick="toggleAddModify();" checked="checked" />$lt{'mexb'}</label>
+</span>
+<br />
+<span class="LC_nobreak">
+<label><input type="radio" name="blockaction" value="add" id="addaction" 
+onclick="toggleAddModify();" />$lt{'addn'}</label>
+</span>
+</fieldset>
+</div>
+<br clear="all" />
+<div id="showadd" style="display:none">
+END
+        } else {
+            $r->print($lt{'ncbc'}.'<br /><br />'.
+                      '<h4>'.$lt{'addn'}.'</h4>'.
+                      '<input type="hidden" name="blockaction" value="add" />');
+        }
+    }
+    my ($navmap,$errormsg) =
+        &Apache::loncourserespicker::get_navmap_object($crstype,'examblock');
+
+# --------------------------------------------- Interface for adding a new block
+    unless ($readonly) {
+        &display_addblocker_table($r,$blockcount,\%ltext,\%intervals,
+                                  $navmap,$errormsg);
+        if ($blockcount > 0) {
+            $r->print('</div>');
+        }
+    }
+# ------------------------------------------------ Interface for existing blocks
+    if (!$blockcount) {
+        if ($readonly) {
+            $r->print($lt{'ncbc'}.'<br />');
+        }
     } else {
-        $r->print($lt{'ncbc'}.'<br /><br />');
+        &display_blocker_status($r,\%records,\%ltext,\%intervals,
+                                $navmap,$errormsg,$blockcount,$readonly);
     }
-    &display_addblocker_table($r,$parmcount,\%ltext);
-    my $end_page=&Apache::loncommon::end_page();
-    $r->print(<<"END");
+    unless ($readonly) {
+        $r->print(<<"END");
 <br />
-<input type="hidden" name="blocktotal" value="$blockcount" />
 <input type ="submit" value="$lt{'stor'}" />
-</form>
-$end_page
 END
-
-    $r->print(&Apache::loncommon::end_page());
+    }
+    $r->print('</form>'.
+              &Apache::loncommon::end_page());
     return OK;
 }
 
+sub get_permission {
+    my %permission;
+    my $allowed = 0;
+    my $readonly = 0;
+    return ($readonly,$allowed) unless ($env{'request.course.id'});
+    if ((&Apache::lonnet::allowed('dcm',$env{'request.course.id'})) ||
+        (&Apache::lonnet::allowed('dcm',$env{'request.course.id'}.'/'.
+                  $env{'request.course.sec'}))) {
+        $allowed = 1;
+    } elsif ((&Apache::lonnet::allowed('vcb',$env{'request.course.id'})) ||
+             (&Apache::lonnet::allowed('vcb',$env{'request.course.id'}.'/'.
+                  $env{'request.course.sec'}))) {
+        $readonly = 1;
+        $allowed = 1;
+    }
+    return ($readonly,$allowed);
+}
+
+sub get_timed_items {
+    my ($cdom,$cnum) = @_;
+    my ($cid,%intervals);
+    if ($cdom eq '' || $cnum eq '') {
+        $cid = $env{'request.course.id'};
+        $cdom = $env{'course.'.$cid.'.domain'};
+        $cnum = $env{'course.'.$cid.'.num'};
+    } else {
+        $cid = $cdom.'_'.$cnum;
+    }
+    if ($cid eq '') {
+        return %intervals;
+    }
+    my $resourcedata=&Apache::lonparmset::readdata($cnum,$cdom);
+    if (ref($resourcedata) eq 'HASH') {
+        foreach my $key (keys(%{$resourcedata})) {
+            if ($key =~ /^\Q$cid\E(.*)\.0\.interval$/) {
+                my $middle = $1;
+                if ($middle eq '') {
+                    $intervals{'course'}{'all'} = $resourcedata->{$key};
+                } elsif ($middle =~ /^\.\[(\w+)\]$/) {
+                    $intervals{'course'}{'secgrp'}{$1} = $resourcedata->{$key};
+                } elsif ($middle =~ /^\.\[useropt\:($match_username\:$match_domain)\]$/) {
+                    $intervals{'course'}{'users'}{$1} = $resourcedata->{$key};
+                } elsif ($middle =~ /^\.(.+)\Q___(all)\E$/) {
+                    my $inner = $1;
+                    if ($inner =~ /^\[(\w+)\]\.([^\]]+)$/) {
+                        $intervals{'map'}{$2}{'secgrp'}{$1} = $resourcedata->{$key};
+                    } elsif ($inner =~ /^\[useropt\:($match_username\:$match_domain)\]\.([^\]]+)$/) {
+                        $intervals{'map'}{$2}{'users'}{$1} = $resourcedata->{$key};
+                    } else {
+                        $intervals{'map'}{$inner}{'all'} = $resourcedata->{$key};
+                    }
+                } elsif ($middle =~ /^\.\[(\w+)\]\.([^\]]+)$/) {
+                    $intervals{'resource'}{$2}{'secgrp'}{$1} = $resourcedata->{$key}; 
+                } elsif ($middle =~ /^\.\[useropt\:($match_username\:$match_domain)\]\.([^\]]+)$/) {
+                    $intervals{'resource'}{$2}{'users'}{$1} = $resourcedata->{$key};
+                } else {
+                    my ($symb) = ($middle =~ /^\.(.+)$/);
+                    $intervals{'resource'}{$symb}{'all'} = $resourcedata->{$key};
+                }
+            }
+        }
+    }
+    return %intervals;
+}
+
 sub blockstore {
-    my $r = shift;
+    my ($r,$crstype,$blockcount,$currblockrecs) = @_;
     my %lt=&Apache::lonlocal::texthash(
             'tfcm' => 'The following changes were made',
-            'ncwm' => 'No changes were made.'
+            'ncwm' => 'No changes were made.',
+            'unna' => 'Unable to retrieve contents of course.', 
     );
     my %adds = ();
     my %removals = ();
@@ -204,42 +772,171 @@ sub blockstore {
     my $modtotal = 0;
     my $canceltotal = 0;
     my $addtotal = 0;
+    my $changestotal = 0;
+    my $addtimer = 0;
     my %blocking = ();
-    $r->print('<h3>'.$lt{'head'}.'</h3>');
-    foreach my $envkey (keys(%env)) {
-        if ($envkey =~ m/^form\.modify_(\d+)$/) {
-            $adds{$1} = $1;
-            $removals{$1} = $1;
-            $modtotal ++;
-        } elsif ($envkey =~ m/^form\.cancel_(\d+)$/) {
-            $cancels{$1} = $1;
-            unless ( defined($removals{$1}) ) {
-                $removals{$1} = $1;
-                $canceltotal ++;
-            }
-        } elsif ($envkey =~ m/^form\.add_(\d+)$/) {
-            $adds{$1} = $1;
-            $addtotal ++;
+    my (%map_url,%resource_symb,%titles,$output);
+    $output = '<h3>'.$lt{'head'}.'</h3>';
+    if ($env{'form.blockaction'} eq 'modify') {
+        foreach my $envkey (keys(%env)) {
+            if ($envkey =~ m/^form\.action_(\d+)$/) {
+                if ($env{$envkey} eq 'modify') {
+                    $adds{$1} = 1;
+                    $removals{$1} = 1;
+                } elsif ($env{$envkey} eq 'cancel') {
+                    $cancels{$1} = $1;
+                    unless ( defined($removals{$1}) ) {
+                        $removals{$1} = 1;
+                        $canceltotal ++;
+                    }
+                }
+            }
         }
+    } elsif ($env{'form.blockaction'} eq 'add') {
+        $adds{$blockcount} = 1;
+    }
+    my ($navmap,$errormsg) =
+        &Apache::loncourserespicker::get_navmap_object($crstype,'examblock');
+    unless (ref($navmap)) {
+        $output = $lt{'unna'}.' '.$lt{'ncwm'}.'</br>';
+        return ($changestotal,$output);
     }
+    &Apache::loncourserespicker::enumerate_course_contents($navmap,\%map_url,\%resource_symb,\%titles,'examblock');
+    my $do_releasereq_update;
+    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+    my $chome = $env{'course.'.$env{'request.course.id'}.'.home'};
+    my $chostname = &Apache::lonnet::hostname($chome);
+    my ($chomemajor,$chomeminor) =
+        split(/\./,&Apache::lonnet::get_server_loncaparev($cdom,$chome));
+
 
     foreach my $key (keys(%removals)) {
         my $hashkey = $env{'form.key_'.$key};
-        &Apache::lonnet::del('comm_block',["$hashkey"],
-                         $env{'course.'.$env{'request.course.id'}.'.domain'},
-                         $env{'course.'.$env{'request.course.id'}.'.num'}
-                         );
+        if ($hashkey =~ /firstaccess____/) {
+           $do_releasereq_update = 1;
+        }
+        if (ref($currblockrecs->{$hashkey}) eq 'HASH') {
+            if (ref($currblockrecs->{$hashkey}->{'blocks'}) eq 'HASH') {
+                foreach my $type ('docs','printout') {
+                    if (exists($currblockrecs->{$hashkey}->{'blocks'}->{$type})) {
+                        $do_releasereq_update = 1;
+                    }
+                }
+            }
+        }
+        &Apache::lonnet::del('comm_block',["$hashkey"],$cdom,$cnum);
+    }
+    if ($do_releasereq_update) {
+        push(@{$modified_courses},[$cdom,$cnum,$chome,$crstype]);
+        unless ($registered_cleanup) {
+            my $handlers = $r->get_handlers('PerlCleanupHandler');
+            $r->set_handlers('PerlCleanupHandler' => [\&update_releasereq,@{$handlers}]);
+            $registered_cleanup=1;
+        }
     }
     foreach my $key (keys(%adds)) {
         unless ( defined($cancels{$key}) ) {
-            my ($newstart,$newend) = &get_dates_from_form($key);
-            my $newkey = $newstart.'____'.$newend;
-            my $blocktypes = &get_block_choices($key);
-            $blocking{$newkey} = {
+            my ($newkey,$status,$needsrelease);;
+            if ($env{'form.firstaccess_'.$key}) {
+                my $interval = 
+                    &HTML::Entities::decode($env{'form.firstaccess_'.$key});
+                if ($interval ne '') {
+                    if ($interval eq 'course') {
+                        $newkey = 'firstaccess____'.$interval;
+                    } elsif ($interval =~ /___\d+___/) {
+                        my ($map,$resid,$url) = 
+                            &Apache::lonnet::decode_symb($interval);
+                        if (&Apache::lonnet::is_on_map($url)) {
+                            $newkey = 'firstaccess____'.$interval;
+                        }
+                    } elsif (&Apache::lonnet::is_on_map($interval)) {
+                        $newkey = 'firstaccess____'.$interval;
+                    }
+                    if ($newkey ne '') {
+                        unless (defined($removals{$key})) {
+                            ($status,$needsrelease) = &check_release_required('timer',$chomemajor,$chomeminor);
+                            if ($status eq 'fail') {
+                                $newkey = '';
+                                $output .= '<p class="LC_warning">'.
+                                           &mt('Triggering of blocking events not allowed for [_1]',
+                                               &escape($env{'form.title_'.$key})).'<br />';
+                            }
+                        }
+                    }
+                }
+            } else {
+                my ($newstart,$newend) = &get_dates_from_form($key);
+                $newkey = $newstart.'____'.$newend;
+            }
+            if ($status eq 'fail') {
+                $output .=  &mt('LON-CAPA version ([_1]) installed on home server ([_2]) does not meet version requirements ([_3] or newer).',
+                                $chomemajor.'.'.$chomeminor,$chostname,$needsrelease).'</p>';
+            }
+            if ($newkey ne '') {
+                my ($blocktypes,$blockdocs) = 
+                    &get_block_choices($key,\%map_url,\%resource_symb);
+                if (ref($blocktypes) eq 'HASH') {
+                    if ($blocktypes->{'printout'} eq 'on') {
+                        ($status,$needsrelease) = &check_release_required('printout',$chomemajor,$chomeminor);
+                        if ($status eq 'fail') {
+                            $blocktypes->{'printout'} = 'off';
+                            $output .= '<p class="LC_warning">'.
+                                       &mt('Printout blocking not allowed for [_1]',
+                                           &escape($env{'form.title_'.$key})).'<br />';
+                        }
+                    }
+                    if ($blocktypes->{'alert'} eq 'on') {
+                        ($status,$needsrelease) = &check_release_required('alert',$chomemajor,$chomeminor);
+                        if ($status eq 'fail') {
+                            $blocktypes->{'alert'} = 'off';
+                            $output .= '<p class="LC_warning">'.
+                                       &mt('Message Alert blocking not allowed for [_1]',
+                                           &escape($env{'form.title_'.$key})).'<br />';
+                        }
+                    }
+                    if ($blocktypes->{'reinit'} eq 'on') {
+                        ($status,$needsrelease) = &check_release_required('reinit',$chomemajor,$chomeminor);
+                        if ($status eq 'fail') {
+                            $blocktypes->{'reinit'} = 'off';
+                            $output .= '<p class="LC_warning">'.
+                                       &mt('Course Re-initialization blocking not allowed for [_1]',
+                                           &escape($env{'form.title_'.$key})).'<br />';
+                        }
+                    }
+                }
+                if ($blockdocs) {
+                    ($status,$needsrelease) = &check_release_required('docs',$chomemajor,$chomeminor);
+                    if ($status eq 'fail') {
+                        delete($blocktypes->{'docs'});
+                        $output .= '<p class="LC_warning">'.
+                                   &mt('Content blocking not allowed for [_1]',
+                                       &escape($env{'form.title_'.$key})).'<br />';
+                    }
+                }
+                $blocking{$newkey} = {
                           setter => $env{'user.name'}.':'.$env{'user.domain'},
                           event  => &escape($env{'form.title_'.$key}),
                           blocks => $blocktypes,
                         };
+                if (exists($removals{$key})) {
+                    $modtotal ++;
+                } else {
+                    $addtotal ++;
+                }
+            } else {
+                if ($env{'form.toggle_'.$key} eq 'timer') {
+                    unless ($status eq 'fail') {
+                        $output .= '<p class="LC_warning">'.
+                                   &mt('Invalid trigger for new blocking event').
+                                   '</p>';
+                    }
+                } else {
+                    $output .= '<p class="LC_warning">'.
+                               &mt('No date range found for new blocking event').
+                               '</p>';
+                }
+            }
         }
     }
     if ($addtotal + $modtotal > 0) {
@@ -248,24 +945,52 @@ sub blockstore {
                      $env{'course.'.$env{'request.course.id'}.'.num'}
                      );
     }
-    my $chgestotal = $canceltotal + $modtotal + $addtotal;
-    if ($chgestotal > 0) {
-        $r->print($lt{'tfcm'}.'<ul>');
+    $changestotal = $canceltotal + $modtotal + $addtotal;
+    if ($changestotal > 0) {
+        &Apache::lonnet::devalidate_cache_new('comm_block',
+                                              $env{'request.course.id'});
+        $output .= $lt{'tfcm'}.'<ul>';
         if ($canceltotal > 0) {
-            $r->print('<li>'.&mt('[quant,_1,communication blocking period was,communication blocking periods were] removed.',$canceltotal).'</li>');
+            $output .= '<li>'.
+                       &mt('[quant,_1,blocking event was,blocking events were] removed.',
+                           $canceltotal).
+                       '</li>';
         }
         if ($modtotal > 0) {
-            $r->print('<li>'.&mt('[quant,_1,communication blocking period was,communication blocking periods were] modified.',$modtotal).'</li>');
+            $output .= '<li>'.
+                       &mt('[quant,_1,blocking event was,blocking events were] modified.',
+                           $modtotal).
+                       '</li>';
         }
         if ($addtotal > 0) {
-            $r->print('<li>'.&mt('[quant,_1,communication blocking period was,communication blocking periods were] added.',$addtotal).'</li>');
+            $output .= '<li>'.
+                       &mt('[quant,_1,blocking event was,blocking events were] added.',
+                           $addtotal).
+                       '</li>';
         }
-        $r->print('</ul>');
+        $output .= '</ul>';
     } else {
-        $r->print($lt{'ncwm'});
+        $output .= $lt{'ncwm'};
     }
-    $r->print('<br />');
-    return;
+    $output .= '<br />';
+    return ($changestotal,$output);
+}
+
+sub update_releasereq {
+    my $readmap = 1;
+    my $getrelreq = 1;
+    if (ref($modified_courses) eq 'ARRAY') {
+        foreach my $item (@{$modified_courses}) {
+            if (ref($item) eq 'ARRAY') {
+                my ($cdom,$cnum,$chome,$crstype) = @{$item};
+                &Apache::lonrelrequtils::modify_course_relreq(undef,undef,$cnum,$cdom,
+                                                              $chome,$crstype,$cdom.'_'.$cnum,
+                                                              $readmap,$getrelreq);
+            }
+        }
+        $modified_courses = [];
+    }
+    return OK;
 }
 
 sub get_dates_from_form {
@@ -276,145 +1001,649 @@ sub get_dates_from_form {
 }
 
 sub get_blockdates {
-    my ($records,$blockcount) = @_;
-    $$blockcount = 0;
+    my ($records) = @_;
+    my $blockcount = 0;
     %{$records} = &Apache::lonnet::dump('comm_block',
                          $env{'course.'.$env{'request.course.id'}.'.domain'},
                          $env{'course.'.$env{'request.course.id'}.'.num'}
                          );
-    $$blockcount = keys(%{$records});
+    $blockcount = keys(%{$records});
 
     if ((keys(%{$records}))[0] =~ /^error: 2 /) {
-        $$blockcount = 0;
+        $blockcount = 0;
     }
+    return $blockcount;
 }
 
 sub get_block_choices {
-    my $item = shift;
+    my ($item,$map_ref,$symb_ref) = @_;
     my $blocklist;
+    my $blockdocs;
     my ($typeorder,$types) = &blocktype_text();
     foreach my $type (@{$typeorder}) {
-        if ($env{'form.'.$type.'_'.$item}) {
-            $blocklist->{$type} = 'on';
+        if ($type eq 'docs') {
+            if ($env{'form.'.$type.'_'.$item}) {
+                $blocklist->{$type} = {};
+                if ($env{'form.docs_resources_'.$item}) {
+                    $env{'form.docs_resources_'.$item} =~ s/,$//;
+                    if (ref($symb_ref) eq 'HASH') {
+                        my %resources = map { $symb_ref->{$_} => 1; } 
+                                            (split(/,/,$env{'form.docs_resources_'.$item}));
+                        if (exists($resources{''})) {
+                            delete($resources{''});
+                        }
+                        $blocklist->{$type}->{resources} = \%resources;
+                        if (keys(%resources) > 0) {
+                            $blockdocs = 1;
+                        }
+                    }
+                }
+                if ($env{'form.docs_maps_'.$item}) {
+                    $env{'form.docs_maps_'.$item} =~ s/,$//;
+                    if (ref($map_ref) eq 'HASH') {
+                        my %maps = map { $map_ref->{$_} => 1; }                             
+                                       (split(/,/,$env{'form.docs_maps_'.$item}));
+                        if (exists($maps{''})) {
+                            delete($maps{''});
+                        }
+                        $blocklist->{$type}->{maps} = \%maps;
+                        if (keys(%maps) > 0) {
+                            $blockdocs = 1;
+                        }
+                    }
+                }
+            }
+        } else {
+            if ($env{'form.'.$type.'_'.$item}) {
+                $blocklist->{$type} = 'on';
+            } else {
+                $blocklist->{$type} = 'off';
+            }
+        }
+    }
+    return ($blocklist,$blockdocs);
+}
+
+sub check_release_required {
+    my ($value,$chomemajor,$chomeminor) = @_; 
+    my $needsrelease = $Apache::lonnet::needsrelease{'course:commblock:'.$value};
+    if ($needsrelease) {
+        my ($needsmajor,$needsminor) = split(/\./,$needsrelease);
+        if (($chomemajor < $needsmajor) || 
+            (($chomemajor == $needsmajor) && ($chomeminor < $needsminor))) {
+            return ('fail',$needsrelease);
+        }
+        my $curr_required =
+            $env{'course.'.$env{'request.course.id'}.'.internal.releaserequired'};
+        if ($curr_required eq '') {
+            &Apache::lonnet::update_released_required($needsrelease);
         } else {
-            $blocklist->{$type} = 'off';
+            my ($currmajor,$currminor) = split(/\./,$curr_required);
+            my ($needsmajor,$needsminor) = split(/\./,$needsrelease);
+            if (($currmajor < $needsmajor) || 
+                ($currmajor == $needsmajor && $currminor < $needsminor)) {
+                &Apache::lonnet::update_released_required($needsrelease);
+            }
         }
     }
-    return $blocklist;
+    return ('ok',$needsrelease);
 }
 
 sub display_blocker_status {
-    my ($r,$records,$ltext) = @_;
+    my ($r,$records,$ltext,$intervals,$navmap,$errormsg,$blockcount,$readonly) = @_;
     my $parmcount = 0;
- 
+    my (%map_url,%resource_symb,%titles,%lookups,$disabled);
+    &Apache::loncourserespicker::enumerate_course_contents($navmap,\%map_url,\%resource_symb,\%titles,'examblock');
+    %{$lookups{'maps'}} = reverse(%map_url);
+    %{$lookups{'resources'}} = reverse(%resource_symb);
     my %lt = &Apache::lonlocal::texthash(
         'modi' => 'Modify',
-        'canc' => 'Cancel',
+        'dele' => 'Delete',
+        'noch' => 'No change',
     );
-    my ($typeorder,$types) = &blocktype_text();
-    $r->print(&Apache::loncommon::start_data_table());
+    $r->print('<div id="showmodify" style="display:block">'.
+              &Apache::loncommon::start_data_table().'<tr>');
+    if ($readonly) {
+        $disabled = ' disabled="disabled"';
+    } else {
+        $r->print('<th></th>');
+    }
     $r->print(<<"END");
-  <tr>
-    <th>$ltext->{'dura'}</th>
-    <th>$ltext->{'setb'}</th>
+    <th>$ltext->{'type'}</th>
     <th>$ltext->{'even'}</th>
     <th>$ltext->{'blck'}</th>
-    <th>$ltext->{'actn'}</th>
   </tr>
 END
     foreach my $record (sort(keys(%{$records}))) {
-        my $onchange = 'onFocus="javascript:window.document.forms['.
-                       "'blockform'].elements['modify_".$parmcount."'].".
-                       'checked=true;"';
-        my ($start,$end) = split(/____/,$record);
-        my $startform = &Apache::lonhtmlcommon::date_setter('blockform','startdate_'.$parmcount,$start,$onchange);
-        my $endform = &Apache::lonhtmlcommon::date_setter('blockform','enddate_'.$parmcount,$end,$onchange);
-
+        my $jschg = 
+            'javascript:window.document.forms['. "'blockform'".']'.
+            '.elements['."'action_$parmcount'".'][0].checked=true;';
+        my $onchange = 'onfocus="'.$jschg.'"';
         my ($setuname,$setudom,$title,$blocks) =
             &Apache::loncommon::parse_block_record($$records{$record});
         $title = &HTML::Entities::encode($title,'"<>&');
+        my $blockid = &HTML::Entities::encode($record,'"<>&');
         my $settername =
            &Apache::loncommon::aboutmewrapper(
                            &Apache::loncommon::plainname($setuname,$setudom),
                            $setuname,$setudom);
+        my $state = '';
         $r->print(&Apache::loncommon::start_data_table_row());
-        $r->print(<<"END");
-        <td>$ltext->{'star'}:&nbsp;$startform<br />$ltext->{'endd'}:&nbsp;&nbsp;$endform</td>
-        <td>$settername</td>
-        <td><input type="text" name="title_$parmcount" size="15" value="$title" /><input type="hidden" name="key_$parmcount" value="$record" /></td>
-        <td>
-END
-        foreach my $block (@{$typeorder}) {
-            my $blockstatus = '';
-            if ($blocks->{$block} eq 'on') {
-                $blockstatus = 'checked="checked"';
+        if ($readonly) {
+            $state = 'disabled';
+        } else {
+            $r->print(<<"ACT");
+
+        <td valign="middle"><span class="LC_nobreak"><label>
+        <input type="radio" name="action_$parmcount" value="modify" />$lt{'modi'}
+        </label></span><br />
+        <span class="LC_nobreak"><label>
+        <input type="radio" name="action_$parmcount" value="cancel" />$lt{'dele'}
+        </label></span><br />
+        <span class="LC_nobreak"><label>
+        <input type="radio" name="action_$parmcount" id="nochange_$parmcount" 
+         value="nochange" checked="checked" />$lt{'noch'}
+        </label></span>
+        </td>
+ACT
+        }
+        my ($start,$end,$startform,$endform); 
+        if ($record =~ /^(\d+)____(\d+)$/) {
+            ($start,$end) = split(/____/,$record);
+            $startform = &Apache::lonhtmlcommon::date_setter('blockform','startdate_'.
+                                                             $parmcount,$start,$onchange,
+                                                             undef,$state);
+            $endform = &Apache::lonhtmlcommon::date_setter('blockform','enddate_'.
+                                                           $parmcount,$end,$onchange,
+                                                           undef,$state);
+            $r->print('<td><fieldset><legend>'.$ltext->{'defs'}.'</legend>'.
+                      $ltext->{'star'}.':&nbsp;'.$startform.'<br />'.
+                      $ltext->{'endd'}.':&nbsp;&nbsp;'.$endform.'</fieldset></td>');
+        } elsif ($record =~ /^firstaccess____(.+)$/) {
+            my $item = $1;
+            my $type = 'map';
+            my $url;
+            if ($item eq 'course') {
+                $type = 'course';
+            } elsif ($item =~ /___\d+___/) {
+                $type = 'resource';
+                (my $map, my $resid, $url) = &Apache::lonnet::decode_symb($item);  
+            } else {
+                $url = $item;
             }
-            $r->print('<span class="LC_nobreak"><label><input type="checkbox" name="'.$block.'_'.$parmcount.'" '.$blockstatus.' value="1" />'.$types->{$block}.'</label></span><br />');
+            $r->print('<td><fieldset><legend>'.$ltext->{'trig'}.'</legend>');
+            my ($itemname,$iteminfo) = &interval_details($item,$type,$url,$navmap,$intervals,$parmcount);
+            $r->print(&create_interval_form($intervals,$parmcount,$navmap,'blocking',$item,$jschg,
+                                            $itemname,$iteminfo,$disabled).'</fieldset></td>');
         }
         $r->print(<<"END");
-        </td> 
-        <td><span class="LC_nobreak"><label>
-        <input type="checkbox" name="modify_$parmcount" />$lt{'modi'}
-        </label></span><br /><span class="LC_nobreak">
-        <label>
-        <input type="checkbox" name="cancel_$parmcount" />$lt{'canc'}
-        </label></span>
+        <td>
+         <input type="text" name="title_$parmcount" size="15" value="$title" onfocus="$jschg" $disabled />
+         <input type="hidden" name="key_$parmcount" value="$blockid" />
+         <br />
+         <br />
+         $ltext->{'setb'}: $settername
+        </td>
 END
-        $r->print(&Apache::loncommon::end_data_table_row());
+        $r->print('<td>'.&blocker_checkboxes($parmcount,$blocks,$jschg,\%lookups,$disabled).'</td>'.
+                  &Apache::loncommon::end_data_table_row());
         $parmcount++;
     }
     $r->print(<<"END");
 </table>
-<br />
-<br />
+</div>
 END
-    return $parmcount;
+    return;
+}
+
+sub convlim {
+    my ($timelimit) = @_;
+    my @order = ('days','hours','minutes','seconds');
+    my %catlimits = ( 
+                      days    => 86400,
+                      hours   => 3600,
+                      minutes => 60,
+                    );
+    my @toshow;
+    foreach my $cat (@order) {
+        if ($cat eq 'seconds') {
+            if ($timelimit > 0) {
+                push(@toshow,&mt("[_1] $cat",$timelimit));
+            }
+        } elsif ($timelimit >= $catlimits{$cat}) {
+            my $val = int($timelimit/$catlimits{$cat});
+            if ($val > 0) {
+                push(@toshow,&mt("[_1] $cat",$val));
+            }
+            $timelimit -= $val*$catlimits{$cat};
+        }
+    }
+    my $output = join(', ',@toshow);
+    return $output;
 }
 
 sub display_addblocker_table {
-    my ($r,$parmcount,$ltext) = @_;
+    my ($r,$parmcount,$ltext,$intervals,$navmap,$errormsg) = @_;
+    return unless ((ref($ltext) eq 'HASH') && (ref($intervals) eq 'HASH'));
     my $start = time;
     my $end = $start + (60 * 60 * 2); #Default is an exam of 2 hours duration.
-    my $onchange = 'onFocus="javascript:window.document.forms['.
-                   "'blockform'].elements['add_".$parmcount."'].".
+    my $onchange = 'onfocus="javascript:window.document.forms['.
+                   "'blockform'].elements['addaction'].".
                    'checked=true;"';
-    my $startform = &Apache::lonhtmlcommon::date_setter('blockform','startdate_'.$parmcount,$start,$onchange);
-    my $endform = &Apache::lonhtmlcommon::date_setter('blockform','enddate_'.$parmcount,$end,$onchange);
+    my $startform = &Apache::lonhtmlcommon::date_setter('blockform','startdate_'.
+                                                        $parmcount,$start,$onchange);
+    my $endform = &Apache::lonhtmlcommon::date_setter('blockform','enddate_'.
+                                                      $parmcount,$end,$onchange);
     my %lt = &Apache::lonlocal::texthash(
-        'addb' => 'Add block',
         'exam' => 'e.g., Exam 1',
-        'addn' => 'Add new communication blocking periods'
     );
-    my ($typeorder,$types) = &blocktype_text();
-    $r->print(<<"END");
-<h4>$lt{'addn'}</h4>
-END
+    my $intervalform = &create_interval_form($intervals,$parmcount,$navmap,'blocking');
+    if ($intervalform ne '') {
+        $intervalform = '<fieldset>'.
+                        '<legend>'.$ltext->{'chtr'}.'</legend>'.
+                        $intervalform.
+                        '</fieldset>';
+    }
     $r->print(&Apache::loncommon::start_data_table());
     $r->print(<<"END");
    <tr>
-     <th>$ltext->{'dura'}</th>
+     <th>$ltext->{'type'}</th>
      <th>$ltext->{'even'} $lt{'exam'}</th>
      <th>$ltext->{'blck'}</th>
-     <th>$ltext->{'actn'}</th>
    </tr>
 END
-    $r->print(&Apache::loncommon::start_data_table_row());
+    $r->print(&Apache::loncommon::start_data_table_row().'<td>');
     $r->print(<<"END");
-     <td>$ltext->{'star'}:&nbsp;$startform<br />$ltext->{'endd'}:&nbsp;&nbsp;$endform</td>
-     <td><input type="text" name="title_$parmcount" size="15" value="" /></td>
-     <td>
-END
-    foreach my $block (@{$typeorder}) {
-        $r->print('<span class="LC_nobreak"><label><input type="checkbox" name="'.$block.'_'.$parmcount.'" value="1" />'.$types->{$block}.'</label></span><br />');
-     }
-     $r->print(<<"END");
+     <span class="LC_nobreak"><label><input type="radio" id="toggle_startstop" 
+     name="toggle_$parmcount" value="startstop" onclick="showBlockType();" checked="checked" />
+     $ltext->{'defs'}</label></span>&nbsp;&nbsp; 
+     <span class="LC_nobreak"><label><input type="radio" id="toggle_timer" name="toggle_$parmcount" 
+     value="timer" onclick="showBlockType();" />$ltext->{'trig'}</label></span><br />
+     <div id="show_startstop" style="display:block">
+     <fieldset><legend>$ltext->{'chda'}</legend>
+     $ltext->{'star'}:&nbsp;$startform<br />$ltext->{'endd'}:&nbsp;&nbsp;$endform</fieldset>
+     <span class="LC_warning">$ltext->{'when'}</span></div>
+     <div id="show_timer" style="display:none">$intervalform</div>
      </td>
-     <td><span class="LC_nobreak"><label>
-     <input type="checkbox" name="add_$parmcount" value="1" />$lt{'addb'}
-     </label></span></td>
+     <td><input type="text" name="title_$parmcount" size="15" value="" /></td>
 END
-    $r->print(&Apache::loncommon::end_data_table_row());
-    $r->print(&Apache::loncommon::end_data_table());
+    $r->print('<td>'.&blocker_checkboxes($parmcount).'</td>'.
+              &Apache::loncommon::end_data_table_row().
+              &Apache::loncommon::end_data_table()."\n".
+              '<br />');
+    return;
+}
+
+sub blocker_checkboxes {
+    my ($parmcount,$blocks,$jschg,$lookups,$disabled) = @_;
+    my ($typeorder,$types) = &blocktype_text();
+    my $numinrow = 2;
+    my %currdocs;
+    my $output = '<table>';
+    for (my $i=0; $i<@{$typeorder}; $i++) {
+        my $block = $typeorder->[$i];
+        my ($clickaction,$blockstatus);
+        if ($jschg) {
+            $clickaction = $jschg;
+        } 
+        if ($block eq 'docs') {
+            if ((ref($blocks) eq 'HASH') && (ref($lookups) eq 'HASH')) {
+                if (ref($blocks->{$block}) eq 'HASH') {
+                    if (keys(%{$blocks->{$block}}) > 0) {
+                        $blockstatus = 'checked="checked"';
+                        foreach my $key (sort(keys(%{$blocks->{$block}}))) {
+                            if (ref($blocks->{$block}{$key}) eq 'HASH') {
+                                my @current = ();
+                                foreach my $item (keys(%{$blocks->{$block}{$key}})) {
+                                    if ($lookups->{$key}{$item}) {
+                                        push(@current,$lookups->{$key}{$item});
+                                    }
+                                }
+                                if (@current > 0) {
+                                    @current=sort { $a <=> $b } (@current);
+                                    $currdocs{$key} = join(',',@current);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            $clickaction .= 'javascript:resblockinfo('."'$parmcount'".');';
+        } else {
+            if (ref($blocks) eq 'HASH') { 
+                if ($blocks->{$block} eq 'on') {
+                    $blockstatus = 'checked="checked"';
+                }
+            }
+        }
+        my $rem = $i%($numinrow);
+        if ($rem == 0) {
+            if ($i > 0) {
+                $output .= '</tr>';
+            }
+            $output .= '<tr>';
+        }
+        if ($i == scalar(@{$typeorder})-1) {
+            my $colsleft = $numinrow-$rem;
+            if ($colsleft > 1) {
+                $output .= '<td colspan="'.$colsleft.'">';
+            } else {
+                $output .= '<td>';
+            }
+        } else {
+            $output .= '<td>';
+        }
+        my $item = $block.'_'.$parmcount;
+        if ($clickaction) {
+            $clickaction = ' onclick="'.$clickaction.'"';
+        }
+        if ($blockstatus) {
+            $blockstatus = ' '.$blockstatus;
+        } 
+        $output .= '<span class="LC_nobreak"><label>'."\n".
+                   '<input type="checkbox" id="'.$item.'" name="'.$item.'"'.
+                   $blockstatus.$clickaction.' value="1"'.$disabled.' />'.
+                   $types->{$block}.'</label></span>'."\n";
+        if ($block eq 'docs') {
+            if ($blockstatus ne '') {
+                $output .= '&nbsp;<a href="javascript:resblockinfo('."'$parmcount'".')">'.
+                            &mt('Details').'</a>';
+            }
+        }
+        $output .= '<br /></td>';
+    }
+    $output .= '</tr></table>'.
+               '<input type="hidden" name="docs_maps_'.$parmcount.'"'.
+               ' id="docs_maps_'.$parmcount.'" value="'.$currdocs{'maps'}.'" />'.
+               '<input type="hidden" name="docs_resources_'.$parmcount.'"'.
+               ' id="docs_resources_'.$parmcount.'" value="'.$currdocs{'resources'}.'" />';
+    return $output;
+}
+
+sub create_interval_form {
+    my ($intervals,$parmcount,$navmap,$context,$currkey,$jschg,$itemname,$iteminfo,$disabled) = @_;
+    return unless ((ref($intervals) eq 'HASH') && (ref($navmap)));
+    my $intervalform;
+    my $counter = 0;
+    if (keys(%{$intervals}) > 0) {
+        foreach my $type (sort(keys(%{$intervals}))) {
+            if ($type eq 'course') {
+                my ($checked,$clickaction);
+                if ($currkey eq 'course') {
+                    $checked = ' checked="checked"';
+                } elsif ($jschg) {
+                    $clickaction = ' onclick="'.$jschg.'"';
+                }
+                $intervalform .= '<label><input type="radio" name="firstaccess_'.$parmcount.
+                                 '" value="course"'.$checked.$clickaction.$disabled.' />';
+                if ($currkey eq 'course') {
+                    $intervalform .= $itemname;
+                } else {
+                    $intervalform .= &mt('Timer for all items in course');
+                }
+                $intervalform .= '</label>';
+                if ($currkey eq 'course') {
+                    $intervalform .= $iteminfo;
+                } elsif ($context eq 'accesstimes') {
+                    (undef,$iteminfo) = &interval_details('course',$type,'',$navmap,$intervals,$counter);
+                    if ($iteminfo) {
+                        $intervalform .= ' '.$iteminfo;
+                    }
+                }
+                $intervalform .= '<br />';
+                $counter ++;
+            } elsif ($type eq 'map') {
+                if (ref($intervals->{$type}) eq 'HASH') {
+                    if (ref($navmap)) {
+                        foreach my $map (sort(keys(%{$intervals->{$type}}))) {
+                            next if ((!&Apache::lonnet::is_on_map($map)) &&
+                                     ($currkey ne $map));
+                            my ($checked,$clickaction);
+                            if ($currkey eq $map) {
+                                $checked = ' checked="checked"';
+                            } elsif ($jschg) {
+                                $clickaction = ' onclick="'.$jschg.'"';
+                            }
+                            $intervalform .= '<label><input type="radio" name="firstaccess_'.$parmcount.
+                                             '" value="'.&HTML::Entities::encode($map,'"<>&').'"'.
+                                             $checked.$clickaction.$disabled.' />';
+                            if ($currkey eq $map) {
+                                $intervalform .= $itemname.'</label>'.$iteminfo;
+                            } else {
+                                my ($resobj,$title,$path,$hierarchy);
+                                $resobj = $navmap->getResourceByUrl($map);
+                                if (ref($resobj)) {
+                                    $title = $resobj->compTitle();
+                                } else {
+                                    $title = &Apache::lonnet::gettitle($map);
+                                }
+                                $hierarchy = &show_timer_path($type,$map,$navmap);
+                                if ($hierarchy) {
+                                    $path = ' <span style="font-size:90%;">'.
+                                            &mt('(in: [_1])',$hierarchy).
+                                            '</span>';
+                                }
+                                $intervalform .= &mt('Timer for all items in folder: [_1]',
+                                                     '<i>'.$title.'</i>').
+                                                 '</label>'.$path;
+                                if ($context eq 'accesstimes') {
+                                    (undef,$iteminfo) = &interval_details($map,$type,$map,$navmap,$intervals,$counter);
+                                    if ($iteminfo) {
+                                        $intervalform .= ' '.$iteminfo;
+                                    }
+                                }
+                            }
+                            $intervalform .= '<br />';
+                            $counter ++;
+                        }
+                    }
+                }
+            } elsif ($type eq 'resource') {
+                if (ref($intervals->{$type}) eq 'HASH') {
+                    if (ref($navmap)) {
+                        foreach my $resource (sort(keys(%{$intervals->{$type}}))) {
+                            my ($checked,$clickaction,$resobj);
+                            if ($currkey eq $resource) {
+                                $checked = ' checked="checked"';
+                            } else {
+                                $resobj = $navmap->getBySymb($resource);
+                                next unless(ref($resobj));
+                                if ($jschg) {
+                                    $clickaction = ' onclick="'.$jschg.'"';
+                                }
+                            }
+                            $intervalform .= '<label><input type="radio" name="firstaccess_'.$parmcount.
+                                             '" value="'.&HTML::Entities::encode($resource,'"<>&').'"'.
+                                             $checked.$clickaction.$disabled.' />';
+                            if ($currkey eq $resource) {
+                                $intervalform .= $itemname.'</label>'.$iteminfo;
+                            } else {
+                                my ($title,$path,$hierarchy);
+                                if (ref($resobj)) {
+                                    $title = $resobj->compTitle();
+                                }
+                                if ($title eq '') {
+                                    $title = &Apache::lonnet::gettitle($resource);
+                                }
+                                $hierarchy = &show_timer_path($type,$resource,$navmap);
+                                if ($hierarchy) {
+                                    $path = ' <span style="font-size:90%;">'.
+                                            &mt('(in: [_1])',$hierarchy).
+                                            '</span>';
+                                }
+                                $intervalform .= &mt('Timer for resource: [_1]','<i>'.$title.'</i>').
+                                                 '</label>'.
+                                                 $path;
+                                if ($context eq 'accesstimes') {
+                                    if (ref($resobj)) {
+                                        my $url = $resobj->src();
+                                        if ($url eq '') {
+                                            (my $map, my $resid, $url) = &Apache::lonnet::decode_symb($resource);
+                                        }
+                                        ($itemname,$iteminfo) = &interval_details($resource,$type,$url,$navmap,$intervals,$counter);
+                                        $intervalform .= ' '.$iteminfo;
+                                    }
+                                }
+                            }
+                            $intervalform .= '<br />';
+                            $counter ++;
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        if ($currkey ne '') {
+            $intervalform = '<input type="radio" name="firstaccess_'.$parmcount.
+                            '" checked="checked" value="'.
+                            &HTML::Entities::encode($currkey,'"<>&').'"'.$disabled.' />'.
+                            $itemname.'<br />';
+        } else {
+            $intervalform = &mt('No timed items defined.').' '.
+                            &mt('Use [_1]Settings[_2] to assign a timer, then return here.',
+                                '<a href="/adm/parmset">','</a>');
+        }
+    }
+    return $intervalform;
+}
+
+sub trigger_details_toggle {
+    my ($parmcount) = @_;
+    return ' <span id="toggletext_'.$parmcount.'" class="LC_cusr_subheading LC_nobreak">'.
+           '<a href="javascript:showTriggerDetails('."'$parmcount'".');" '.
+           'style="text-decoration: none;"><b>'.&mt('(More ...)').'</b></a></span>';
+}
+
+sub interval_details {
+    my ($item,$type,$url,$navmap,$intervals,$parmcount) = @_;
+    my ($itemname,$iteminfo,$skipdetails);
+    if ($type eq 'course') {
+        $itemname = &mt('Timer for all items in course.');
+    } else {
+        if (&Apache::lonnet::is_on_map($url)) {
+            if ($type eq 'map') {
+                if (ref($navmap)) {
+                    my $title;
+                    my $resobj = $navmap->getResourceByUrl($item);
+                    if (ref($resobj)) {
+                        $title = $resobj->compTitle();
+                    } else {
+                        $title = &Apache::lonnet::gettitle($item);
+                    }
+                    $itemname = &mt('Timer for all items in folder: [_1]',
+                                    '<span style="font-style:italic">'.
+                                    $title.'</span>');
+                }
+            } else {
+                if (ref($navmap)) {
+                    my $title;
+                    my $resobj = $navmap->getBySymb($item);
+                    if (ref($resobj)) {
+                        $title = $resobj->compTitle();
+                    } else {
+                        $title = &Apache::lonnet::gettitle($item);
+                    }
+                    $itemname = &mt('Timer for resource: [_1]',
+                                    '<span style="font-style:italic">'.
+                                    $title.'</span>');
+                }
+            }
+            if (ref($navmap)) {
+                my $path = &show_timer_path($type,$item);
+                if ($path) {
+                   $iteminfo  = ' <span style="font-size:90%;">'.
+                                  &mt('(in: [_1])',$path).
+                                  '</span>';
+                }
+            }
+        } else {
+            $skipdetails = 1;
+            $itemname = '<span style="LC_warning">'.
+                        &mt('Timer folder/resource not in course').
+                        '</span>';
+        }
+    }
+    if ((!$skipdetails) && (ref($intervals) eq 'HASH') && (ref($intervals->{$type}) eq 'HASH')) {
+        $iteminfo = &trigger_details_toggle($parmcount).
+                    '<ul id="trigdetails_'.$parmcount.'" style="display:none">';
+        if ($type eq 'course') {
+            foreach my $scope (keys(%{$intervals->{$type}})) {
+                if ($scope eq 'all') {
+                    $iteminfo .= '<li>'.&mt('All users -- time limit: [_1]',
+                                 &convlim($intervals->{$type}->{$scope})).'</li>';
+                } elsif ($scope eq 'secgrp') {
+                    if (ref($intervals->{$type}->{$scope}) eq 'HASH') {
+                        $iteminfo .= '<li>'.&mt('Sections/groups').'<ul>';
+                        foreach my $item (sort(keys(%{$intervals->{$type}->{$scope}}))) {
+                            $iteminfo .= '<li>'.&mt('[_1] -- time limit: [_2]',$item,
+                                         &convlim($intervals->{$type}->{$scope}->{$item})).
+                                         '</li>';
+                        }
+                        $iteminfo .= '</ul></li>';
+                    }
+                } elsif ($scope eq 'users') {
+                    if (ref($intervals->{$type}->{$scope}) eq 'HASH') {
+                        $iteminfo .= '<li>'.&mt('Users').'<ul>';
+                        foreach my $item (sort(keys(%{$intervals->{$type}->{$scope}}))) {
+                            $iteminfo .= '<li>'.&mt('[_1] -- time limit: [_2]',
+                                         &convlim($item,$intervals->{$type}->{$scope}->{$item})).
+                                         '</li>';
+                        }
+                        $iteminfo .= '</ul></li>';
+                    }
+                }
+            }
+        } elsif (($type eq 'map') || ($type eq 'resource')) {
+            if (ref($intervals->{$type}->{$item}) eq 'HASH') {
+                foreach my $scope (keys(%{$intervals->{$type}->{$item}})) {
+                    if ($scope eq 'all') {
+                        $iteminfo .= '<li>'.&mt('All users -- time limit: [_1]',
+                                     &convlim($intervals->{$type}->{$item}->{$scope})).
+                                     '</li>';
+                    } elsif ($scope eq 'secgrp') {
+                        if (ref($intervals->{$type}->{$item}->{$scope}) eq 'HASH') {
+                            $iteminfo .= '<li>'.&mt('Sections/groups').'<ul>';
+                            foreach my $sec (sort(keys(%{$intervals->{$type}->{$item}->{$scope}}))) {
+                                $iteminfo .= '<li>'.&mt('[_1] -- time limit: [_2]',$sec,
+                                             &convlim($intervals->{$type}->{$item}->{$scope}->{$sec})).
+                                            '</li>';
+                            }
+                            $iteminfo .= '</ul></li>';
+                        }
+                    } elsif ($scope eq 'users') {
+                        if (ref($intervals->{$type}->{$item}->{$scope}) eq 'HASH') {
+                            $iteminfo .= '<li>'.&mt('Users').'<ul>';
+                            foreach my $user (sort(keys(%{$intervals->{$type}->{$item}->{$scope}}))) {
+                                $iteminfo .= '<li>'.&mt('[_1] -- time limit: [_2]',$user,
+                                             &convlim($intervals->{$type}->{$item}->{$scope}->{$user})).
+                                             '</li>';
+                            }
+                            $iteminfo .= '</ul></li>';
+                        }
+                    }
+                }
+            }
+        }
+        $iteminfo .= '</ul>';
+    }
+    return ($itemname,$iteminfo);
+}
+
+sub show_timer_path {
+    my ($type,$item,$navmap) = @_;
+    return unless(ref($navmap));
+    my @pathitems;
+    if ($type eq 'map') {
+        @pathitems = 
+            &Apache::loncommon::get_folder_hierarchy($navmap,$item);
+    } elsif ($type eq 'resource') {
+        my ($map,$id,$resource) = &Apache::lonnet::decode_symb($item);
+        @pathitems = 
+            &Apache::loncommon::get_folder_hierarchy($navmap,$map,1);
+    }
+    if (@pathitems) {
+        return join(' &raquo; ',@pathitems);
+    }
     return;
 }
 
@@ -426,11 +1655,112 @@ sub blocktype_text {
         'port' => 'Portfolio',
         'groups' => 'Groups',
         'blogs' => 'Blogs',
+        'about' => 'User Information',
+        'docs' => 'Content',
+        'printout' => 'Printouts',
+        'passwd' => 'Change Password',
+        'grades' => 'Gradebook',
+        'search' => 'Content Search',
+        'alert'  => 'Critical Alert',
+        'reinit' => 'Course Re-init',
     );
-    my $typeorder = ['com','chat','boards','port','groups','blogs'];
+    my $typeorder = ['com','chat','boards','port','groups','blogs','about','printout','docs','grades','search','alert','reinit','passwd'];
     return ($typeorder,\%types);
 }
 
+sub blockingmenu_javascript {
+    my ($blockcount) = @_;
+    return <<ENDSCRIPT;
+<script type="text/javascript">
+// <![CDATA[
+function resblockinfo(blockid) {
+    if (document.getElementById('docs_'+blockid).checked) {
+        var resblockwin = null;
+        var url = '/adm/setblock?action=showdocs&block='+blockid;
+        if (!resblockwin || resblockwin.closed) {
+            resblockwin=window.open(url,'blockingwin','height=480,width=600,resizable=yes,scrollbars=yes,location=no,menubar=no,toolbar=no');
+        }
+        resblockwin.focus();
+    } else {
+        document.getElementById('docs_resources_'+blockid).value = '';
+        document.getElementById('docs_maps_'+blockid).value = '';
+    }
+    return;
+}
+
+function showBlockType() {
+    if (document.getElementById('toggle_startstop').checked == true) {
+        document.getElementById('show_startstop').style.display='block';
+    } else {
+        document.getElementById('show_startstop').style.display='none';
+    }
+    if (document.getElementById('toggle_timer').checked == true) {
+        document.getElementById('show_timer').style.display='block';
+    } else {
+        document.getElementById('show_timer').style.display='none';
+    }
+    return;
+}
+
+function toggleAddModify() {
+    for (var i=0; i<document.blockform.blockaction.length; i++) {
+        if (document.blockform.blockaction[i].checked) {
+            if (document.blockform.blockaction[i].value == 'add') {
+               document.getElementById('showadd').style.display='block';
+               document.getElementById('showmodify').style.display='none';
+               var blocktotal = $blockcount;
+               if (blocktotal > 0) {
+                   for (var i=0; i<blocktotal; i++) {
+                       document.getElementById('nochange_'+i).checked = true;
+                   }
+               }
+               document.getElementById('showmodify').style.display='none';
+               document.getElementById('showadd').style.display='block';
+            } else {
+               document.getElementById('showadd').style.display='none';
+               document.getElementById('showmodify').style.display='block';
+            }
+        }
+    }
+    return;
+}
+
+// ]]>
+</script>
+ENDSCRIPT
+
+}
+
+sub details_javascript {
+    my %lt = &Apache::lonlocal::texthash (
+                                           more => 'More ...',
+                                           less => 'Less ...',
+                                         );
+    return <<ENDSCRIPT;
+
+<script type="text/javascript">
+// <![CDATA[
+
+function showTriggerDetails(item) {
+    document.getElementById('trigdetails_'+item).style.display='block';
+    document.getElementById('trigdetails_'+item).style.textAlign='left';
+    document.getElementById('trigdetails_'+item).style.textFace='normal';
+    document.getElementById('toggletext_'+item).innerHTML = '<a href="javascript:hideTriggerDetails('+item+');" style="text-decoration: none;"><b>($lt{'less'})</b></a>';
+    return;
+}
+
+function hideTriggerDetails(item) {
+    document.getElementById('trigdetails_'+item).style.display='none';
+    document.getElementById('toggletext_'+item).innerHTML = '<a href="javascript:showTriggerDetails('+item+');" style="text-decoration: none;"><b>($lt{'more'})</b></a>';
+    return;
+}
+
+// ]]>
+</script>
+ENDSCRIPT
+
+}
+
 1;
 
 __END__