--- loncom/lonnet/perl/lonnet.pm 2008/12/21 15:26:50 1.976.2.3 +++ loncom/lonnet/perl/lonnet.pm 2009/09/16 05:59:49 1.1028 @@ -1,7 +1,7 @@ # The LearningOnline Network # TCP networking package # -# $Id: lonnet.pm,v 1.976.2.3 2008/12/21 15:26:50 raeburn Exp $ +# $Id: lonnet.pm,v 1.1028 2009/09/16 05:59:49 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -73,7 +73,8 @@ package Apache::lonnet; use strict; use LWP::UserAgent(); use HTTP::Date; -# use Date::Parse; +use Image::Magick; + use vars qw(%perlvar %spareid %pr %prp $memcache %packagetab $tmpdir $_64bit %env %protocol); @@ -91,12 +92,15 @@ use Time::HiRes qw( gettimeofday tv_inte use Cache::Memcached; use Digest::MD5; use Math::Random; +use File::MMagic; use LONCAPA qw(:DEFAULT :match); use LONCAPA::Configuration; my $readit; my $max_connection_retries = 10; # Or some such value. +my $upload_photo_form = 0; #Variable to check when user upload a photo 0=not 1=true + require Exporter; our @ISA = qw (Exporter); @@ -146,7 +150,8 @@ sub logthis { my $now=time; my $local=localtime($now); if (open(my $fh,">>$execdir/logs/lonnet.log")) { - print $fh "$local ($$): $message\n"; + my $logstring = $local. " ($$): ".$message."\n"; # Keep any \'s in string. + print $fh $logstring; close($fh); } return 1; @@ -177,6 +182,47 @@ sub create_connection { return 0; } +sub get_server_timezone { + my ($cnum,$cdom) = @_; + my $home=&homeserver($cnum,$cdom); + if ($home ne 'no_host') { + my $cachetime = 24*3600; + my ($timezone,$cached)=&is_cached_new('servertimezone',$home); + if (defined($cached)) { + return $timezone; + } else { + my $timezone = &reply('servertimezone',$home); + return &do_cache_new('servertimezone',$home,$timezone,$cachetime); + } + } +} + +sub get_server_loncaparev { + my ($dom,$lonhost) = @_; + if (defined($lonhost)) { + if (!defined(&hostname($lonhost))) { + undef($lonhost); + } + } + if (!defined($lonhost)) { + if (defined(&domain($dom,'primary'))) { + $lonhost=&domain($dom,'primary'); + if ($lonhost eq 'no_host') { + undef($lonhost); + } + } + } + if (defined($lonhost)) { + my $cachetime = 24*3600; + my ($loncaparev,$cached)=&is_cached_new('serverloncaparev',$lonhost); + if (defined($cached)) { + return $loncaparev; + } else { + my $loncaparev = &reply('serverloncaparev',$lonhost); + return &do_cache_new('serverloncaparev',$lonhost,$loncaparev,$cachetime); + } + } +} # -------------------------------------------------- Non-critical communication sub subreply { @@ -508,7 +554,7 @@ sub appenv { # ----------------------------------------------------- Delete from Environment sub delenv { - my $delthis=shift; + my ($delthis,$regexp) = @_; if (($delthis=~/user\.role/) || ($delthis=~/user\.priv/)) { &logthis("WARNING: ". "Attempt to delete from environment ".$delthis); @@ -521,10 +567,17 @@ sub delenv { tie(my %disk_env,'GDBM_File',$env{'user.environment'}, (&GDBM_WRITER()|&GDBM_NOLOCK()),0640)) { foreach my $key (keys(%disk_env)) { - if ($key=~/^$delthis/) { - delete($env{$key}); - delete($disk_env{$key}); - } + if ($regexp) { + if ($key=~/^$delthis/) { + delete($env{$key}); + delete($disk_env{$key}); + } + } else { + if ($key=~/^\Q$delthis\E/) { + delete($env{$key}); + delete($disk_env{$key}); + } + } } untie(%disk_env); } @@ -666,7 +719,12 @@ sub spareserver { if ($protocol{$spare_server} eq 'https') { $protocol = $protocol{$spare_server}; } - $spare_server = $protocol.'://'.&hostname($spare_server); + if (defined($spare_server)) { + my $hostname = &hostname($spare_server); + if (defined($hostname)) { + $spare_server = $protocol.'://'.$hostname; + } + } } return $spare_server; } @@ -901,7 +959,21 @@ sub idput { } } -# ------------------------------------------- get items from domain db files +# ------------------------------dump from db file owned by domainconfig user +sub dump_dom { + my ($namespace,$udom,$regexp,$range)=@_; + if (!$udom) { + $udom=$env{'user.domain'}; + } + my %returnhash; + if ($udom) { + my $uname = &get_domainconfiguser($udom); + %returnhash = &dump($namespace,$udom,$uname,$regexp,$range); + } + return %returnhash; +} + +# ------------------------------------------ get items from domain db files sub get_dom { my ($namespace,$storearr,$udom,$uhome)=@_; @@ -975,30 +1047,71 @@ sub put_dom { } } +# --------------------- newput for items in db file owned by domainconfig user +sub newput_dom { + my ($namespace,$storehash,$udom) = @_; + my $result; + if (!$udom) { + $udom=$env{'user.domain'}; + } + if ($udom) { + my $uname = &get_domainconfiguser($udom); + $result = &newput($namespace,$storehash,$udom,$uname); + } + return $result; +} + +# --------------------- delete for items in db file owned by domainconfig user +sub del_dom { + my ($namespace,$storearr,$udom)=@_; + if (ref($storearr) eq 'ARRAY') { + if (!$udom) { + $udom=$env{'user.domain'}; + } + if ($udom) { + my $uname = &get_domainconfiguser($udom); + return &del($namespace,$storearr,$udom,$uname); + } + } +} + +# ----------------------------------construct domainconfig user for a domain +sub get_domainconfiguser { + my ($udom) = @_; + return $udom.'-domainconfig'; +} + sub retrieve_inst_usertypes { my ($udom) = @_; my (%returnhash,@order); - if (defined(&domain($udom,'primary'))) { - my $uhome=&domain($udom,'primary'); - my $rep=&reply("inst_usertypes:$udom",$uhome); - if ($rep =~ /^(con_lost|error|no_such_host|refused)/) { - &logthis("get_dom failed - $rep returned from $uhome in domain: $udom"); - return (\%returnhash,\@order); - } - my ($hashitems,$orderitems) = split(/:/,$rep); - my @pairs=split(/\&/,$hashitems); - foreach my $item (@pairs) { - my ($key,$value)=split(/=/,$item,2); - $key = &unescape($key); - next if ($key =~ /^error: 2 /); - $returnhash{$key}=&thaw_unescape($value); - } - my @esc_order = split(/\&/,$orderitems); - foreach my $item (@esc_order) { - push(@order,&unescape($item)); - } + my %domdefs = &Apache::lonnet::get_domain_defaults($udom); + if ((ref($domdefs{'inststatustypes'}) eq 'HASH') && + (ref($domdefs{'inststatusorder'}) eq 'ARRAY')) { + %returnhash = %{$domdefs{'inststatustypes'}}; + @order = @{$domdefs{'inststatusorder'}}; } else { - &logthis("get_dom failed - no primary domain server for $udom"); + if (defined(&domain($udom,'primary'))) { + my $uhome=&domain($udom,'primary'); + my $rep=&reply("inst_usertypes:$udom",$uhome); + if ($rep =~ /^(con_lost|error|no_such_host|refused)/) { + &logthis("get_dom failed - $rep returned from $uhome in domain: $udom"); + return (\%returnhash,\@order); + } + my ($hashitems,$orderitems) = split(/:/,$rep); + my @pairs=split(/\&/,$hashitems); + foreach my $item (@pairs) { + my ($key,$value)=split(/=/,$item,2); + $key = &unescape($key); + next if ($key =~ /^error: 2 /); + $returnhash{$key}=&thaw_unescape($value); + } + my @esc_order = split(/\&/,$orderitems); + foreach my $item (@esc_order) { + push(@order,&unescape($item)); + } + } else { + &logthis("get_dom failed - no primary domain server for $udom"); + } } return (\%returnhash,\@order); } @@ -1227,7 +1340,6 @@ sub inst_userrules { sub get_domain_defaults { my ($domain) = @_; my $cachetime = 60*60*24; - my ($defauthtype,$defautharg,$deflang,%deftools); my ($result,$cached)=&is_cached_new('domdefaults',$domain); if (defined($cached)) { if (ref($result) eq 'HASH') { @@ -1236,11 +1348,14 @@ sub get_domain_defaults { } my %domdefaults; my %domconfig = - &Apache::lonnet::get_dom('configuration',['defaults','quotas'],$domain); + &Apache::lonnet::get_dom('configuration',['defaults','quotas', + 'requestcourses','inststatus'],$domain); if (ref($domconfig{'defaults'}) eq 'HASH') { $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'}; $domdefaults{'auth_arg_def'} = $domconfig{'defaults'}{'auth_arg_def'}; + $domdefaults{'timezone_def'} = $domconfig{'defaults'}{'timezone_def'}; + $domdefaults{'datelocale_def'} = $domconfig{'defaults'}{'datelocale_def'}; } else { $domdefaults{'lang_def'} = &domain($domain,'lang_def'); $domdefaults{'auth_def'} = &domain($domain,'auth_def'); @@ -1259,6 +1374,16 @@ sub get_domain_defaults { } } } + if (ref($domconfig{'requestcourses'}) eq 'HASH') { + foreach my $item ('official','unofficial','community') { + $domdefaults{$item} = $domconfig{'requestcourses'}{$item}; + } + } + if (ref($domconfig{'inststatus'}) eq 'HASH') { + foreach my $item ('inststatustypes','inststatusorder') { + $domdefaults{$item} = $domconfig{'inststatus'}{$item}; + } + } &Apache::lonnet::do_cache_new('domdefaults',$domain,\%domdefaults, $cachetime); return %domdefaults; @@ -1589,12 +1714,14 @@ sub userenvironment { } $items=~s/\&$//; my %returnhash=(); - my @answer=split(/\&/, - &reply('get:'.$udom.':'.$unam.':environment:'.$items, - &homeserver($unam,$udom))); - my $i; - for ($i=0;$i<=$#what;$i++) { - $returnhash{$what[$i]}=&unescape($answer[$i]); + my $uhome = &homeserver($unam,$udom); + unless ($uhome eq 'no_host') { + my @answer=split(/\&/, + &reply('get:'.$udom.':'.$unam.':environment:'.$items,$uhome)); + my $i; + for ($i=0;$i<=$#what;$i++) { + $returnhash{$what[$i]}=&unescape($answer[$i]); + } } return %returnhash; } @@ -1790,6 +1917,8 @@ sub ssi_body { if ($filelink=~/^https?\:/) { ($output,$response)=&externalssi($filelink); } else { + $filelink .= $filelink=~/\?/ ? '&' : '?'; + $filelink .= 'inhibitmenu=yes'; ($output,$response)=&ssi($filelink,%form); } $output=~s|//(\s*)?\s||gs; @@ -1833,7 +1962,7 @@ sub ssi { &Apache::lonenc::check_encrypt(\$fn); if (%form) { $request=new HTTP::Request('POST',&absolute_url().$fn); - $request->content(join('&',map { &escape($_).'='.&escape($form{$_}) } keys %form)); + $request->content(join('&',map { &escape($_).'='.&escape($form{$_}) } keys(%form))); } else { $request=new HTTP::Request('GET',&absolute_url().$fn); } @@ -1931,9 +2060,13 @@ sub process_coursefile { print $fh $env{'form.'.$source}; close($fh); if ($parser eq 'parse') { - my $parse_result = &extract_embedded_items($filepath.'/'.$fname,$allfiles,$codebase); - unless ($parse_result eq 'ok') { - &logthis('Failed to parse '.$filepath.'/'.$fname.' for embedded media: '.$parse_result); + my $mm = new File::MMagic; + my $mime_type = $mm->checktype_filename($filepath.'/'.$fname); + if ($mime_type eq 'text/html') { + my $parse_result = &extract_embedded_items($filepath.'/'.$fname,$allfiles,$codebase); + unless ($parse_result eq 'ok') { + &logthis('Failed to parse '.$filepath.'/'.$fname.' for embedded media: '.$parse_result); + } } } $fetchresult= &reply('fetchuserfile:'.$docudom.'/'.$docuname.'/'.$file, @@ -2010,6 +2143,32 @@ sub clean_filename { $fname=~s/\.(\d+)(?=\.)/_$1/g; return $fname; } +#This Function check if a Image max 400px width and height 500px. If not then scale the image down +sub resizeImage { + my($img_url) = @_; + my $ima = Image::Magick->new; + $ima->Read($img_url); + if($ima->Get('width') > 400) + { + my $factor = $ima->Get('width')/400; + $ima->Scale( width=>400, height=>$ima->Get('height')/$factor ); + } + if($ima->Get('height') > 500) + { + my $factor = $ima->Get('height')/500; + $ima->Scale( width=>$ima->Get('width')/$factor, height=>500); + } + + $ima->Write($img_url); +} + +#Wrapper function for userphotoupload +sub userphotoupload +{ + my($formname,$subdir) = @_; + $upload_photo_form = 1; + return &userfileupload($formname,undef,$subdir); +} # --------------- Take an uploaded file and put it into the userfiles directory # input: $formname - the contents of the file are in $env{"form.$formname"} @@ -2069,9 +2228,12 @@ sub userfileupload { close($fh); return $fullpath.'/'.$fname; } - + if ($subdir eq 'scantron') { + $fname = 'scantron_orig_'.$fname; + } else { # Create the directory if not present - $fname="$subdir/$fname"; + $fname="$subdir/$fname"; + } if ($coursedoc) { my $docuname=$env{'course.'.$env{'request.course.id'}.'.num'}; my $docudom=$env{'course.'.$env{'request.course.id'}.'.domain'}; @@ -2110,6 +2272,7 @@ sub finishuserfileupload { $thumbwidth,$thumbheight) = @_; my $path=$docudom.'/'.$docuname.'/'; my $filepath=$perlvar{'lonDocRoot'}; + my ($fnamepath,$file,$fetchthumb); $file=$fname; if ($fname=~m|/|) { @@ -2124,6 +2287,7 @@ sub finishuserfileupload { mkdir($filepath,0777); } } + # Save the file { if (!open(FH,'>'.$filepath.'/'.$file)) { @@ -2137,13 +2301,22 @@ sub finishuserfileupload { return '/adm/notfound.html'; } close(FH); + if($upload_photo_form==1) + { + resizeImage($filepath.'/'.$file); + $upload_photo_form = 0; + } } if ($parser eq 'parse') { - my $parse_result = &extract_embedded_items($filepath.'/'.$file,$allfiles, - $codebase); - unless ($parse_result eq 'ok') { - &logthis('Failed to parse '.$filepath.$file. - ' for embedded media: '.$parse_result); + my $mm = new File::MMagic; + my $mime_type = $mm->checktype_filename($filepath.'/'.$file); + if ($mime_type eq 'text/html') { + my $parse_result = &extract_embedded_items($filepath.'/'.$file, + $allfiles,$codebase); + unless ($parse_result eq 'ok') { + &logthis('Failed to parse '.$filepath.$file. + ' for embedded media: '.$parse_result); + } } } if (($thumbwidth =~ /^\d+$/) && ($thumbheight =~ /^\d+$/)) { @@ -2158,7 +2331,7 @@ sub finishuserfileupload { # Notify homeserver to grep it # - my $docuhome=&homeserver($docuname,$docudom); + my $docuhome=&homeserver($docuname,$docudom); my $fetchresult= &reply('fetchuserfile:'.$path.$file,$docuhome); if ($fetchresult eq 'ok') { if ($fetchthumb) { @@ -2290,21 +2463,21 @@ sub add_filetype { } sub removeuploadedurl { - my ($url)=@_; - my (undef,undef,$udom,$uname,$fname)=split('/',$url,5); + my ($url)=@_; + my (undef,undef,$udom,$uname,$fname)=split('/',$url,5); return &removeuserfile($uname,$udom,$fname); } sub removeuserfile { my ($docuname,$docudom,$fname)=@_; - my $home=&homeserver($docuname,$docudom); + my $home=&homeserver($docuname,$docudom); my $result = &reply("removeuserfile:$docudom/$docuname/$fname",$home); - if ($result eq 'ok') { + if ($result eq 'ok') { if (($fname !~ /\.meta$/) && (&is_portfolio_file($fname))) { my $metafile = $fname.'.meta'; my $metaresult = &removeuserfile($docuname,$docudom,$metafile); my $url = "/uploaded/$docudom/$docuname/$fname"; - my ($file,$group) = (&parse_portfolio_url($url))[3,4]; + my ($file,$group) = (&parse_portfolio_url($url))[3,4]; my $sqlresult = &update_portfolio_table($docuname,$docudom,$file, 'portfolio_metadata',$group, @@ -2447,7 +2620,7 @@ sub flushcourselogs { # Reverse lookup of domain roles (dc, ad, li, sc, au) # my %domrolebuffer = (); - foreach my $entry (keys %domainrolehash) { + foreach my $entry (keys(%domainrolehash)) { my ($role,$uname,$udom,$runame,$rudom,$rsec)=split(/:/,$entry); if ($domrolebuffer{$rudom}) { $domrolebuffer{$rudom}.='&'.&escape($entry). @@ -2602,6 +2775,9 @@ sub courserolelog { $storehash{'section'} = $sec; } &instructor_log($namespace,\%storehash,$delflag,$username,$domain,$cnum,$cdom); + if (($trole ne 'st') || ($sec ne '')) { + &devalidate_cache_new('getcourseroles',$cdom.'_'.$cnum); + } } } return; @@ -2611,6 +2787,7 @@ sub get_course_adv_roles { my ($cid,$codes) = @_; $cid=$env{'request.course.id'} unless (defined($cid)); my %coursehash=&coursedescription($cid); + my $crstype = &Apache::loncommon::course_type($cid); my %nothide=(); foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) { if ($user !~ /:/) { @@ -2623,15 +2800,29 @@ sub get_course_adv_roles { my %dumphash= &dump('nohist_userroles',$coursehash{'domain'},$coursehash{'num'}); my $now=time; - foreach my $entry (keys %dumphash) { + my %privileged; + foreach my $entry (keys(%dumphash)) { my ($tend,$tstart)=split(/\:/,$dumphash{$entry}); if (($tstart) && ($tstart<0)) { next; } if (($tend) && ($tend<$now)) { next; } if (($tstart) && ($now<$tstart)) { next; } my ($role,$username,$domain,$section)=split(/\:/,$entry); if ($username eq '' || $domain eq '') { next; } - if ((&privileged($username,$domain)) && - (!$nothide{$username.':'.$domain})) { next; } + unless (ref($privileged{$domain}) eq 'HASH') { + my %dompersonnel = + &Apache::lonnet::get_domain_roles($domain,['dc'],$now,$now); + $privileged{$domain} = {}; + foreach my $server (keys(%dompersonnel)) { + if (ref($dompersonnel{$server}) eq 'HASH') { + foreach my $user (keys(%{$dompersonnel{$server}})) { + my ($trole,$uname,$udom) = split(/:/,$user); + $privileged{$udom}{$uname} = 1; + } + } + } + } + if ((exists($privileged{$domain}{$username})) && + (!$nothide{$username.':'.$domain})) { next; } if ($role eq 'cr') { next; } if ($codes) { if ($section) { $role .= ':'.$section; } @@ -2641,7 +2832,7 @@ sub get_course_adv_roles { $returnhash{$role}=$username.':'.$domain; } } else { - my $key=&plaintext($role); + my $key=&plaintext($role,$crstype); if ($section) { $key.=' ('.&Apache::lonlocal::mt('Section [_1]',$section).')'; } if ($returnhash{$key}) { $returnhash{$key}.=','.$username.':'.$domain; @@ -2676,6 +2867,7 @@ sub get_my_roles { } my %returnhash=(); my $now=time; + my %privileged; foreach my $entry (keys(%dumphash)) { my ($role,$tend,$tstart); if ($context eq 'userroles') { @@ -2724,9 +2916,32 @@ sub get_my_roles { } } if ($hidepriv) { - if ((&privileged($username,$domain)) && - (!$nothide{$username.':'.$domain})) { - next; + if ($context eq 'userroles') { + if ((&privileged($username,$domain)) && + (!$nothide{$username.':'.$domain})) { + next; + } + } else { + unless (ref($privileged{$domain}) eq 'HASH') { + my %dompersonnel = + &Apache::lonnet::get_domain_roles($domain,['dc'],$now,$now); + $privileged{$domain} = {}; + if (keys(%dompersonnel)) { + foreach my $server (keys(%dompersonnel)) { + if (ref($dompersonnel{$server}) eq 'HASH') { + foreach my $user (keys(%{$dompersonnel{$server}})) { + my ($trole,$uname,$udom) = split(/:/,$user); + $privileged{$udom}{$uname} = $trole; + } + } + } + } + } + if (exists($privileged{$domain}{$username})) { + if (!$nothide{$username.':'.$domain}) { + next; + } + } } } if ($withsec) { @@ -2812,7 +3027,7 @@ sub courseidput { sub courseiddump { my ($domfilter,$descfilter,$sincefilter,$instcodefilter,$ownerfilter, $coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok, - $selfenrollonly,$catfilter,$showhidden,$caller)=@_; + $selfenrollonly,$catfilter,$showhidden,$caller,$cloner,$cc_clone,$cloneonly)=@_; my $as_hash = 1; my %returnhash; if (!$domfilter) { $domfilter=''; } @@ -2831,7 +3046,8 @@ sub courseiddump { ':'.&escape($coursefilter).':'.&escape($typefilter). ':'.&escape($regexp_ok).':'.$as_hash.':'. &escape($selfenrollonly).':'.&escape($catfilter).':'. - $showhidden.':'.$caller,$tryserver); + $showhidden.':'.$caller.':'.&escape($cloner).':'. + &escape($cc_clone).':'.$cloneonly,$tryserver); my @pairs=split(/\&/,$rep); foreach my $item (@pairs) { my ($key,$value)=split(/\=/,$item,2); @@ -2846,7 +3062,7 @@ sub courseiddump { for (my $i=0; $i<@responses; $i++) { $returnhash{$key}{$items[$i]} = &unescape($responses[$i]); } - } + } } } } @@ -2886,10 +3102,10 @@ sub dcmaildump { sub get_domain_roles { my ($dom,$roles,$startdate,$enddate)=@_; - if (undef($startdate) || $startdate eq '') { + if ((!defined($startdate)) || ($startdate eq '')) { $startdate = '.'; } - if (undef($enddate) || $enddate eq '') { + if ((!defined($enddate)) || ($enddate eq '')) { $enddate = '.'; } my $rolelist; @@ -3296,7 +3512,7 @@ sub tmpreset { if (tie(%hash,'GDBM_File', $path.'/tmpstore_'.$stuname.'_'.$domain.'_'.$namespace.'.db', &GDBM_WRCREAT(),0640)) { - foreach my $key (keys %hash) { + foreach my $key (keys(%hash)) { if ($key=~ /:$symb/) { delete($hash{$key}); } @@ -3732,7 +3948,7 @@ sub set_userprivs { my $adv=0; my %grouproles = (); if (keys(%{$allgroups}) > 0) { - foreach my $role (keys %{$allroles}) { + foreach my $role (keys(%{$allroles})) { my ($trole,$area,$sec,$extendedarea); if ($role =~ m-^(\w+|cr/$match_domain/$match_username/\w+)\.(/$match_domain/$match_courseid)(/?\w*)\.-) { $trole = $1; @@ -3775,6 +3991,101 @@ sub set_userprivs { return ($author,$adv); } +sub role_status { + my ($rolekey,$then,$refresh,$now,$role,$where,$trolecode,$tstatus,$tstart,$tend) = @_; + my @pwhere = (); + if (exists($env{$rolekey}) && $env{$rolekey} ne '') { + (undef,undef,$$role,@pwhere)=split(/\./,$rolekey); + unless (!defined($$role) || $$role eq '') { + $$where=join('.',@pwhere); + $$trolecode=$$role.'.'.$$where; + ($$tstart,$$tend)=split(/\./,$env{$rolekey}); + $$tstatus='is'; + if ($$tstart && $$tstart>$then) { + $$tstatus='future'; + if ($$tstart && $$tstart>$refresh) { + if ($$tstart<$now) { + if (($$where ne '') && ($$role ne '')) { + my (%allroles,%allgroups,$group_privs); + my %userroles = ( + 'user.role.'.$$role.'.'.$$where => $$tstart.'.'.$$tend + ); + my $spec=$$role.'.'.$$where; + my ($tdummy,$tdomain,$trest)=split(/\//,$$where); + if ($$role eq 'gr') { + my %rolehash = &get('roles',[$$where.'_'.$$role],$env{'user.domain'}, + $env{'user.name'})=@_; + my ($trole) = split('_',$role,1); + (undef,my $group_privs) = split(/\//,$trole); + $group_privs = &unescape($group_privs); + } + if ($$role =~ /^cr\//) { + &custom_roleprivs(\%allroles,$$role,$tdomain,$trest,$spec,$$where); + } elsif ($$role eq 'gr') { + my %rolehash = &get('roles',[$$where.'_'.$$role],$env{'user.domain'}, + $env{'user.name'}); + my $trole = split('_',$rolehash{$$where.'_'.$$role},1); + (undef,my $group_privs) = split(/\//,$trole); + $group_privs = &unescape($group_privs); + &group_roleprivs(\%allgroups,$$where,$group_privs,$$tend,$$tstart); + } else { + &standard_roleprivs(\%allroles,$$role,$tdomain,$spec,$trest,$$where); + } + my ($author,$adv)= &set_userprivs(\%userroles,\%allroles,\%allgroups); + &appenv(\%userroles,[$$role,'cm']); + &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$role); + $$tstatus = 'is'; + } + } + } + } + if ($$tend) { + if ($$tend<$then) { + $$tstatus='expired'; + } elsif ($$tend<$now) { + $$tstatus='will_not'; + } + } + } + } +} + +sub check_adhoc_privs { + my ($cdom,$cnum,$then,$refresh,$now,$checkrole) = @_; + my $cckey = 'user.role.'.$checkrole.'./'.$cdom.'/'.$cnum; + if ($env{$cckey}) { + my ($role,$where,$trolecode,$tstart,$tend,$tremark,$tstatus,$tpstart,$tpend); + &role_status($cckey,$then,$refresh,$now,\$role,\$where,\$trolecode,\$tstatus,\$tstart,\$tend); + unless (($tstatus eq 'is') || ($tstatus eq 'will_not')) { + &set_adhoc_privileges($cdom,$cnum,$checkrole); + } + } else { + &set_adhoc_privileges($cdom,$cnum,$checkrole); + } +} + +sub set_adhoc_privileges { +# role can be cc or ca + my ($dcdom,$pickedcourse,$role) = @_; + my $area = '/'.$dcdom.'/'.$pickedcourse; + my $spec = $role.'.'.$area; + my %userroles = &set_arearole($role,$area,'','',$env{'user.domain'}, + $env{'user.name'}); + my %ccrole = (); + &standard_roleprivs(\%ccrole,$role,$dcdom,$spec,$pickedcourse,$area); + my ($author,$adv)= &set_userprivs(\%userroles,\%ccrole); + &appenv(\%userroles,[$role,'cm']); + &log($env{'user.domain'},$env{'user.name'},$env{'user.home'},"Role ".$role); + &appenv( {'request.role' => $spec, + 'request.role.domain' => $dcdom, + 'request.course.sec' => '' + } + ); + my $tadv=0; + if (&allowed('adv') eq 'F') { $tadv=1; } + &appenv({'request.role.adv' => $tadv}); +} + # --------------------------------------------------------------- get interface sub get { @@ -3810,11 +4121,11 @@ sub del { foreach my $item (@$storearr) { $items.=&escape($item).'&'; } + $items=~s/\&$//; if (!$udomain) { $udomain=$env{'user.domain'}; } if (!$uname) { $uname=$env{'user.name'}; } my $uhome=&homeserver($uname,$udomain); - return &reply("del:$udomain:$uname:$namespace:$items",$uhome); } @@ -4375,13 +4686,24 @@ sub is_portfolio_file { } sub usertools_access { - my ($uname,$udom,$tool,$action) = @_; - my $access; - my %tools = ( - aboutme => 1, - blog => 1, - portfolio => 1, - ); + my ($uname,$udom,$tool,$action,$context) = @_; + my ($access,%tools); + if ($context eq '') { + $context = 'tools'; + } + if ($context eq 'requestcourses') { + %tools = ( + official => 1, + unofficial => 1, + community => 1, + ); + } else { + %tools = ( + aboutme => 1, + blog => 1, + portfolio => 1, + ); + } return if (!defined($tools{$tool})); if ((!defined($udom)) || (!defined($uname))) { @@ -4391,18 +4713,23 @@ sub usertools_access { if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) { if ($action ne 'reload') { - return $env{'environment.availabletools.'.$tool}; + if ($context eq 'requestcourses') { + return $env{'environment.canrequest.'.$tool}; + } else { + return $env{'environment.availabletools.'.$tool}; + } } } my ($toolstatus,$inststatus); - if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) { - $toolstatus = $env{'environment.tools.'.$tool}; + if (($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'}) && + ($action ne 'reload')) { + $toolstatus = $env{'environment.'.$context.'.'.$tool}; $inststatus = $env{'environment.inststatus'}; } else { - my %userenv = &userenvironment($udom,$uname,'tools.'.$tool); - $toolstatus = $userenv{'tools.'.$tool}; + my %userenv = &userenvironment($udom,$uname,$context.'.'.$tool,'inststatus'); + $toolstatus = $userenv{$context.'.'.$tool}; $inststatus = $userenv{'inststatus'}; } @@ -4458,7 +4785,11 @@ sub usertools_access { } } } else { - $access = 1; + if ($context eq 'tools') { + $access = 1; + } else { + $access = 0; + } return $access; } } @@ -4812,7 +5143,7 @@ sub allowed { my $envkey; if ($thisallowed=~/L/) { - foreach $envkey (keys %env) { + foreach $envkey (keys(%env)) { if ($envkey=~/^user\.role\.(st|ta)\.([^\.]*)/) { my $courseid=$2; my $roleid=$1.'.'.$2; @@ -5103,7 +5434,7 @@ sub fetch_enrollment_query { } my $host=&hostname($homeserver); my $cmd = ''; - foreach my $affiliate (keys %{$affiliatesref}) { + foreach my $affiliate (keys(%{$affiliatesref})) { $cmd .= $affiliate.'='.join(",",@{$$affiliatesref{$affiliate}}).'%%'; } $cmd =~ s/%%$//; @@ -5236,11 +5567,21 @@ sub auto_run { sub auto_get_sections { my ($cnum,$cdom,$inst_coursecode) = @_; - my $homeserver = &homeserver($cnum,$cdom); - my @secs = (); - my $response=&unescape(&reply('autogetsections:'.$inst_coursecode.':'.$cdom,$homeserver)); - unless ($response eq 'refused') { - @secs = split(/:/,$response); + my $homeserver; + if (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/)) { + $homeserver = &homeserver($cnum,$cdom); + } + if (!defined($homeserver)) { + if ($cdom =~ /^$match_domain$/) { + $homeserver = &domain($cdom,'primary'); + } + } + my @secs; + if (defined($homeserver)) { + my $response=&unescape(&reply('autogetsections:'.$inst_coursecode.':'.$cdom,$homeserver)); + unless ($response eq 'refused') { + @secs = split(/:/,$response); + } } return @secs; } @@ -5259,6 +5600,23 @@ sub auto_validate_courseID { return $response; } +sub auto_validate_instcode { + my ($cnum,$cdom,$instcode,$owner) = @_; + my ($homeserver,$response); + if (($cdom =~ /^$match_domain$/) && ($cnum =~ /^$match_courseid$/)) { + $homeserver = &homeserver($cnum,$cdom); + } + if (!defined($homeserver)) { + if ($cdom =~ /^$match_domain$/) { + $homeserver = &domain($cdom,'primary'); + } + } + my $response=&unescape(&reply('autovalidateinstcode:'.$cdom.':'. + &escape($instcode).':'.&escape($owner),$homeserver)); + my ($outcome,$description) = map { &unescape($_); } split('&',$response,2); + return ($outcome,$description); +} + sub auto_create_password { my ($cnum,$cdom,$authparam,$udom) = @_; my ($homeserver,$response); @@ -5373,6 +5731,13 @@ sub auto_instcode_format { push(@homeservers,$tryserver); } } + } elsif ($caller eq 'requests') { + if ($codedom =~ /^$match_domain$/) { + my $chome = &domain($codedom,'primary'); + unless ($chome eq 'no_host') { + push(@homeservers,$chome); + } + } } else { push(@homeservers,&homeserver($caller,$codedom)); } @@ -5430,7 +5795,81 @@ sub auto_instcode_defaults { } return $response; -} +} + +sub auto_possible_instcodes { + my ($domain,$codetitles,$cat_titles,$cat_orders,$code_order) = @_; + unless ((ref($codetitles) eq 'ARRAY') && (ref($cat_titles) eq 'HASH') && + (ref($cat_orders) eq 'HASH') && (ref($code_order) eq 'ARRAY')) { + return; + } + my (@homeservers,$uhome); + if (defined(&domain($domain,'primary'))) { + $uhome=&domain($domain,'primary'); + push(@homeservers,&domain($domain,'primary')); + } else { + my %servers = &get_servers($domain,'library'); + foreach my $tryserver (keys(%servers)) { + if (!grep(/^\Q$tryserver\E$/,@homeservers)) { + push(@homeservers,$tryserver); + } + } + } + my $response; + foreach my $server (@homeservers) { + $response=&reply('autopossibleinstcodes:'.$domain,$server); + next if ($response =~ /(con_lost|error|no_such_host|refused)/); + my ($codetitlestr,$codeorderstr,$cat_title,$cat_order) = + split(':',$response); + @{$codetitles} = map { &unescape($_); } (split('&',$codetitlestr)); + @{$code_order} = map { &unescape($_); } (split('&',$codeorderstr)); + foreach my $item (split('&',$cat_title)) { + my ($name,$value)=split('=',$item); + $cat_titles->{&unescape($name)}=&thaw_unescape($value); + } + foreach my $item (split('&',$cat_order)) { + my ($name,$value)=split('=',$item); + $cat_orders->{&unescape($name)}=&thaw_unescape($value); + } + return 'ok'; + } + return $response; +} + +sub auto_courserequest_checks { + my ($dom) = @_; + my ($homeserver,%validations); + if ($dom =~ /^$match_domain$/) { + $homeserver = &domain($dom,'primary'); + } + unless ($homeserver eq 'no_host') { + my $response=&reply('autocrsreqchecks:'.$dom,$homeserver); + unless ($response =~ /(con_lost|error|no_such_host|refused)/) { + my @items = split(/&/,$response); + foreach my $item (@items) { + my ($key,$value) = split('=',$item); + $validations{&unescape($key)} = &thaw_unescape($value); + } + } + } + return %validations; +} + +sub auto_courserequest_validation { + my ($dom,$owner,$crstype,$inststatuslist,$instcode,$instseclist) = @_; + my ($homeserver,$response); + if ($dom =~ /^$match_domain$/) { + $homeserver = &domain($dom,'primary'); + } + unless ($homeserver eq 'no_host') { + + $response=&unescape(&reply('autocrsreqvalidation:'.$dom.':'.&escape($owner). + ':'.&escape($crstype).':'.&escape($inststatuslist). + ':'.&escape($instcode).':'.&escape($instseclist), + $homeserver)); + } + return $response; +} sub auto_validate_class_sec { my ($cdom,$cnum,$owners,$inst_class) = @_; @@ -5583,20 +6022,23 @@ sub devalidate_getgroups_cache { # ------------------------------------------------------------------ Plain Text sub plaintext { - my ($short,$type,$cid) = @_; + my ($short,$type,$cid,$forcedefault) = @_; if ($short =~ /^cr/) { return (split('/',$short))[-1]; } if (!defined($cid)) { $cid = $env{'request.course.id'}; } - if (defined($cid) && defined($env{'course.'.$cid.'.'.$short.'.plaintext'})) { - return &Apache::lonlocal::mt($env{'course.'.$cid.'.'.$short. - '.plaintext'}); + if (defined($cid) && ($env{'course.'.$cid.'.'.$short.'.plaintext'} ne '')) { + unless ($forcedefault) { + my $roletext = $env{'course.'.$cid.'.'.$short.'.plaintext'}; + &Apache::lonlocal::mt_escape(\$roletext); + return &Apache::lonlocal::mt($roletext); + } } my %rolenames = ( - Course => 'std', - Group => 'alt1', + Course => 'std', + Community => 'alt1', ); if (defined($type) && defined($rolenames{$type}) && @@ -5617,10 +6059,27 @@ sub assignrole { my $cwosec=$url; $cwosec=~s/^\/($match_domain)\/($match_courseid)\/.*/$1\/$2/; unless (&allowed('ccr',$cwosec)) { - &logthis('Refused custom assignrole: '. - $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start.' by '. - $env{'user.name'}.' at '.$env{'user.domain'}); - return 'refused'; + my $refused = 1; + if ($context eq 'requestcourses') { + if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) { + if ($role =~ m{^cr/($match_domain)/($match_username)/([^/]+)$}) { + if (($1 eq $env{'user.domain'}) && ($2 eq $env{'user.name'})) { + my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$}); + my %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner')); + if ($crsenv{'internal.courseowner'} eq + $env{'user.name'}.':'.$env{'user.domain'}) { + $refused = ''; + } + } + } + } + } + if ($refused) { + &logthis('Refused custom assignrole: '. + $udom.' '.$uname.' '.$url.' '.$role.' '.$end.' '.$start. + ' by '.$env{'user.name'}.' at '.$env{'user.domain'}); + return 'refused'; + } } $mrole='cr'; } elsif ($role =~ /^gr\//) { @@ -5648,7 +6107,18 @@ sub assignrole { if ($refused) { if (($selfenroll == 1) && ($role eq 'st') && ($udom eq $env{'user.domain'}) && ($uname eq $env{'user.name'})) { $refused = ''; - } else { + } elsif ($context eq 'requestcourses') { + my @possroles = ('st','ta','ep','in','cc'); + if ((grep(/^\Q$role\E$/,@possroles)) && ($env{'user.name'} ne '' && $env{'user.domain'} ne '')) { + my ($cdom,$cnum) = ($cwosec =~ m{^/?($match_domain)/($match_courseid)$}); + my %crsenv = &userenvironment($cdom,$cnum,('internal.courseowner')); + if ($crsenv{'internal.courseowner'} eq + $env{'user.name'}.':'.$env{'user.domain'}) { + $refused = ''; + } + } + } + if ($refused) { &logthis('Refused assignrole: '.$udom.' '.$uname.' '.$url. ' '.$role.' '.$end.' '.$start.' by '. $env{'user.name'}.' at '.$env{'user.domain'}); @@ -5813,7 +6283,21 @@ sub modifyuser { if ($email=~/\@/) { $names{'permanentemail'} = $email; } } if ($uid) { $names{'id'} = $uid; } - if (defined($inststatus)) { $names{'inststatus'} = $inststatus; } + if (defined($inststatus)) { + $names{'inststatus'} = ''; + my ($usertypes,$typesorder) = &retrieve_inst_usertypes($udom); + if (ref($usertypes) eq 'HASH') { + my @okstatuses; + foreach my $item (split(/:/,$inststatus)) { + if (defined($usertypes->{$item})) { + push(@okstatuses,$item); + } + } + if (@okstatuses) { + $names{'inststatus'} = join(':', map { &escape($_); } @okstatuses); + } + } + } my $reply = &put('environment', \%names, $udom,$uname); if ($reply ne 'ok') { return 'error: '.$reply; } my $sqlresult = &update_allusers_table($uname,$udom,\%names); @@ -5835,7 +6319,7 @@ sub modifyuser { sub modifystudent { my ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$usec, $end,$start,$forceid,$desiredhome,$email,$type,$locktype,$cid, - $selfenroll,$context)=@_; + $selfenroll,$context,$inststatus)=@_; if (!$cid) { unless ($cid=$env{'request.course.id'}) { return 'not_in_class'; @@ -5844,7 +6328,7 @@ sub modifystudent { # --------------------------------------------------------------- Make the user my $reply=&modifyuser ($udom,$uname,$uid,$umode,$upass,$first,$middle,$last,$gene,$forceid, - $desiredhome,$email); + $desiredhome,$email,$inststatus); unless ($reply eq 'ok') { return $reply; } # This will cause &modify_student_enrollment to get the uid from the # students environment @@ -5958,28 +6442,57 @@ sub writecoursepref { sub createcourse { my ($udom,$description,$url,$course_server,$nonstandard,$inst_code, - $course_owner,$crstype)=@_; + $course_owner,$crstype,$cnum,$context,$category)=@_; $url=&declutter($url); my $cid=''; - unless (&allowed('ccc',$udom)) { + if ($context eq 'requestcourses') { + my $can_create = 0; + my ($ownername,$ownerdom) = split(':',$course_owner); + if ($udom eq $ownerdom) { + if (&usertools_access($ownername,$ownerdom,$category,undef, + $context)) { + $can_create = 1; + } + } else { + my %userenv = &userenvironment($ownerdom,$ownername,'reqcrsotherdom.'. + $category); + if ($userenv{'reqcrsotherdom.'.$category} ne '') { + my @curr = split(',',$userenv{'reqcrsotherdom.'.$category}); + if (@curr > 0) { + my @options = qw(approval validate autolimit); + my $optregex = join('|',@options); + if (grep(/^\Q$udom\E:($optregex)(=?\d*)$/,@curr)) { + $can_create = 1; + } + } + } + } + if ($can_create) { + unless ($ownername eq $env{'user.name'} && $ownerdom eq $env{'user.domain'}) { + unless (&allowed('ccc',$udom)) { + return 'refused'; + } + } + } else { + return 'refused'; + } + } elsif (!&allowed('ccc',$udom)) { return 'refused'; } -# ------------------------------------------------------------------- Create ID - my $uname=int(1+rand(9)). - ('a'..'z','A'..'Z','0'..'9')[int(rand(62))]. - substr($$.time,0,5).unpack("H8",pack("I32",time)). - unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'}; -# ----------------------------------------------- Make sure that does not exist - my $uhome=&homeserver($uname,$udom,'true'); - unless (($uhome eq '') || ($uhome eq 'no_host')) { - $uname=substr($$.time,0,5).unpack("H8",pack("I32",time)). - unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'}; - $uhome=&homeserver($uname,$udom,'true'); - unless (($uhome eq '') || ($uhome eq 'no_host')) { - return 'error: unable to generate unique course-ID'; - } - } -# ------------------------------------------------ Check supplied server name +# --------------------------------------------------------------- Get Unique ID + my $uname; + if ($cnum =~ /^$match_courseid$/) { + my $chome=&homeserver($cnum,$udom,'true'); + if (($chome eq '') || ($chome eq 'no_host')) { + $uname = $cnum; + } else { + $uname = &generate_coursenum($udom); + } + } else { + $uname = &generate_coursenum($udom); + } + return $uname if ($uname =~ /^error/); +# -------------------------------------------------- Check supplied server name $course_server = $env{'user.homeserver'} if (! defined($course_server)); if (! &is_library($course_server)) { return 'error:bad server name '.$course_server; @@ -5988,7 +6501,7 @@ sub createcourse { my $reply=&reply('encrypt:makeuser:'.$udom.':'.$uname.':none::', $course_server); unless ($reply eq 'ok') { return 'error: '.$reply; } - $uhome=&homeserver($uname,$udom,'true'); + my $uhome=&homeserver($uname,$udom,'true'); if (($uhome eq '') || ($uhome eq 'no_host')) { return 'error: no such course'; } @@ -6029,6 +6542,30 @@ ENDINITMAP return '/'.$udom.'/'.$uname; } +# ------------------------------------------------------------------- Create ID +sub generate_coursenum { + my ($udom) = @_; + my $domdesc = &domain($udom); + return 'error: invalid domain' if ($domdesc eq ''); + my $uname=int(1+rand(9)). + ('a'..'z','A'..'Z','0'..'9')[int(rand(62))]. + substr($$.time,0,5).unpack("H8",pack("I32",time)). + unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'}; +# ----------------------------------------------- Make sure that does not exist + my $uhome=&homeserver($uname,$udom,'true'); + unless (($uhome eq '') || ($uhome eq 'no_host')) { + $uname=int(1+rand(9)). + ('a'..'z','A'..'Z','0'..'9')[int(rand(62))]. + substr($$.time,0,5).unpack("H8",pack("I32",time)). + unpack("H2",pack("I32",int(rand(255)))).$perlvar{'lonHostID'}; + $uhome=&homeserver($uname,$udom,'true'); + unless (($uhome eq '') || ($uhome eq 'no_host')) { + return 'error: unable to generate unique course-ID'; + } + } + return $uname; +} + sub is_course { my ($cdom,$cnum) = @_; my %courses = &courseiddump($cdom,'.',1,'.','.',$cnum,undef, @@ -6039,6 +6576,39 @@ sub is_course { return 0; } +sub store_userdata { + my ($storehash,$datakey,$namespace,$udom,$uname) = @_; + my $result; + if ($datakey ne '') { + if (ref($storehash) eq 'HASH') { + if ($udom eq '' || $uname eq '') { + $udom = $env{'user.domain'}; + $uname = $env{'user.name'}; + } + my $uhome=&homeserver($uname,$udom); + if (($uhome eq '') || ($uhome eq 'no_host')) { + $result = 'error: no_host'; + } else { + $storehash->{'ip'} = $ENV{'REMOTE_ADDR'}; + $storehash->{'host'} = $perlvar{'lonHostID'}; + + my $namevalue=''; + foreach my $key (keys(%{$storehash})) { + $namevalue.=&escape($key).'='.&freeze_escape($$storehash{$key}).'&'; + } + $namevalue=~s/\&$//; + $result = &reply("store:$env{'user.domain'}:$env{'user.name'}:". + "$namespace:$datakey:$namevalue",$uhome); + } + } else { + $result = 'error: data to store was not a hash reference'; + } + } else { + $result= 'error: invalid requestkey'; + } + return $result; +} + # ---------------------------------------------------------- Assign Custom Role sub assigncustomrole { @@ -7528,6 +8098,11 @@ sub devalidate_title_cache { &devalidate_cache_new('title',$key); } +# ------------------------------------------------- Get the title of a course + +sub current_course_title { + return $env{ 'course.' . $env{'request.course.id'} . '.description' }; +} # ------------------------------------------------- Get the title of a resource sub gettitle { @@ -7594,7 +8169,7 @@ sub symblist { if (($env{'request.course.fn'}) && (%newhash)) { if (tie(%hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_WRCREAT(),0640)) { - foreach my $url (keys %newhash) { + foreach my $url (keys(%newhash)) { next if ($url eq 'last_known' && $env{'form.no_update_last_known'}); $hash{declutter($url)}=&encode_symb($mapname, @@ -8932,6 +9507,31 @@ sub get_dns { return %iphost; } + + # + # Given a DNS returns the loncapa host name for that DNS + # + sub host_from_dns { + my ($dns) = @_; + my @hosts; + my $ip; + + if (exists($name_to_ip{$dns})) { + $ip = $name_to_ip{$dns}; + } + if (!$ip) { + $ip = gethostbyname($dns); # Initial translation to IP is in net order. + if (length($ip) == 4) { + $ip = &IO::Socket::inet_ntoa($ip); + } + } + if ($ip) { + @hosts = get_hosts_from_ip($ip); + return $hosts[0]; + } + return undef; + } + } BEGIN { @@ -9213,9 +9813,11 @@ in the user's environment.db and in %env =item * X -B: removes all items from the session -environment file that matches the regular expression in $regexp. The -values are also delted from the current processes %env. +B: removes all items from the session +environment file that begin with $delthis. If the +optional second arg - $regexp - is true, $delthis is treated as a +regular expression, otherwise \Q$delthis\E is used. +The values are also deleted from the current processes %env. =item * get_env_multiple($name) @@ -9312,9 +9914,14 @@ and course level =item * -plaintext($short) : return value in %prp hash (rolesplain.tab); plain text -explanation of a user role term - +plaintext($short,$type,$cid,$forcedefault) : return value in %prp hash +(rolesplain.tab); plain text explanation of a user role term. +$type is Course (default) or Community. +If $forcedefault evaluates to true, text returned will be default +text for $type. Otherwise, if this is a course, the text returned +will be a custom name for the role (if defined in the course's +environment). If no custom name is defined the default is returned. + =item * get_my_roles($uname,$udom,$context,$types,$roles,$roledoms,$withsec) : @@ -9519,7 +10126,11 @@ database) for a course =item * -createcourse($udom,$description,$url) : make/modify course +createcourse($udom,$description,$url,$course_server,$nonstandard,$inst_code,$course_owner,$crstype,$cnum) : make course + +=item * + +generate_coursenum($udom) : get a unique (unused) course number in domain $udom =back @@ -9823,8 +10434,15 @@ dirlist($uri) : return directory list ba spareserver() : find server with least workload from spare.tab + +=item * + +host_from_dns($dns) : Returns the loncapa hostname corresponding to a DNS name or undef +if there is no corresponding loncapa host. + =back + =head2 Apache Request =over 4