[LON-CAPA-cvs] cvs: loncom /interface lonblockingmenu.pm

raeburn raeburn at source.lon-capa.org
Sat Mar 31 10:15:24 EDT 2012


raeburn		Sat Mar 31 14:15:24 2012 EDT

  Modified files:              
    /loncom/interface	lonblockingmenu.pm 
  Log:
  - Bug 6518.
    - Exam blocks can also block access to specified folders and/or resources
      for the duration of an exam.
    - The start time for blocking can be based on activation of the timer for
      a specific timed quiz item (map or resource specific).  In this case,
      the end time for blocking is the start time plus the duration of the 
      interval.
  - Add documentation
  
  
-------------- next part --------------
Index: loncom/interface/lonblockingmenu.pm
diff -u loncom/interface/lonblockingmenu.pm:1.3 loncom/interface/lonblockingmenu.pm:1.4
--- loncom/interface/lonblockingmenu.pm:1.3	Wed Dec 28 22:41:02 2011
+++ loncom/interface/lonblockingmenu.pm	Sat Mar 31 14:15:24 2012
@@ -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.4 2012/03/31 14:15:24 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -26,7 +26,7 @@
 #
 # http://www.lon-capa.org/
 #
-###############################################################
+##############################################################
 ##############################################################
 
 =pod
@@ -44,24 +44,355 @@
 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.
+
+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 also 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 assigments.
+
+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_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: 2
+      $crstype - Container type: Course or Community.
+
+      $blockcount - Total number of blocking events in course.
+
+Outputs: 2
+      $changestotal - Total number of changes made.
+
+      $output - Information about changes made.
+
+
+=item &enumerate_course_contents()
+
+Create hashes of maps (for folders/pages) and symbs (for resources) in
+a course, where keys are numbers (starting with 1) and values are
+map url, or symb, for an iteration through the course, as seen by
+a Course Coordinator. Used to generate numerical IDs to facilitate
+storage of lists of maps or resources to be blocked during an exam.   
+
+Inputs: 3
+      $navmap - navmaps object
+
+      $map_url - reference to hash to contain URLs of maps in course
+
+      $resource_symb - reference to hash to contain symbs for
+                       resources in course
+
+Outputs: None
+
+Side Effects: $map_url and $resource_symb hashrefs are populated.
+
+
 =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 partilcuar 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: 1 - type of constraint (currently: 'docs' or 'timer'). 
+
+Outputs: None
+
+Side Effects: &update_released_required() called in lonnet, if
+              needed to update version requirements for course.   
+
+
 =item &display_blocker_status()
 
+Generates web form elements used to display, cancel, or modify 
+existing blocking events. 
+
+Inputs: 7 
+      - $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
+
+Output: None
+
+Side Effects: prints web form elements (in a table) for current blocks. 
+
+=item &path_to_trigger()
+
+Provides hierarchy of names of folders/sub-folders containing the current
+item identified as an item with an interval timer set, to be used as a 
+trigger. 
+
+Inputs: 3 
+     - $navmap - navmaps object
+
+     - $map - url for map (either the trigger itself, or map containing
+                           the resource, which is the trigger). 
+
+     - $type - type of trigger: map or resource.
+
+Outputs: 1 @pathitems - array of folder/subfolder names.  
+
+
+=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: 7 (three required, last four optional)
+   - $intervals - Reference to hash of parameters for timed intervals
+
+   - $parmcount - numeric ID of current block
+
+   - $navmap - navmaps object
+
+   - $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 &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()
 
+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 - 
+
+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
+
+=item *  for display of detailed information about intervals 
+
+=back
+
+
 =back  
 
 =cut
@@ -73,6 +404,7 @@
 use Apache::Constants qw(:common :http);
 use Apache::loncommon();
 use Apache::lonhtmlcommon();
+use Apache::lonparmset();
 use HTML::Entities();
 use Apache::lonlocal;
 use lib '/home/httpd/lib/perl/';
@@ -108,10 +440,59 @@
 
 # -----------------------------Get action and calling context from query string
 
-    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['action','caller']);
+    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+                                            ['action','caller','block']);
 
-# ----------------------------------------------------------------- Breadcrumbs
+    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'}));
+        } else {
+            $r->print($errormsg);
+        }
+        return OK;
+    }
 
