--- loncom/interface/lonblockingmenu.pm 2013/01/04 05:45:50 1.9 +++ loncom/interface/lonblockingmenu.pm 2021/01/02 21:07:36 1.29 @@ -2,7 +2,7 @@ # Routines for configuring blocking of access to collaborative functions, # and specific resources during an exam # -# $Id: lonblockingmenu.pm,v 1.9 2013/01/04 05:45:50 raeburn Exp $ +# $Id: lonblockingmenu.pm,v 1.29 2021/01/02 21:07:36 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -57,6 +57,9 @@ 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 @@ -64,7 +67,7 @@ take place in a lab setting where use of 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. +can be restricted for the duration of an exam. Exam blocks are of two types: (a) Blocks with a defined start and end date. @@ -87,7 +90,7 @@ it is important to use reasonable time w 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. +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 @@ -102,6 +105,19 @@ block (including deletion), or when a ne =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 @@ -124,38 +140,21 @@ Output: 1 Hash Stores changes to exam blocks in comm_block.db file for course. Processes deletions, modifications and additions. -Inputs: 2 +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 &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 @@ -181,7 +180,7 @@ 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, +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. @@ -205,12 +204,30 @@ Update LON-CAPA version requirements for (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'). +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: None +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 - needed to update version requirements for course. + 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() @@ -218,7 +235,7 @@ Side Effects: &update_released_required( Generates web form elements used to display, cancel, or modify existing blocking events. -Inputs: 7 +Inputs: 8 - $r - Apache request object - $records - Reference to hash of current blocks @@ -234,6 +251,9 @@ Inputs: 7 - $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. @@ -298,13 +318,16 @@ Creates web form elements used to select items in the course for use in an exam block of type: "Triggered by Activating Timer". -Inputs: 7 (three required, last four optional) +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). @@ -319,6 +342,34 @@ Inputs: 7 (three required, last four opt 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 @@ -328,6 +379,7 @@ Inputs: 1 - $parmcount - numericID of ex 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 @@ -358,7 +410,8 @@ Output: 2 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 - +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 @@ -371,12 +424,21 @@ Output: 1 - Javascript (with tags) for functions used to: + toggle visibility of unordered list for display of detailed + information about intervals. + +=back =cut @@ -388,11 +450,15 @@ 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; @@ -413,16 +479,18 @@ sub handler { # ----------------------------------------------------------- Permissions check - unless ((&Apache::lonnet::allowed('dcm',$env{'request.course.id'})) || - (&Apache::lonnet::allowed('dcm',$env{'request.course.id'}. - '/'.$env{'request.course.sec'}))) { + my ($readonly,$allowed) = &get_permission(); + unless ($allowed) { $env{'user.error.msg'}= - "/adm/setblock:dcm:0:0:Cannot set blocking of communications in a course"; + "/adm/setblock:dcm:0:0:Cannot view/set blocking of communications in a course"; return HTTP_NOT_ACCEPTABLE; } # -----------------------------Get action and calling context from query string + $registered_cleanup=0; + @{$modified_courses}=(); + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['action','caller','block']); @@ -459,7 +527,7 @@ sub handler { $r->print(&Apache::loncourserespicker::create_picker($navmap, 'examblock','resourceblocks',$crstype, \%blockedmaps,\%blockedresources, - $env{'form.block'})); + $env{'form.block'},'','',undef,undef,$readonly)); } else { $r->print($errormsg); } @@ -468,10 +536,12 @@ sub handler { # -------------------------- 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); + unless ($readonly) { + if ($env{'form.action'} eq 'store') { + (my $numchanges,$storeresult) = &blockstore($r,$crstype,$blockcount,\%records); + if ($numchanges > 0) { + $blockcount = &get_blockdates(\%records); + } } } @@ -491,7 +561,8 @@ sub handler { ({href=>'/adm/setblock', text=>'Blocking communication/content access'}); - my $js = &blockingmenu_javascript($blockcount); + my $js = &blockingmenu_javascript($blockcount). + &details_javascript(); $r->print( &Apache::loncommon::start_page('Blocking communication/content access',$js). @@ -507,8 +578,9 @@ sub handler { 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 assigments in courses besides your own. Please use this feature wisely.", + '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)', @@ -546,9 +618,17 @@ sub handler { ''. + $lt{'flow'}. + ''. '

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

