--- loncom/lonnet/perl/lonnet.pm 2011/12/14 21:03:53 1.1149 +++ loncom/lonnet/perl/lonnet.pm 2012/04/01 16:19:20 1.1163 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.1149 2011/12/14 21:03:53 raeburn Exp $ +# $Id: lonnet.pm,v 1.1163 2012/04/01 16:19:20 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -96,6 +96,7 @@ use Math::Random; use File::MMagic; use LONCAPA qw(:DEFAULT :match); use LONCAPA::Configuration; +use LONCAPA::lonmetadata; use File::Copy; @@ -595,13 +596,21 @@ sub transfer_profile_to_env { # ---------------------------------------------------- Check for valid session sub check_for_valid_session { - my ($r) = @_; + my ($r,$name) = @_; my %cookies=CGI::Cookie->parse($r->header_in('Cookie')); - my $lonid=$cookies{'lonID'}; + if ($name eq '') { + $name = 'lonID'; + } + my $lonid=$cookies{$name}; return undef if (!$lonid); my $handle=&LONCAPA::clean_handle($lonid->value); - my $lonidsdir=$r->dir_config('lonIDsDir'); + my $lonidsdir; + if ($name eq 'lonDAV') { + $lonidsdir=$r->dir_config('lonDAVsessDir'); + } else { + $lonidsdir=$r->dir_config('lonIDsDir'); + } return undef if (!-e "$lonidsdir/$handle.id"); my $opened = open(my $idf,'+<',"$lonidsdir/$handle.id"); @@ -930,7 +939,7 @@ sub choose_server { my %domconfhash = &Apache::loncommon::get_domainconf($udom); my %servers = &get_servers($udom); my $lowest_load = 30000; - my ($login_host,$hostname,$portal_path); + my ($login_host,$hostname,$portal_path,$isredirect); foreach my $lonhost (keys(%servers)) { my $loginvia; if ($checkloginvia) { @@ -941,12 +950,14 @@ sub choose_server { &compare_server_load($server, $login_host, $lowest_load); if ($login_host eq $server) { $portal_path = $path; + $isredirect = 1; } } else { ($login_host, $lowest_load) = &compare_server_load($lonhost, $login_host, $lowest_load); if ($login_host eq $lonhost) { $portal_path = ''; + $isredirect = ''; } } } else { @@ -957,7 +968,7 @@ sub choose_server { if ($login_host ne '') { $hostname = &hostname($login_host); } - return ($login_host,$hostname,$portal_path); + return ($login_host,$hostname,$portal_path,$isredirect); } # --------------------------------------------- Try to change a user's password @@ -2799,7 +2810,7 @@ sub resizeImage { # $resizewidth - width (pixels) to which to resize uploaded image # $resizeheight - height (pixels) to which to resize uploaded image # $mimetype - reference to scalar to accommodate mime type determined -# from File::MMagic if $parser = parse. +# from File::MMagic. # # output: url of file in userspace, or error: # or /adm/notfound.html if failure to upload occurse @@ -2968,10 +2979,17 @@ sub finishuserfileupload { } } } + if (($context eq 'coursedoc') || ($parser eq 'parse')) { + if (ref($mimetype)) { + if ($$mimetype eq '') { + my $mm = new File::MMagic; + my $type = $mm->checktype_filename($filepath.'/'.$file); + $$mimetype = $type; + } + } + } if ($parser eq 'parse') { - my $mm = new File::MMagic; - my $type = $mm->checktype_filename($filepath.'/'.$file); - if ($type eq 'text/html') { + if ((ref($mimetype)) && ($$mimetype eq 'text/html')) { my $parse_result = &extract_embedded_items($filepath.'/'.$file, $allfiles,$codebase); unless ($parse_result eq 'ok') { @@ -2979,9 +2997,6 @@ sub finishuserfileupload { ' for embedded media: '.$parse_result); } } - if (ref($mimetype)) { - $$mimetype = $type; - } } if (($thumbwidth =~ /^\d+$/) && ($thumbheight =~ /^\d+$/)) { my $input = $filepath.'/'.$file; @@ -3252,15 +3267,10 @@ sub flushcourselogs { my $result = &inc('nohist_accesscount',\%temphash,$dom,$name); if ($result eq 'ok') { delete $accesshash{$entry}; - } elsif ($result eq 'unknown_cmd') { - # Target server has old code running on it. - my %temphash=($entry => $value); - if (&put('nohist_resevaldata',\%temphash,$dom,$name) eq 'ok') { - delete $accesshash{$entry}; - } } } else { my ($dom,$name) = ($entry=~m{___($match_domain)/($match_name)/(.*)___(\w+)$}); + if (($dom eq 'uploaded') || ($dom eq 'adm')) { next; } my %temphash=($entry => $accesshash{$entry}); if (&put('nohist_resevaldata',\%temphash,$dom,$name) eq 'ok') { delete $accesshash{$entry}; @@ -3371,7 +3381,13 @@ sub countacc { my $url=&declutter(shift); return if (! defined($url) || $url eq ''); unless ($env{'request.course.id'}) { return ''; } +# +# Mark that this url was used in this course +# $accesshash{$env{'request.course.id'}.'___'.$url.'___course'}=1; +# +# Increase the access count for this resource in this child process +# my $key=$$.$processmarker.'_'.$dumpcount.'___'.$url.'___count'; $accesshash{$key}++; } @@ -3383,6 +3399,22 @@ sub linklog { $accesshash{$from.'___'.$to.'___comefrom'}=1; $accesshash{$to.'___'.$from.'___goto'}=1; } + +sub statslog { + my ($symb,$part,$users,$av_attempts,$degdiff)=@_; + if ($users<2) { return; } + my %dynstore=&LONCAPA::lonmetadata::dynamic_metadata_storage({ + 'course' => $env{'request.course.id'}, + 'sections' => '"all"', + 'num_students' => $users, + 'part' => $part, + 'symb' => $symb, + 'mean_tries' => $av_attempts, + 'deg_of_diff' => $degdiff}); + foreach my $key (keys(%dynstore)) { + $accesshash{$key}=$dynstore{$key}; + } +} sub userrolelog { my ($trole,$username,$domain,$area,$tstart,$tend)=@_; @@ -3852,11 +3884,34 @@ sub get_domain_roles { # ----------------------------------------------------------- Interval timing +{ +# Caches needed for speedup of navmaps +# We don't want to cache this for very long at all (5 seconds at most) +# +# The user for whom we cache +my $cachedkey=''; +# The cached times for this user +my %cachedtimes=(); +# When this was last done +my $cachedtime=(); + +sub load_all_first_access { + my ($uname,$udom)=@_; + if (($cachedkey eq $uname.':'.$udom) && + (abs($cachedtime-time)<5)) { + return; + } + $cachedtime=time; + $cachedkey=$uname.':'.$udom; + %cachedtimes=&dump('firstaccesstimes',$udom,$uname); +} + sub get_first_access { - my ($type,$argsymb)=@_; + my ($type,$argsymb,$argmap)=@_; my ($symb,$courseid,$udom,$uname)=&whichuser(); if ($argsymb) { $symb=$argsymb; } my ($map,$id,$res)=&decode_symb($symb); + if ($argmap) { $map = $argmap; } if ($type eq 'course') { $res='course'; } elsif ($type eq 'map') { @@ -3864,12 +3919,12 @@ sub get_first_access { } else { $res=$symb; } - my %times=&get('firstaccesstimes',["$courseid\0$res"],$udom,$uname); - return $times{"$courseid\0$res"}; + &load_all_first_access($uname,$udom); + return $cachedtimes{"$courseid\0$res"}; } sub set_first_access { - my ($type)=@_; + my ($type,$interval)=@_; my ($symb,$courseid,$udom,$uname)=&whichuser(); my ($map,$id,$res)=&decode_symb($symb); if ($type eq 'course') { @@ -3879,13 +3934,27 @@ sub set_first_access { } else { $res=$symb; } - my $firstaccess=&get_first_access($type,$symb); + $cachedkey=''; + my $firstaccess=&get_first_access($type,$symb,$map); if (!$firstaccess) { - return &put('firstaccesstimes',{"$courseid\0$res"=>time},$udom,$uname); + my $start = time; + my $putres = &put('firstaccesstimes',{"$courseid\0$res"=>$start}, + $udom,$uname); + if ($putres eq 'ok') { + &put('timerinterval',{"$courseid\0$res"=>$interval}, + $udom,$uname); + &appenv( + { + 'course.'.$courseid.'.firstaccess.'.$res => $start, + 'course.'.$courseid.'.timerinterval.'.$res => $interval, + } + ); + } + return $putres; } return 'already_set'; } - +} # --------------------------------------------- Set Expire Date for Spreadsheet sub expirespread { @@ -4514,8 +4583,20 @@ sub rolesinit { ($rolesdump =~ /^error:/)) { return \%userroles; } + my %firstaccess = &dump('firstaccesstimes',$domain,$username); + my %timerinterval = &dump('timerinterval',$domain,$username); + my (%coursetimerstarts,%firstaccchk,%firstaccenv, + %coursetimerintervals,%timerintchk,%timerintenv); + foreach my $key (keys(%firstaccess)) { + my ($cid,$rest) = split(/\0/,$key); + $coursetimerstarts{$cid}{$rest} = $firstaccess{$key}; + } + foreach my $key (keys(%timerinterval)) { + my ($cid,$rest) = split(/\0/,$key); + $coursetimerintervals{$cid}{$rest} = $timerinterval{$key}; + } my %allroles=(); - my %allgroups=(); + my %allgroups=(); if ($rolesdump ne '') { foreach my $entry (split(/&/,$rolesdump)) { @@ -4553,6 +4634,27 @@ sub rolesinit { } else { &standard_roleprivs(\%allroles,$trole,$tdomain,$spec,$trest,$area); } + if ($trole ne 'gr') { + my $cid = $tdomain.'_'.$trest; + unless ($firstaccchk{$cid}) { + if (ref($coursetimerstarts{$cid}) eq 'HASH') { + foreach my $item (keys(%{$coursetimerstarts{$cid}})) { + $firstaccenv{'course.'.$cid.'.firstaccess.'.$item} = + $coursetimerstarts{$cid}{$item}; + } + } + $firstaccchk{$cid} = 1; + } + unless ($timerintchk{$cid}) { + if (ref($coursetimerintervals{$cid}) eq 'HASH') { + foreach my $item (keys(%{$coursetimerintervals{$cid}})) { + $timerintenv{'course.'.$cid.'.timerinterval.'.$item} = + $coursetimerintervals{$cid}{$item}; + } + } + $timerintchk{$cid} = 1; + } + } } } } @@ -4561,7 +4663,7 @@ sub rolesinit { $userroles{'user.author'} = $author; $env{'user.adv'}=$adv; } - return \%userroles; + return (\%userroles,\%firstaccenv,\%timerintenv); } sub set_arearole { @@ -5942,7 +6044,12 @@ sub allowed { if ($match) { if ($env{'user.priv.'.$env{'request.role'}.'./'} =~/\Q$priv\E\&([^\:]*)/) { - $thisallowed.=$1; + my @blockers = &has_comm_blocking($priv,$symb,$uri); + if (@blockers > 0) { + $thisallowed = 'B'; + } else { + $thisallowed.=$1; + } } } else { my $refuri = $env{'httpref.'.$orguri} || $env{'httpref.'.$ver_orguri}; @@ -5953,7 +6060,12 @@ sub allowed { $refuri=&declutter($refuri); my ($match) = &is_on_map($refuri); if ($match) { - $thisallowed='F'; + my @blockers = &has_comm_blocking($priv,$symb,$refuri); + if (@blockers > 0) { + $thisallowed = 'B'; + } else { + $thisallowed='F'; + } } } } @@ -6005,7 +6117,17 @@ sub allowed { $statecond=$cond; if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid} =~/\Q$priv\E\&([^\:]*)/) { - $thisallowed.=$1; + my $value = $1; + if ($priv eq 'bre') { + my @blockers = &has_comm_blocking($priv,$symb,$uri); + if (@blockers > 0) { + $thisallowed = 'B'; + } else { + $thisallowed.=$value; + } + } else { + $thisallowed.=$value; + } $checkreferer=0; } } @@ -6033,7 +6155,17 @@ sub allowed { my $refstatecond=$cond; if ($env{'user.priv.'.$env{'request.role'}.'./'.$courseprivid} =~/\Q$priv\E\&([^\:]*)/) { - $thisallowed.=$1; + my $value = $1; + if ($priv eq 'bre') { + my @blockers = &has_comm_blocking($priv,$symb,$refuri); + if (@blockers > 0) { + $thisallowed = 'B'; + } else { + $thisallowed.=$value; + } + } else { + $thisallowed.=$value; + } $uri=$refuri; $statecond=$refstatecond; } @@ -6192,6 +6324,164 @@ sub allowed { } return 'F'; } + +sub get_comm_blocks { + my ($cdom,$cnum) = @_; + if ($cdom eq '' || $cnum eq '') { + return unless ($env{'request.course.id'}); + $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + } + my %commblocks; + my $hashid=$cdom.'_'.$cnum; + my ($blocksref,$cached)=&is_cached_new('comm_block',$hashid); + if ((defined($cached)) && (ref($blocksref) eq 'HASH')) { + %commblocks = %{$blocksref}; + } else { + %commblocks = &Apache::lonnet::dump('comm_block',$cdom,$cnum); + my $cachetime = 600; + &do_cache_new('comm_block',$hashid,\%commblocks,$cachetime); + } + return %commblocks; +} + +sub has_comm_blocking { + my ($priv,$symb,$uri,$blocks) = @_; + return unless ($env{'request.course.id'}); + return unless ($priv eq 'bre'); + return if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/); + my %commblocks; + if (ref($blocks) eq 'HASH') { + %commblocks = %{$blocks}; + } else { + %commblocks = &get_comm_blocks(); + } + return unless (keys(%commblocks) > 0); + if (!$symb) { $symb=&symbread($uri,1); } + my ($map,$resid,undef)=&decode_symb($symb); + my %tocheck = ( + maps => $map, + resources => $symb, + ); + my @blockers; + my $now = time; + my $navmap = Apache::lonnavmaps::navmap->new(); + foreach my $block (keys(%commblocks)) { + if ($block =~ /^(\d+)____(\d+)$/) { + my ($start,$end) = ($1,$2); + if ($start <= $now && $end >= $now) { + if (ref($commblocks{$block}{'blocks'}) eq 'HASH') { + if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') { + if (ref($commblocks{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') { + if ($commblocks{$block}{'blocks'}{'docs'}{'maps'}{$map}) { + unless (grep(/^\Q$block\E$/,@blockers)) { + push(@blockers,$block); + } + } + } + if (ref($commblocks{$block}{'blocks'}{'docs'}{'resources'}) eq 'HASH') { + if ($commblocks{$block}{'blocks'}{'docs'}{'resources'}{$symb}) { + unless (grep(/^\Q$block\E$/,@blockers)) { + push(@blockers,$block); + } + } + } + } + } + } + } elsif ($block =~ /^firstaccess____(.+)$/) { + my $item = $1; + my @to_test; + if (ref($commblocks{$block}{'blocks'}) eq 'HASH') { + if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') { + my $check_interval; + if (&check_docs_block($commblocks{$block}{'blocks'}{'docs'},\%tocheck)) { + my @interval; + my $type = 'map'; + if ($item eq 'course') { + $type = 'course'; + @interval=&EXT("resource.0.interval"); + } else { + if ($item =~ /___\d+___/) { + $type = 'resource'; + @interval=&EXT("resource.0.interval",$item); + if (ref($navmap)) { + my $res = $navmap->getBySymb($item); + push(@to_test,$res); + } + } else { + my $mapsymb = &symbread($item,1); + if ($mapsymb) { + if (ref($navmap)) { + my $mapres = $navmap->getBySymb($mapsymb); + @to_test = $mapres->retrieveResources($mapres,undef,0,1); + foreach my $res (@to_test) { + my $symb = $res->symb(); + next if ($symb eq $mapsymb); + if ($symb ne '') { + @interval=&EXT("resource.0.interval",$symb); + last; + } + } + } + } + } + } + if ($interval[0] =~ /\d+/) { + my $first_access; + if ($type eq 'resource') { + $first_access=&get_first_access($interval[1],$item); + } elsif ($type eq 'map') { + $first_access=&get_first_access($interval[1],undef,$item); + } else { + $first_access=&get_first_access($interval[1]); + } + if ($first_access) { + my $timesup = $first_access+$interval[0]; + if ($timesup > $now) { + foreach my $res (@to_test) { + if ($res->is_problem()) { + if ($res->completable()) { + unless (grep(/^\Q$block\E$/,@blockers)) { + push(@blockers,$block); + } + last; + } + } + } + } + } + } + } + } + } + } + } + return @blockers; +} + +sub check_docs_block { + my ($docsblock,$tocheck) =@_; + if ((ref($docsblock) ne 'HASH') || (ref($tocheck) ne 'HASH')) { + return; + } + if (ref($docsblock->{'maps'}) eq 'HASH') { + if ($tocheck->{'maps'}) { + if ($docsblock->{'maps'}{$tocheck->{'maps'}}) { + return 1; + } + } + } + if (ref($docsblock->{'resources'}) eq 'HASH') { + if ($tocheck->{'resources'}) { + if ($docsblock->{'resources'}{$tocheck->{'resources'}}) { + return 1; + } + } + } + return; +} + # # Removes the versino from a URI and # splits it in to its filename and path to the filename. @@ -9367,6 +9657,10 @@ sub gettitle { } $title=~s/\&colon\;/\:/gs; if ($title) { +# Remember both $symb and $title for dynamic metadata + $accesshash{$symb.'___crstitle'}=$title; + $accesshash{&declutter($map).'___'.&declutter($url).'___usage'}=time; +# Cache this title and then return it return &do_cache_new('title',$key,$title,600); } $urlsymb=$url; @@ -9399,6 +9693,49 @@ sub get_slot { } return $slotinfo{$which}; } + +sub get_reservable_slots { + my ($cnum,$cdom,$uname,$udom) = @_; + my $now = time; + my $reservable_info; + my $key=join("\0",'reservableslots',$cdom,$cnum,$uname,$udom); + if (exists($remembered{$key})) { + $reservable_info = $remembered{$key}; + } else { + my %resv; + ($resv{'now_order'},$resv{'now'},$resv{'future_order'},$resv{'future'}) = + &Apache::loncommon::get_future_slots($cnum,$cdom,$now); + $reservable_info = \%resv; + $remembered{$key} = $reservable_info; + } + return $reservable_info; +} + +sub get_course_slots { + my ($cnum,$cdom) = @_; + my $hashid=$cnum.':'.$cdom; + my ($result,$cached) = &Apache::lonnet::is_cached_new('allslots',$hashid); + if (defined($cached)) { + if (ref($result) eq 'HASH') { + return %{$result}; + } + } else { + my %slots=&Apache::lonnet::dump('slots',$cdom,$cnum); + my ($tmp) = keys(%slots); + if ($tmp !~ /^(con_lost|error|no_such_host)/i) { + &Apache::lonnet::do_cache_new('allslots',$hashid,\%slots,600); + return %slots; + } + } + return; +} + +sub devalidate_slots_cache { + my ($cnum,$cdom)=@_; + my $hashid=$cnum.':'.$cdom; + &devalidate_cache_new('allslots',$hashid); +} + # ------------------------------------------------- Update symbolic store links sub symblist {