--- loncom/interface/lonblockingmenu.pm 2011/12/28 22:09:44 1.2 +++ loncom/interface/lonblockingmenu.pm 2021/01/02 21:07:36 1.29 @@ -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.2 2011/12/28 22:09:44 raeburn Exp $ +# $Id: lonblockingmenu.pm,v 1.29 2021/01/02 21:07:36 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 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 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,18 +479,73 @@ 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 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,$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 @@ -123,78 +559,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('

'.$lt{'cbds'}.'

'); +# ---------------------------------------------------- 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'}. + ''. + $lt{'flow'}. + ''. + '

'.$lt{'blca'}.'
'.$lt{'pobl'}.'

' + ); } - $r->print($lt{'desc'}.'

-
- '); +# ------------------------ Choose between modifying existing block or adding new + $r->print(''); - $r->print('

'.$lt{'mecb'}.'

'); - 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"); +
+
$lt{'actt'} + + + +
+ + + +
+
+
+'); + } + } +# ------------------------------------------------ Interface for existing blocks + if (!$blockcount) { + if ($readonly) { + $r->print($lt{'ncbc'}.'
'); + } } else { - $r->print($lt{'ncbc'}.'

'); + &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");
- -
-$end_page END - - $r->print(&Apache::loncommon::end_page()); + } + $r->print(''. + &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 = (); @@ -202,42 +771,171 @@ sub blockstore { my $modtotal = 0; my $canceltotal = 0; my $addtotal = 0; + my $changestotal = 0; + my $addtimer = 0; my %blocking = (); - $r->print('

'.$lt{'head'}.'

'); - 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 = '

'.$lt{'head'}.'

'; + 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'}.'
'; + 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 .= '

'. + &mt('Triggering of blocking events not allowed for [_1]', + &escape($env{'form.title_'.$key})).'
'; + } + } + } + } + } 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).'

'; + } + 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 .= '

'. + &mt('Printout blocking not allowed for [_1]', + &escape($env{'form.title_'.$key})).'
'; + } + } + if ($blocktypes->{'alert'} eq 'on') { + ($status,$needsrelease) = &check_release_required('alert',$chomemajor,$chomeminor); + if ($status eq 'fail') { + $blocktypes->{'alert'} = 'off'; + $output .= '

'. + &mt('Message Alert blocking not allowed for [_1]', + &escape($env{'form.title_'.$key})).'
'; + } + } + if ($blocktypes->{'reinit'} eq 'on') { + ($status,$needsrelease) = &check_release_required('reinit',$chomemajor,$chomeminor); + if ($status eq 'fail') { + $blocktypes->{'reinit'} = 'off'; + $output .= '

'. + &mt('Course Re-initialization blocking not allowed for [_1]', + &escape($env{'form.title_'.$key})).'
'; + } + } + } + if ($blockdocs) { + ($status,$needsrelease) = &check_release_required('docs',$chomemajor,$chomeminor); + if ($status eq 'fail') { + delete($blocktypes->{'docs'}); + $output .= '

'. + &mt('Content blocking not allowed for [_1]', + &escape($env{'form.title_'.$key})).'
'; + } + } + $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 .= '

'. + &mt('Invalid trigger for new blocking event'). + '

'; + } + } else { + $output .= '

'. + &mt('No date range found for new blocking event'). + '

'; + } + } } } if ($addtotal + $modtotal > 0) { @@ -246,23 +944,52 @@ sub blockstore { $env{'course.'.$env{'request.course.id'}.'.num'} ); } - my $chgestotal = $canceltotal + $modtotal + $addtotal; - if ($chgestotal > 0) { - $r->print($lt{'tfcm'}.''; } else { - $r->print($lt{'ncwm'}); + $output .= $lt{'ncwm'}; + } + $output .= '
'; + 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 = []; } - $r->print('
'); + undef($registered_cleanup); return; } @@ -274,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('
'. + &Apache::loncommon::start_data_table().''); + if ($readonly) { + $disabled = ' disabled="disabled"'; + } else { + $r->print(''); + } $r->print(<<"END"); - - $ltext->{'dura'} - $ltext->{'setb'} + $ltext->{'type'} $ltext->{'even'} $ltext->{'blck'} - $ltext->{'actn'} 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"); - $ltext->{'star'}: $startform
$ltext->{'endd'}:  $endform - $settername - - -END - foreach my $block (@{$typeorder}) { - my $blockstatus = ''; - if ($blocks->{$block} eq 'on') { - $blockstatus = 'checked="checked"'; + if ($readonly) { + $state = 'disabled'; + } else { + $r->print(<<"ACT"); + +
+
+ + +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('
'.$ltext->{'defs'}.''. + $ltext->{'star'}.': '.$startform.'
'. + $ltext->{'endd'}.':  '.$endform.'
'); + } 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('
'); + $r->print('
'.$ltext->{'trig'}.''); + 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).'
'); } $r->print(<<"END"); - -
- + + + +
+
+ $ltext->{'setb'}: $settername + END - $r->print(&Apache::loncommon::end_data_table_row()); + $r->print(''.&blocker_checkboxes($parmcount,$blocks,$jschg,\%lookups,$disabled).''. + &Apache::loncommon::end_data_table_row()); $parmcount++; } $r->print(<<"END"); -
-
+
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"); -

$lt{'addn'}

-END + my $intervalform = &create_interval_form($intervals,$parmcount,$navmap,'blocking'); + if ($intervalform ne '') { + $intervalform = '
'. + ''.$ltext->{'chtr'}.''. + $intervalform. + '
'; + } $r->print(&Apache::loncommon::start_data_table()); $r->print(<<"END"); - $ltext->{'dura'} + $ltext->{'type'} $ltext->{'even'} $lt{'exam'} $ltext->{'blck'} - $ltext->{'actn'} END - $r->print(&Apache::loncommon::start_data_table_row()); + $r->print(&Apache::loncommon::start_data_table_row().''); $r->print(<<"END"); - $ltext->{'star'}: $startform
$ltext->{'endd'}:  $endform - - -END - foreach my $block (@{$typeorder}) { - $r->print('
'); - } - $r->print(<<"END"); +    +
+
+
$ltext->{'chda'} + $ltext->{'star'}: $startform
$ltext->{'endd'}:  $endform
+ $ltext->{'when'}
+ - + END - $r->print(&Apache::loncommon::end_data_table_row()); - $r->print(&Apache::loncommon::end_data_table()); + $r->print(''.&blocker_checkboxes($parmcount).''. + &Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table()."\n". + '
'); + return; +} + +sub blocker_checkboxes { + my ($parmcount,$blocks,$jschg,$lookups,$disabled) = @_; + my ($typeorder,$types) = &blocktype_text(); + my $numinrow = 2; + my %currdocs; + my $output = ''; + 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 .= ''; + } + $output .= ''; + } + if ($i == scalar(@{$typeorder})-1) { + my $colsleft = $numinrow-$rem; + if ($colsleft > 1) { + $output .= ''; + } + $output .= '
'; + } else { + $output .= ''; + } + } else { + $output .= ''; + } + my $item = $block.'_'.$parmcount; + if ($clickaction) { + $clickaction = ' onclick="'.$clickaction.'"'; + } + if ($blockstatus) { + $blockstatus = ' '.$blockstatus; + } + $output .= ''."\n"; + if ($block eq 'docs') { + if ($blockstatus ne '') { + $output .= ' '. + &mt('Details').''; + } + } + $output .= '
'. + ''. + ''; + 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 .= ''; + if ($currkey eq 'course') { + $intervalform .= $iteminfo; + } elsif ($context eq 'accesstimes') { + (undef,$iteminfo) = &interval_details('course',$type,'',$navmap,$intervals,$counter); + if ($iteminfo) { + $intervalform .= ' '.$iteminfo; + } + } + $intervalform .= '
'; + $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 .= ''.$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 = ' '. + &mt('(in: [_1])',$hierarchy). + ''; + } + $intervalform .= &mt('Timer for all items in folder: [_1]', + ''.$title.''). + ''.$path; + if ($context eq 'accesstimes') { + (undef,$iteminfo) = &interval_details($map,$type,$map,$navmap,$intervals,$counter); + if ($iteminfo) { + $intervalform .= ' '.$iteminfo; + } + } + } + $intervalform .= '
'; + $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 .= ''.$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 = ' '. + &mt('(in: [_1])',$hierarchy). + ''; + } + $intervalform .= &mt('Timer for resource: [_1]',''.$title.''). + ''. + $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 .= '
'; + $counter ++; + } + } + } + } + } + } else { + if ($currkey ne '') { + $intervalform = '&').'"'.$disabled.' />'. + $itemname.'
'; + } else { + $intervalform = &mt('No timed items defined.').' '. + &mt('Use [_1]Settings[_2] to assign a timer, then return here.', + '',''); + } + } + return $intervalform; +} + +sub trigger_details_toggle { + my ($parmcount) = @_; + return ' '. + ''.&mt('(More ...)').''; +} + +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]', + ''. + $title.''); + } + } 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]', + ''. + $title.''); + } + } + if (ref($navmap)) { + my $path = &show_timer_path($type,$item); + if ($path) { + $iteminfo = ' '. + &mt('(in: [_1])',$path). + ''; + } + } + } else { + $skipdetails = 1; + $itemname = ''. + &mt('Timer folder/resource not in course'). + ''; + } + } + if ((!$skipdetails) && (ref($intervals) eq 'HASH') && (ref($intervals->{$type}) eq 'HASH')) { + $iteminfo = &trigger_details_toggle($parmcount). + ''; + } + 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(' » ',@pathitems); + } return; } @@ -424,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 < +// 0) { + for (var i=0; i + +ENDSCRIPT + +} + +sub details_javascript { + my %lt = &Apache::lonlocal::texthash ( + more => 'More ...', + less => 'Less ...', + ); + return < +// ($lt{'less'})'; + return; +} + +function hideTriggerDetails(item) { + document.getElementById('trigdetails_'+item).style.display='none'; + document.getElementById('toggletext_'+item).innerHTML = '($lt{'more'})'; + return; +} + +// ]]> + +ENDSCRIPT + +} + 1; __END__