' ); @@ -556,8 +636,10 @@ sub handler { # ------------------------ Choose between modifying existing block or adding new $r->print('
'); - if ($blockcount > 0) { - $r->print(<<"END"); + + unless ($readonly) { + if ($blockcount > 0) { + $r->print(<<"END");
$lt{'actt'} @@ -574,33 +656,61 @@ onclick="toggleAddModify();" />$lt{'addn
'); + unless ($readonly) { + &display_addblocker_table($r,$blockcount,\%ltext,\%intervals, + $navmap,$errormsg); + if ($blockcount > 0) { + $r->print('
'); + } + } +# ------------------------------------------------ Interface for existing blocks + if (!$blockcount) { + if ($readonly) { + $r->print($lt{'ncbc'}.'
'); + } + } else { &display_blocker_status($r,\%records,\%ltext,\%intervals, - $navmap,$errormsg,$blockcount); + $navmap,$errormsg,$blockcount,$readonly); } - $r->print(<<"END"); + unless ($readonly) { + $r->print(<<"END");
-
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); @@ -617,7 +727,7 @@ sub get_timed_items { my $resourcedata=&Apache::lonparmset::readdata($cnum,$cdom); if (ref($resourcedata) eq 'HASH') { foreach my $key (keys(%{$resourcedata})) { - if ($key =~ /^\Q$cid\E(.+)\.0\.interval$/) { + if ($key =~ /^\Q$cid\E(.*)\.0\.interval$/) { my $middle = $1; if ($middle eq '') { $intervals{'course'}{'all'} = $resourcedata->{$key}; @@ -649,7 +759,7 @@ sub get_timed_items { } sub blockstore { - my ($crstype,$blockcount) = @_; + my ($r,$crstype,$blockcount,$currblockrecs) = @_; my %lt=&Apache::lonlocal::texthash( 'tfcm' => 'The following changes were made', 'ncwm' => 'No changes were made.', @@ -664,7 +774,7 @@ sub blockstore { my $changestotal = 0; my $addtimer = 0; my %blocking = (); - my (%map_url,%resource_symb,$output); + my (%map_url,%resource_symb,%titles,$output); $output = '

'.$lt{'head'}.'

'; if ($env{'form.blockaction'} eq 'modify') { foreach my $envkey (keys(%env)) { @@ -690,17 +800,43 @@ sub blockstore { $output = $lt{'unna'}.' '.$lt{'ncwm'}.'
'; return ($changestotal,$output); } - &enumerate_course_contents($navmap,\%map_url,\%resource_symb); + &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 $newkey; + my ($newkey,$status,$needsrelease);; if ($env{'form.firstaccess_'.$key}) { my $interval = &HTML::Entities::decode($env{'form.firstaccess_'.$key}); @@ -718,7 +854,13 @@ sub blockstore { } if ($newkey ne '') { unless (defined($removals{$key})) { - $addtimer ++; + ($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})).'
'; + } } } } @@ -726,22 +868,56 @@ sub blockstore { 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 ($blockdocs) { - &check_release_required('docs'); - } - if (ref($blocktypes) eq 'HASH') { - if ($blocktypes->{'printout'} eq 'on') { - &check_release_required('printout'); - } - } if (exists($removals{$key})) { $modtotal ++; } else { @@ -749,9 +925,11 @@ sub blockstore { } } else { if ($env{'form.toggle_'.$key} eq 'timer') { - $output .= '

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

'; + unless ($status eq 'fail') { + $output .= '

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

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

'. &mt('No date range found for new blocking event'). @@ -765,9 +943,6 @@ sub blockstore { $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'} ); - if ($addtimer) { - &check_release_required('timer'); - } } $changestotal = $canceltotal + $modtotal + $addtotal; if ($changestotal > 0) { @@ -800,31 +975,21 @@ sub blockstore { 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; - } +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 = []; } + undef($registered_cleanup); return; } @@ -864,6 +1029,9 @@ sub get_block_choices { 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; @@ -875,6 +1043,9 @@ sub get_block_choices { 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; @@ -894,10 +1065,15 @@ sub get_block_choices { } sub check_release_required { - my ($value) = @_; + my ($value,$chomemajor,$chomeminor) = @_; my $needsrelease = $Apache::lonnet::needsrelease{'course:commblock:'.$value}; if ($needsrelease) { - my $curr_required = + 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); @@ -910,14 +1086,14 @@ sub check_release_required { } } } - return; + return ('ok',$needsrelease); } sub display_blocker_status { - my ($r,$records,$ltext,$intervals,$navmap,$errormsg,$blockcount) = @_; + my ($r,$records,$ltext,$intervals,$navmap,$errormsg,$blockcount,$readonly) = @_; my $parmcount = 0; - my (%map_url,%resource_symb,%lookups); - &enumerate_course_contents($navmap,\%map_url,\%resource_symb); + 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( @@ -926,10 +1102,13 @@ sub display_blocker_status { 'noch' => 'No change', ); $r->print('

'. - &Apache::loncommon::start_data_table()); + &Apache::loncommon::start_data_table().''); + if ($readonly) { + $disabled = ' disabled="disabled"'; + } else { + $r->print(''); + } $r->print(<<"END"); - - $ltext->{'type'} $ltext->{'even'} $ltext->{'blck'} @@ -948,8 +1127,12 @@ END &Apache::loncommon::aboutmewrapper( &Apache::loncommon::plainname($setuname,$setudom), $setuname,$setudom); + my $state = ''; $r->print(&Apache::loncommon::start_data_table_row()); - $r->print(<<"ACT"); + 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); + $parmcount,$start,$onchange, + undef,$state); $endform = &Apache::lonhtmlcommon::date_setter('blockform','enddate_'. - $parmcount,$end,$onchange); + $parmcount,$end,$onchange, + undef,$state); $r->print('
'.$ltext->{'defs'}.''. $ltext->{'star'}.': '.$startform.'
'. $ltext->{'endd'}.':  '.$endform.'
'); } elsif ($record =~ /^firstaccess____(.+)$/) { my $item = $1; - my ($itemname,$iteminfo,$skipdetails); my $type = 'map'; my $url; if ($item eq 'course') { @@ -987,120 +1172,20 @@ ACT $url = $item; } $r->print('
'.$ltext->{'trig'}.''); - 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]', - ''. - $title.''); - } - } else { - if (ref($navmap)) { - my $res = $navmap->getBySymb($item); - my $title = $res->compTitle(); - $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')) { - if (ref($intervals->{$type}) eq 'HASH') { - $iteminfo .= &trigger_details_toggle($parmcount). - ''; - } - } - $r->print(&create_interval_form($intervals,$parmcount,$navmap,$item,$jschg, - $itemname,$iteminfo).'
'); + 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(''.&blocker_checkboxes($parmcount,$blocks,$jschg,\%lookups).''. + $r->print(''.&blocker_checkboxes($parmcount,$blocks,$jschg,\%lookups,$disabled).''. &Apache::loncommon::end_data_table_row()); $parmcount++; } @@ -1152,7 +1237,7 @@ sub display_addblocker_table { my %lt = &Apache::lonlocal::texthash( 'exam' => 'e.g., Exam 1', ); - my $intervalform = &create_interval_form($intervals,$parmcount,$navmap); + my $intervalform = &create_interval_form($intervals,$parmcount,$navmap,'blocking'); if ($intervalform ne '') { $intervalform = '
'. ''.$ltext->{'chtr'}.''. @@ -1190,7 +1275,7 @@ END } sub blocker_checkboxes { - my ($parmcount,$blocks,$jschg,$lookups) = @_; + my ($parmcount,$blocks,$jschg,$lookups,$disabled) = @_; my ($typeorder,$types) = &blocktype_text(); my $numinrow = 2; my %currdocs; @@ -1257,7 +1342,7 @@ sub blocker_checkboxes { } $output .= ''."\n"; if ($block eq 'docs') { if ($blockstatus ne '') { @@ -1276,9 +1361,10 @@ sub blocker_checkboxes { } sub create_interval_form { - my ($intervals,$parmcount,$navmap,$currkey,$jschg,$itemname,$iteminfo) = @_; + 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') { @@ -1289,7 +1375,7 @@ sub create_interval_form { $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"'; @@ -1312,14 +1406,18 @@ sub create_interval_form { } $intervalform .= ''; + $intervalform .= $itemname.''.$iteminfo; } else { - my $res = $navmap->getResourceByUrl($map); - my $title = $res->compTitle(); - my $path; - my $hierarchy = &show_timer_path($type,$map,$navmap); + 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). @@ -1328,11 +1426,15 @@ sub create_interval_form { $intervalform .= &mt('Timer for all items in folder: [_1]', ''.$title.''). ''.$path; - } - if ($currkey eq $map) { - $intervalform .= $iteminfo; + if ($context eq 'accesstimes') { + (undef,$iteminfo) = &interval_details($map,$type,$map,$navmap,$intervals,$counter); + if ($iteminfo) { + $intervalform .= ' '.$iteminfo; + } + } } $intervalform .= '
'; + $counter ++; } } } @@ -1340,22 +1442,30 @@ sub create_interval_form { if (ref($intervals->{$type}) eq 'HASH') { if (ref($navmap)) { foreach my $resource (sort(keys(%{$intervals->{$type}}))) { - my ($checked,$clickaction); + my ($checked,$clickaction,$resobj); if ($currkey eq $resource) { $checked = ' checked="checked"'; - } elsif ($jschg) { - $clickaction = ' onclick="'.$jschg.'"'; + } else { + $resobj = $navmap->getBySymb($resource); + next unless(ref($resobj)); + if ($jschg) { + $clickaction = ' onclick="'.$jschg.'"'; + } } $intervalform .= ''; + $intervalform .= $itemname.''.$iteminfo; } else { - my $res = $navmap->getBySymb($resource); - my $title = $res->compTitle(); - my $path; - my $hierarchy = &show_timer_path($type,$resource,$navmap); + 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). @@ -1364,11 +1474,19 @@ sub create_interval_form { $intervalform .= &mt('Timer for resource: [_1]',''.$title.''). ''. $path; - } - if ($currkey eq $resource) { - $intervalform .= $iteminfo; + 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 ++; } } } @@ -1378,7 +1496,7 @@ sub create_interval_form { if ($currkey ne '') { $intervalform = '&').' />'. + &HTML::Entities::encode($currkey,'"<>&').'"'.$disabled.' />'. $itemname.'
'; } else { $intervalform = &mt('No timed items defined.').' '. @@ -1396,6 +1514,121 @@ sub trigger_details_toggle { 'style="text-decoration: none;">'.&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)); @@ -1422,19 +1655,21 @@ 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','printout','docs']; + my $typeorder = ['com','chat','boards','port','groups','blogs','about','printout','docs','grades','search','alert','reinit','passwd']; return ($typeorder,\%types); } sub blockingmenu_javascript { my ($blockcount) = @_; - my %lt = &Apache::lonlocal::texthash ( - more => 'More ...', - less => 'Less ...', - ); return < // + +ENDSCRIPT + +} + +sub details_javascript { + my %lt = &Apache::lonlocal::texthash ( + more => 'More ...', + less => 'Less ...', + ); + return < +//