+# -------------------------- Store changes and retrieve latest block information
+    my $storeresult;
+    if ($env{'form.action'} eq 'store') {
+        (my $numchanges,$storeresult) = &blockstore($crstype,$blockcount);
+        if ($numchanges > 0) {
+            $blockcount = &get_blockdates(\%records);
+        }
+    }
+
+# ------------------------------------------------------------------ Breadcrumbs
     &Apache::lonhtmlcommon::clear_breadcrumbs();
     if ($env{'form.caller'} eq 'email') {  
         &Apache::lonhtmlcommon::add_breadcrumb
@@ -127,10 +508,11 @@
         ({href=>'/adm/setblock',
           text=>'Blocking communication/resource access'});
 
-    $r->print(&Apache::loncommon::start_page('Blocking communication/resource access').
-              &Apache::lonhtmlcommon::breadcrumbs('Blocking communication/resource access'));
+    my $js = &blockingmenu_javascript($blockcount);
 
-# ----------------------------------------------------------- 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();
@@ -140,63 +522,155 @@
         $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:",
+            '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 assigments 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 or blogs').'</li>'."\n".
+            '<li>'.&mt("accessing $lctype content in specified folders or resources").'</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">
-             ');
-
-    $r->print('<h4>'.$lt{'mecb'}.'</h4>');
-    my %records = ();
-    my $blockcount = 0;
-    my $parmcount = 0;
-    &get_blockdates(\%records,\$blockcount);
+# ------------------------ Choose between modifying existing block or adding new
+    $r->print('<form name="blockform" method="post" action="/adm/setblock?action=store">');
     if ($blockcount > 0) {
-        $parmcount = &display_blocker_status($r,\%records,\%ltext);
+         $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 />');
+        $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
+    &display_addblocker_table($r,$blockcount,\%ltext,\%intervals,
+                              $navmap,$errormsg);
+
+# ------------------------------------------------- Interface for existig blocks
+    if ($blockcount > 0) {
+        $r->print('</div>');
+        &display_blocker_status($r,\%records,\%ltext,\%intervals,
+                                $navmap,$errormsg,$blockcount);
     }
-    &display_addblocker_table($r,$parmcount,\%ltext);
-    my $end_page=&Apache::loncommon::end_page();
     $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());
     return OK;
 }
 
+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 ($crstype,$blockcount) = @_;
     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,25 +678,36 @@
     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,$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);
+    }
+    &enumerate_course_contents($navmap,\%map_url,\%resource_symb);
     foreach my $key (keys(%removals)) {
         my $hashkey = $env{'form.key_'.$key};
         &Apache::lonnet::del('comm_block',["$hashkey"],
@@ -232,14 +717,59 @@
     }
     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;
+            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})) {
+                            $addtimer ++;
+                        }
+                    }
+                }
+            } else {
+                my ($newstart,$newend) = &get_dates_from_form($key);
+                $newkey = $newstart.'____'.$newend;
+            }
+            if ($newkey ne '') {
+                my ($blocktypes,$blockdocs) = 
+                    &get_block_choices($key,\%map_url,\%resource_symb);
+                $blocking{$newkey} = {
                           setter => $env{'user.name'}.':'.$env{'user.domain'},
                           event  => &escape($env{'form.title_'.$key}),
                           blocks => $blocktypes,
                         };
+                if ($blockdocs) {
+                    &check_release_required('docs');
+                }
+                if (exists($removals{$key})) {
+                    $modtotal ++;
+                } else {
+                    $addtotal ++;
+                }
+            } else {
+                if ($env{'form.toggle_'.$key} eq 'timer') {
+                    $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) {
@@ -247,24 +777,66 @@
                      $env{'course.'.$env{'request.course.id'}.'.domain'},
                      $env{'course.'.$env{'request.course.id'}.'.num'}
                      );
+        if ($addtimer) {
+            &check_release_required('timer');
+        }
     }
-    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'};
+    }
+    $output .= '<br />';
+    return ($changestotal,$output);
+}
+
+sub enumerate_course_contents {
+    my ($navmap,$map_url,$resource_symb) = @_;
+    if ((ref($navmap)) && (ref($map_url) eq 'HASH') && 
+        (ref($resource_symb) eq 'HASH')) {
+        my $it = $navmap->getIterator(undef,undef,undef,1,undef,undef);
+        my $count = 0;
+        while (my $curRes = $it->next()) {
+            if (ref($curRes)) {
+                $count ++;
+                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';
+                    }
+                }
+                if (($curRes->is_sequence()) || ($curRes->is_page())) {
+                    $map_url->{$count} = (&Apache::lonnet::decode_symb($symb))[2];
+                } else {
+                    $resource_symb->{$count} = $ressymb;
+                }
+            }
+        }
     }
-    $r->print('<br />');
     return;
 }
 
@@ -276,145 +848,618 @@
 }
 
 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}));
+                        $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}));
+                        $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) = @_; 
+    my $needsrelease = $Apache::lonnet::needsrelease{'course:commblock:'.$value};
+    if ($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;
 }
 
 sub display_blocker_status {
-    my ($r,$records,$ltext) = @_;
+    my ($r,$records,$ltext,$intervals,$navmap,$errormsg,$blockcount) = @_;
     my $parmcount = 0;
- 
+    my (%map_url,%resource_symb,%lookups);
+    &enumerate_course_contents($navmap,\%map_url,\%resource_symb);
+    %{$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());
     $r->print(<<"END");
   <tr>
-    <th>$ltext->{'dura'}</th>
-    <th>$ltext->{'setb'}</th>
+    <th></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);
         $r->print(&Apache::loncommon::start_data_table_row());
-        $r->print(<<"END");
-        <td>$ltext->{'star'}: $startform<br />$ltext->{'endd'}:  $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"';
+        $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);
+            $endform = &Apache::lonhtmlcommon::date_setter('blockform','enddate_'.
+                                                           $parmcount,$end,$onchange);
+            $r->print('<td><fieldset><legend>'.$ltext->{'defs'}.'</legend>'.
+                      $ltext->{'star'}.': '.$startform.'<br />'.
+                      $ltext->{'endd'}.':  '.$endform.'</fieldset></td>');
+        } elsif ($record =~ /^firstaccess____(.+)$/) {
+            my $item = $1;
+            my ($itemname,$iteminfo,$skipdetails);
+            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('<td><fieldset><legend>'.$ltext->{'trig'}.'</legend>');
+            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 $res = $navmap->getResourceByUrl($item);
+                            my $title = $res->compTitle();
+                            $itemname = &mt('Timer for all items in folder: [_1]',
+                                            '<span style="font-style:italic">'.
+                                            $title.'</span>');
+                        }
+                    } else {
+                        if (ref($navmap)) {
+                            my $res = $navmap->getBySymb($item);
+                            my $title = $res->compTitle();
+                            $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>';  
+                }
             }
-            $r->print('<span class="LC_nobreak"><label><input type="checkbox" name="'.$block.'_'.$parmcount.'" '.$blockstatus.' value="1" />'.$types->{$block}.'</label></span><br />');
+            if ((!$skipdetails) && (ref($intervals) eq 'HASH')) {
+                if (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>';
+                }
+            }
+            $r->print(&create_interval_form($intervals,$parmcount,$navmap,$item,$jschg,
+                                            $itemname,$iteminfo).'</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" />
+         <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).'</td>'.
+                  &Apache::loncommon::end_data_table_row());
         $parmcount++;
     }
     $r->print(<<"END");
 </table>
-<br />
-<br />
+</div>
 END
-    return $parmcount;
+    return;
+}
+
+sub path_to_trigger {
+    my ($navmap,$map,$type) = @_;
+    my @pathitems;
+    if (ref($navmap)) {
+        my $mapres = $navmap->getResourceByUrl($map);
+        if (ref($mapres)) {
+            my $pcslist = $mapres->map_hierarchy();
+            if ($pcslist ne '') {
+                my @pcs = split(/,/,$pcslist);
+                foreach my $pc (@pcs) {    
+                    if ($pc == 1) {
+                        push(@pathitems,&mt('Main Course Documents'));
+                    } else {
+                        my $res = $navmap->getByMapPc($pc);
+                        if (ref($res)) {
+                            my $title = $res->compTitle();
+                            $title =~ s/\W+/_/g;
+                            if ($title ne '') {
+                                push(@pathitems,$title);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if ($type eq 'resource') {
+            if ($mapres->{ID} eq '0.0') {
+                push(@pathitems,&mt('Main Course Documents'));
+            } else {
+                my $maptitle = $mapres->compTitle();
+                $maptitle =~ s/\W+/_/g;
+                if ($maptitle ne '') {
+                    push(@pathitems,$maptitle);
+                }
+            }
+        }
+    }
+    return @pathitems;
+}
+
+sub convlim {
+    my ($timelimit) = @_;
+    my $output;
+    my @order = ('days','hours','minutes','seconds');
+    my %catlimits = ( 
+                      days    => 86400,
+                      hours   => 3600,
+                      minutes => 60,
+                    );
+    my @toshow;
+    foreach my $cat (@order) {
+        if ($cat eq 'seconds') {
+            last if ($timelimit <= 0);
+        } 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(', ', at 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);
+    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'}: $startform<br />$ltext->{'endd'}:  $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>   
+     <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'}: $startform<br />$ltext->{'endd'}:  $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) = @_;
+    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(',', at 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" />'.
+                   $types->{$block}.'</label></span>'."\n";
+        if ($block eq 'docs') {
+            if ($blockstatus ne '') {
+                $output .= ' <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,$currkey,$jschg,$itemname,$iteminfo) = @_;
+    return unless ((ref($intervals) eq 'HASH') && (ref($navmap)));
+    my $intervalform;
+    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.' />';
+                if ($currkey eq 'course') {
+                    $intervalform .= $itemname;
+                } else {
+                    $intervalform .= &mt('Timer for all items in course');
+                }
+                $intervalform .= '</label>';
+                if ($currkey eq 'course') {
+                    $intervalform .= $iteminfo;
+                }
+                $intervalform .= '<br />';
+            } elsif ($type eq 'map') {
+                if (ref($intervals->{$type}) eq 'HASH') {
+                    if (ref($navmap)) {
+                        foreach my $map (sort(keys(%{$intervals->{$type}}))) {
+                            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.' />';
+                            if ($currkey eq $map) {
+                                $intervalform .= $itemname.'</label>';
+                            } else {
+                                my $res = $navmap->getResourceByUrl($map);
+                                my $title = $res->compTitle();
+                                my $path;
+                                my $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 ($currkey eq $map) {
+                                $intervalform .= $iteminfo;
+                            }
+                            $intervalform .= '<br />';
+                        }
+                    }
+                }
+            } elsif ($type eq 'resource') {
+                if (ref($intervals->{$type}) eq 'HASH') {
+                    if (ref($navmap)) {
+                        foreach my $resource (sort(keys(%{$intervals->{$type}}))) {
+                            my ($checked,$clickaction);
+                            if ($currkey eq $resource) {
+                                $checked = ' checked="checked"';
+                            } elsif ($jschg) {
+                                $clickaction = ' onclick="'.$jschg.'"';
+                            }
+                            $intervalform .= '<label><input type="radio" name="firstaccess_'.$parmcount.
+                                             '" value="'.&HTML::Entities::encode($resource,'"<>&').'"'.
+                                             $checked.$clickaction.' />';
+                            if ($currkey eq $resource) {
+                                $intervalform .= $itemname.'</label>';
+                            } else {
+                                my $res = $navmap->getBySymb($resource);
+                                my $title = $res->compTitle();
+                                my $path;
+                                my $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 ($currkey eq $resource) {
+                                $intervalform .= $iteminfo;
+                            }
+                            $intervalform .= '<br />';
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        if ($currkey ne '') {
+            $intervalform = '<input type="radio" name="firstaccess_'.$parmcount.
+                            '" checked="checked" value="'.
+                            &HTML::Entities::encode($currkey,'"<>&').' />'.
+                            $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 show_timer_path {
+    my ($type,$item,$navmap) = @_;
+    return unless(ref($navmap));
+    my @pathitems;
+    if ($type eq 'map') {
+        @pathitems = &path_to_trigger($navmap,$item,$type);
+    } elsif ($type eq 'resource') {
+        my ($map,$id,$resource) = &Apache::lonnet::decode_symb($item);
+        @pathitems = &path_to_trigger($navmap,$map,$type);
+    }
+    if (@pathitems) {
+        return join(' » ', at pathitems);
+    }
     return;
 }
 
@@ -426,11 +1471,93 @@
         'port' => 'Portfolio',
         'groups' => 'Groups',
         'blogs' => 'Blogs',
+        'docs' => 'Content', 
     );
-    my $typeorder = ['com','chat','boards','port','groups','blogs'];
+    my $typeorder = ['com','chat','boards','port','groups','blogs','docs'];
     return ($typeorder,\%types);
 }
 
+sub blockingmenu_javascript {
+    my ($blockcount) = @_;
+    my %lt = &Apache::lonlocal::texthash (
+                                           more => 'More ...',
+                                           less => 'Less ...',
+                                         );
+    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;
+}
+
+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__


More information about the LON-CAPA-cvs mailing list