--- loncom/interface/lonuserutils.pm 2015/08/05 18:47:21 1.171 +++ loncom/interface/lonuserutils.pm 2022/11/23 02:55:37 1.212 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Utility functions for managing LON-CAPA user accounts # -# $Id: lonuserutils.pm,v 1.171 2015/08/05 18:47:21 raeburn Exp $ +# $Id: lonuserutils.pm,v 1.212 2022/11/23 02:55:37 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -30,12 +30,30 @@ package Apache::lonuserutils; +=pod + +=head1 NAME + +Apache::lonuserutils.pm + +=head1 SYNOPSIS + + Utilities for management of users and custom roles + + Provides subroutines called by loncreateuser.pm + +=head1 OVERVIEW + +=cut + use strict; use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon; +use Apache::loncoursequeueadmin; use Apache::lonlocal; use Apache::longroup; +use HTML::Entities; use LONCAPA qw(:DEFAULT :match); ############################################################### @@ -136,6 +154,357 @@ sub modifyuserrole { return ($userresult,$authresult,$roleresult,$idresult); } +sub role_approval { + my ($dom,$context,$process_by,$notifydc) = @_; + if (ref($process_by) eq 'HASH') { + my %domconfig = &Apache::lonnet::get_dom('configuration',['privacy'],$dom); + if (ref($domconfig{'privacy'}) eq 'HASH') { + if (ref($notifydc) eq 'ARRAY') { + if ($domconfig{'privacy'}{'notify'} ne '') { + @{$notifydc} = split(/,/,$domconfig{'privacy'}{'notify'}); + } + } + if (ref($domconfig{'privacy'}{'approval'}) eq 'HASH') { + my %approvalconf = %{$domconfig{'privacy'}{'approval'}}; + foreach my $key ('instdom','extdom') { + if (ref($approvalconf{$key}) eq 'HASH') { + if (keys(%{$approvalconf{$key}})) { + $process_by->{$key} = $approvalconf{$key}{$context}; + } + } + } + } + } + } + return; +} + +sub get_instdoms { + my ($udom,$instdoms) = @_; + return unless (ref($instdoms) eq 'ARRAY'); + my @intdoms; + my %iphost = &Apache::lonnet::get_iphost(); + my $primary_id = &Apache::lonnet::domain($udom,'primary'); + my $primary_ip = &Apache::lonnet::get_host_ip($primary_id); + if (ref($iphost{$primary_ip}) eq 'ARRAY') { + foreach my $id (@{$iphost{$primary_ip}}) { + my $intdom = &Apache::lonnet::internet_dom($id); + unless(grep(/^\Q$intdom\E$/,@intdoms)) { + push(@intdoms,$intdom); + } + } + } + foreach my $ip (keys(%iphost)) { + if (ref($iphost{$ip}) eq 'ARRAY') { + foreach my $id (@{$iphost{$ip}}) { + my $location = &Apache::lonnet::internet_dom($id); + if ($location) { + if (grep(/^\Q$location\E$/,@intdoms)) { + my $dom = &Apache::lonnet::host_domain($id); + unless (grep(/^\Q$dom\E/,@{$instdoms})) { + push(@{$instdoms},$dom); + } + } + } + } + } + } + return; +} + +sub restricted_dom { + my ($context,$key,$udom,$uname,$role,$start,$end,$cdom,$cnum,$csec,$credits, + $process_by,$instdoms,$got_role_approvals,$got_instdoms,$reject,$pending, + $notifydc) = @_; + return if ($udom eq $cdom); + return unless ((ref($process_by) eq 'HASH') && (ref($instdoms) eq 'HASH') && + (ref($got_role_approvals) eq 'HASH') && (ref($got_instdoms) eq 'HASH') && + (ref($reject) eq 'HASH') && (ref($pending) eq 'HASH') && + (ref($notifydc) eq 'HASH')); + my (%approval,@notify,$gotdata,$skip); + if (ref($got_role_approvals->{$context}) eq 'HASH') { + if ($got_role_approvals->{$context}{$udom}) { + $gotdata = 1; + if (ref($process_by->{$context}{$udom}) eq 'HASH') { + %approval = %{$process_by->{$context}{$udom}}; + } + } + } + unless ($gotdata) { + &role_approval($udom,$context,\%approval,\@notify); + $process_by->{$context} = { + $udom => \%approval, + }; + $got_role_approvals->{$context} = { + $udom => 1, + }; + $notifydc->{$udom} = \@notify; + } + if (ref($process_by->{$context}) eq 'HASH') { + if (ref($process_by->{$context}{$udom}) eq 'HASH') { + my @inst; + if ($got_instdoms->{$udom}) { + if (ref($instdoms->{$udom}) eq 'ARRAY') { + @inst = @{$instdoms->{$udom}}; + } + } else { + &get_instdoms(\@inst); + $instdoms->{$udom} = \@inst; + $got_instdoms->{$udom} = 1; + } + if (grep(/^\Q$cdom\E$/,@inst)) { + if (exists($approval{'instdom'})) { + my $rule = $approval{'instdom'}; + if ($rule eq 'none') { + $reject->{$key} = { + cdom => $cdom, + cnum => $cnum, + csec => $csec, + udom => $udom, + uname => $uname, + role => $role, + }; + $skip = 1; + } elsif (($rule eq 'user') || ($rule eq 'domain')) { + $pending->{$key} = { + cdom => $cdom, + cnum => $cnum, + csec => $csec, + udom => $udom, + uname => $uname, + role => $role, + start => $start, + end => $end, + adj => $rule, + }; + if (($role eq 'st') && ($credits ne '')) { + $pending->{$key}->{'credits'} = $credits; + } + $skip = 1; + } + } + } elsif (exists($approval{'extdom'})) { + my $rule = $approval{'extdom'}; + if ($rule eq 'none') { + $reject->{$key} = { + cdom => $cdom, + cnum => $cnum, + csec => $csec, + udom => $udom, + uname => $uname, + role => $role, + }; + $skip = 1; + } elsif (($rule eq 'user') || ($rule eq 'domain')) { + $pending->{$key} = { + cdom => $cdom, + cnum => $cnum, + csec => $csec, + udom => $udom, + uname => $uname, + role => $role, + start => $start, + end => $end, + adj => $rule, + }; + if (($role eq 'st') && ($credits ne '')) { + $pending->{$key}->{'credits'} = $credits; + } + $skip = 1; + } + } + } + } + return $skip; +} + +sub print_roles_rejected { + my ($context,$reject) = @_; + return unless (ref($reject) eq 'HASH'); + my $output; + if (keys(%{$reject}) > 0) { + $output = '

'. + &mt("The following roles could not be assigned because the user is from another domain, and that domain's policies disallow it").'

'; + } + return $output; +} + +sub print_roles_queued { + my ($context,$pending,$notifydc) = @_; + return unless ((ref($pending) eq 'HASH') && (ref($notifydc) eq 'HASH')); + my $output; + if (keys(%{$pending}) > 0) { + my $now = time; + $output = '

'. + &mt("The following role assignments have been queued because the user is from another domain, and that domain's policies require approval by the user themselves or by a domain coordinator in that domain").'

'; + if (keys(%touser)) { + foreach my $key (keys(%touser)) { + my ($uname,$udom) = split(/:/,$touser{$key}); + if (&Apache::lonnet::put('queuedrolereqs',$touser{$key},$udom,$uname) eq 'ok') { + my $owndomdesc = &Apache::lonnet::domain($udom); + &Apache::loncoursequeueadmin::send_selfserve_notification($uname.':'.$udom, + '','',$owndomdesc,$now,'othdomroleuser',$requester); + } + } + } + if (keys(%todom)) { + foreach my $dom (keys(%todom)) { + if (ref($todom{$dom}) eq 'HASH') { + my $confname = &Apache::lonnet::get_domainconfiguser($dom); + if (&Apache::lonnet::put('queuedrolereqs',$todom{$dom},$dom,$confname) eq 'ok') { + if (ref($notifydc->{$dom}) eq 'ARRAY') { + if (@{$notifydc->{$dom}} > 0) { + my $notifylist = join(',',@{$notifydc->{$dom}}); + &Apache::loncoursequeueadmin::send_selfserve_notification($notifylist, + '','','',$now,'othdomroledc',$requester); + } + } + } + } + } + } + if (keys(%crsqueue)) { + foreach my $key (keys(%crsqueue)) { + my ($cdom,$cnum) = split(/_/,$key); + if (ref($crsqueue{$key}) eq 'HASH') { + &Apache::lonnet::put('othdomqueued',$crsqueue{$key},$cdom,$cnum); + } + } + } + if (keys(%caqueue)) { + foreach my $key (keys(%caqueue)) { + my ($auname,$audom) = split(/:/,$key); + if (ref($caqueue{$key}) eq 'HASH') { + &Apache::lonnet::put('othdomqueued',$caqueue{$key},$audom,$auname); + } + } + } + if (keys(%domqueue)) { + my $confname = &Apache::lonnet::get_domainconfiguser($env{'request.role.domain'}); + &Apache::lonnet::put('othdomqueued',\%domqueue,$env{'request.role.domain'},$confname); + } + } + return $output; +} + sub propagate_id_change { my ($uname,$udom,$user) = @_; my (@types,@roles); @@ -421,7 +790,7 @@ sub javascript_validations { } elsif ($context eq 'domain') { $setsection_call = 'setCourse()'; $setsections_js = &dc_setcourse_js($param{'formname'},$mode, - $context,$showcredits); + $context,$showcredits,$domain); } $finish = " var checkSec = $setsection_call\n". " if (checkSec == 'ok') {\n". @@ -514,21 +883,28 @@ END /* regexp here to check for non \d \. in credits */ END } else { + my ($numrules,$intargjs) = + &Apache::loncommon::passwd_validation_js('vf.elements[current.argfield].value',$domain); $auth_checks .= (< 0) { +$intargjs + } } END } @@ -624,6 +1004,7 @@ END $section_checks.$authheader; return $result; } + ############################################################### ############################################################### sub upload_manager_javascript_forward_associate { @@ -881,6 +1262,7 @@ sub print_upload_manager_footer { my $krbform = &Apache::loncommon::authform_kerberos(%param); my $intform = &Apache::loncommon::authform_internal(%param); my $locform = &Apache::loncommon::authform_local(%param); + my $ltiform = &Apache::loncommon::authform_lti(%param); my $date_table = &date_setting_table(undef,undef,$context,undef, $formname,$permission,$crstype); @@ -909,7 +1291,7 @@ sub print_upload_manager_footer { &Apache::loncommon::help_open_topic('Auth_Options'). "

\n"; } - $Str .= &set_login($defdom,$krbform,$intform,$locform); + $Str .= &set_login($defdom,$krbform,$intform,$locform,$ltiform); my ($home_server_pick,$numlib) = &Apache::loncommon::home_server_form_item($defdom,'lcserver', @@ -926,8 +1308,14 @@ sub print_upload_manager_footer { &Apache::lonhtmlcommon::row_closure(); } + my ($trusted,$untrusted); + if ($context eq 'course') { + ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('enroll',$defdom); + } elsif ($context eq 'author') { + ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('othcoau',$defdom); + } $Str .= &Apache::lonhtmlcommon::row_title(&mt('Default domain')) - .&Apache::loncommon::select_dom_form($defdom,'defaultdomain',undef,1) + .&Apache::loncommon::select_dom_form($defdom,'defaultdomain',undef,1,undef,$trusted,$untrusted) .&Apache::lonhtmlcommon::row_closure(); $Str .= &Apache::lonhtmlcommon::row_title(&mt('Starting and Ending Dates')) @@ -1104,8 +1492,15 @@ sub print_upload_manager_form { if (!$env{'form.datatoken'}) { $datatoken=&Apache::loncommon::upfile_store($r); } else { - $datatoken=$env{'form.datatoken'}; - &Apache::loncommon::load_tmp_file($r); + $datatoken=&Apache::loncommon::valid_datatoken($env{'form.datatoken'}); + if ($datatoken ne '') { + &Apache::loncommon::load_tmp_file($r,$datatoken); + } + } + if ($datatoken eq '') { + $r->print('

'.&mt('Error').': '. + &mt('Invalid datatoken').'

'); + return 'missingdata'; } my @records=&Apache::loncommon::upfile_record_sep(); if($env{'form.noFirstLine'}){ @@ -1189,6 +1584,7 @@ sub print_upload_manager_form { } &print_upload_manager_footer($r,$i,$keyfields,$defdom,$today,$halfyear, $context,$permission,$crstype,$showcredits); + return 'ok'; } sub setup_date_selectors { @@ -1530,16 +1926,17 @@ sub curr_role_permissions { # ======================================================= Existing Custom Roles sub my_custom_roles { - my ($crstype) = @_; + my ($crstype,$udom,$uname) = @_; my %returnhash=(); my $extra = &Apache::lonnet::freeze_escape({'skipcheck' => 1}); - my %rolehash=&Apache::lonnet::dump('roles'); + my %rolehash=&Apache::lonnet::dump('roles',$udom,$uname); foreach my $key (keys(%rolehash)) { if ($key=~/^rolesdef\_(\w+)$/) { + my $role = $1; if ($crstype eq 'Community') { next if ($rolehash{$key} =~ /bre\&S/); } - $returnhash{$1}=$1; + $returnhash{$role}=$role; } } return %returnhash; @@ -2262,7 +2659,6 @@ sub build_user_record { sub courses_selector { my ($cdom,$formname) = @_; - my %coursecodes = (); my %codes = (); my @codetitles = (); my %cat_titles = (); @@ -2275,14 +2671,15 @@ sub courses_selector { my $jscript = ''; my $totcodes = 0; - $totcodes = - &Apache::courseclassifier::retrieve_instcodes(\%coursecodes, - $cdom,$totcodes); - if ($totcodes > 0) { - $format_reply = - &Apache::lonnet::auto_instcode_format($caller,$cdom,\%coursecodes, - \%codes,\@codetitles,\%cat_titles,\%cat_order); - if ($format_reply eq 'ok') { + my $instcats = &Apache::lonnet::get_dom_instcats($cdom); + if (ref($instcats) eq 'HASH') { + if ((ref($instcats->{'codetitles'}) eq 'ARRAY') && (ref($instcats->{'codes'}) eq 'HASH') && + (ref($instcats->{'cat_titles'}) eq 'HASH') && (ref($instcats->{'cat_order'}) eq 'HASH')) { + %codes = %{$instcats->{'codes'}}; + @codetitles = @{$instcats->{'codetitles'}}; + %cat_titles = %{$instcats->{'cat_titles'}}; + %cat_order = %{$instcats->{'cat_order'}}; + $totcodes = scalar(keys(%codes)); my $numtypes = @codetitles; &Apache::courseclassifier::build_code_selections(\%codes,\@codetitles,\%cat_titles,\%cat_order,\%idlist,\%idnums,\%idlist_titles); my ($scripttext,$longtitles) = &Apache::courseclassifier::javascript_definitions(\@codetitles,\%idlist,\%idlist_titles,\%idnums,\%cat_titles); @@ -2290,7 +2687,7 @@ sub courses_selector { my $allidlist = $idlist{$codetitles[0]}; $jscript .= &Apache::courseclassifier::courseset_js_start($formname,$longtitles_str,$allidlist); $jscript .= $scripttext; - $jscript .= &Apache::courseclassifier::javascript_code_selections($formname,@codetitles); + $jscript .= &Apache::courseclassifier::javascript_code_selections($formname,\@codetitles); } } my $cb_jscript = &Apache::loncommon::coursebrowser_javascript($cdom); @@ -2319,7 +2716,8 @@ function setCourseCat(formname) { } courseSet('$codetitles[1]'); for (var j=0; j{'photo'} = &Apache::loncoursedata::CL_PHOTO(); $index->{'thumbnail'} = &Apache::loncoursedata::CL_THUMBNAIL(); $index->{'credits'} = &Apache::loncoursedata::CL_CREDITS(); + $index->{'instsec'} = &Apache::loncoursedata::CL_INSTSEC(); $index->{'authorquota'} = &Apache::loncoursedata::CL_AUTHORQUOTA(); $index->{'authorusage'} = &Apache::loncoursedata::CL_AUTHORUSAGE(); foreach my $key (keys(%{$index})) { @@ -2545,14 +2944,22 @@ $verify_action_js function username_display_launch(username,domain) { var target; - for (var i=0; i "Open in a new window", 'modify' => "Modify a user's information", 'track' => "View a user's recent activity", + 'activity' => "View a user's access log", ); my %lt = (%coltxt,%acttxt); my $rolefilter = $env{'form.showrole'}; @@ -2697,12 +3105,17 @@ END if ($permission->{'cusr'}) { unshift (@linkdests,'modify'); } - if (&Apache::lonnet::allowed('vsa', $env{'request.course.id'}) || - &Apache::lonnet::allowed('vsa', $env{'request.course.id'}.'/'. - $env{'request.course.sec'})) { - push(@linkdests,'track'); + if ($context eq 'course') { + if (&Apache::lonnet::allowed('vsa', $env{'request.course.id'}) || + &Apache::lonnet::allowed('vsa', $env{'request.course.id'}.'/'. + $env{'request.course.sec'})) { + push(@linkdests,'track'); + } + } elsif ($context eq 'domain') { + if (&Apache::lonnet::allowed('vac',$env{'request.role.domain'})) { + push(@linkdests,'activity'); + } } - $output .= ''; my $usernamelink = $env{'form.usernamelink'}; if ($usernamelink eq '') { @@ -2725,7 +3138,7 @@ END .''.$lt{'owin'} .''; } - $output .= "\n".'
'."\n". + $output .= "\n".'
'."\n". &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(); if ($mode eq 'autoenroll') { @@ -2971,6 +3384,12 @@ END } (keys(%$userlist)); } my $rowcount = 0; + my $disabled; + if ($mode eq 'autoenroll') { + unless ($permission->{'cusr'}) { + $disabled = ' disabled="disabled"'; + } + } foreach my $user (@sorted_users) { my %in; my $sdata = $userlist->{$user}; @@ -3007,16 +3426,16 @@ END if ($mode eq 'autoenroll') { my $cellentry; if ($in{'type'} eq 'auto') { - $cellentry = ''.&mt('auto').' '; + $cellentry = ''.&mt('auto').' '; $autocount ++; } else { - $cellentry = ''); + 'actionlist" value="'. + &HTML::Entities::encode($checkval,'&<>"').'" />'); + foreach my $item ('start','end') { + $r->print(''); + } + $r->print(''); } else { $r->print(''); } @@ -3076,8 +3503,6 @@ END foreach my $item (@cols) { if ($item eq 'username') { $r->print(''); - } elsif (($item eq 'start' || $item eq 'end') && ($actionselect)) { - $r->print(''."\n"); } elsif ($item eq 'status') { my $showitem = $in{$item}; if (defined($ltstatus{$in{$item}})) { @@ -3468,6 +3893,8 @@ END setSections(formname,'$crstype'); if (seccheck == 'ok') { opener.document.$callingform.newsecs.value = formname.sections.value; + } else { + return; } END } else { @@ -4052,7 +4479,7 @@ sub print_first_users_upload_form { .&Apache::lonhtmlcommon::end_pick_box(); $str .= '

' - .'' .'

'; @@ -4063,7 +4490,10 @@ sub print_first_users_upload_form { # ================================================= Drop/Add from uploaded file sub upfile_drop_add { my ($r,$context,$permission,$showcredits) = @_; - &Apache::loncommon::load_tmp_file($r); + my $datatoken = &Apache::loncommon::valid_datatoken($env{'form.datatoken'}); + if ($datatoken ne '') { + &Apache::loncommon::load_tmp_file($r,$datatoken); + } my @userdata=&Apache::loncommon::upfile_record_sep(); if($env{'form.noFirstLine'}){shift(@userdata);} my @keyfields = split(/\,/,$env{'form.keyfields'}); @@ -4077,10 +4507,6 @@ sub upfile_drop_add { $fields{$env{'form.f'.$i}}=$keyfields[$i]; } } - if ($env{'form.fullup'} ne 'yes') { - $r->print(''."\n". - ''); - } # # Store the field choices away my @storefields = qw/username names fname mname lname gen id @@ -4094,17 +4520,21 @@ sub upfile_drop_add { $fieldstype{$field.'_choice'} = 'scalar'; } &Apache::loncommon::store_course_settings('enrollment_upload',\%fieldstype); - my ($cid,$crstype,$setting); + my ($cid,$crstype,$setting,$crsdom,$crsnum); if ($context eq 'domain') { $setting = $env{'form.roleaction'}; } if ($env{'request.course.id'} ne '') { $cid = $env{'request.course.id'}; $crstype = &Apache::loncommon::course_type(); + $crsdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + $crsnum = $env{'course.'.$env{'request.course.id'}.'.num'}; } elsif ($setting eq 'course') { if (&Apache::lonnet::is_course($env{'form.dcdomain'},$env{'form.dccourse'})) { $cid = $env{'form.dcdomain'}.'_'.$env{'form.dccourse'}; $crstype = &Apache::loncommon::course_type($cid); + $crsdom = $env{'form.dcdomain'}; + $crsnum = $env{'form.dccourse'}; } } my ($startdate,$enddate) = &get_dates_from_form(); @@ -4115,7 +4545,47 @@ sub upfile_drop_add { my $defdom=$env{'request.role.domain'}; my $domain; if ($env{'form.defaultdomain'} ne '') { - $domain = $env{'form.defaultdomain'}; + if (($context eq 'course') || ($setting eq 'course')) { + if ($env{'form.defaultdomain'} eq $crsdom) { + $domain = $env{'form.defaultdomain'}; + } else { + if (&Apache::lonnet::will_trust('enroll',$crsdom,$env{'form.defaultdomain'})) { + $domain = $env{'form.defaultdomain'}; + } else { + $r->print(''.&mt('Error').': '. + &mt('Enrollment of users not permitted for specified default domain: [_1].', + &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).''); + return 'untrusted'; + } + } + } elsif ($context eq 'author') { + if ($env{'form.defaultdomain'} eq $defdom) { + $domain = $env{'form.defaultdomain'}; + } else { + if ((&Apache::lonnet::will_trust('othcoau',$defdom,$env{'form.defaultdomain'})) && + (&Apache::lonnet::will_trust('coaurem',$env{'form.defaultdomain'},$defdom))) { + $domain = $env{'form.defaultdomain'}; + } else { + $r->print(''.&mt('Error').': '. + &mt('Addition of users not permitted for specified default domain: [_1].', + &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).''); + return 'untrusted'; + } + } + } elsif (($context eq 'domain') && ($setting eq 'domain')) { + if ($env{'form.defaultdomain'} eq $defdom) { + $domain = $env{'form.defaultdomain'}; + } else { + if (&Apache::lonnet::will_trust('domroles',$defdom,$env{'form.defaultdomain'})) { + $domain = $env{'form.defaultdomain'}; + } else { + $r->print(''.&mt('Error').': '. + &mt('Addition of users not permitted for specified default domain: [_1].', + &Apache::lonnet::domain($env{'form.defaultdomain'},'description')).''); + return 'untrusted'; + } + } + } } else { $domain = $defdom; } @@ -4125,10 +4595,9 @@ sub upfile_drop_add { } else { my %home_servers = &Apache::lonnet::get_servers($defdom,'library'); if (! exists($home_servers{$desiredhost})) { - $r->print(''.&mt('Error'). - &mt('Invalid home server specified').''); - $r->print(&Apache::loncommon::end_page()); - return; + $r->print('

'.&mt('Error').': '. + &mt('Invalid home server specified').'

'); + return 'invalidhome'; } } # Determine authentication mechanism @@ -4138,6 +4607,7 @@ sub upfile_drop_add { } my $amode = ''; my $genpwd = ''; + my @genpwdfail; if ($env{'form.login'} eq 'krb') { $amode='krb'; $amode.=$env{'form.krbver'}; @@ -4146,12 +4616,16 @@ sub upfile_drop_add { $amode='internal'; if ((defined($env{'form.intarg'})) && ($env{'form.intarg'})) { $genpwd=$env{'form.intarg'}; + @genpwdfail = + &Apache::loncommon::check_passwd_rules($domain,$genpwd); } } elsif ($env{'form.login'} eq 'loc') { $amode='localauth'; if ((defined($env{'form.locarg'})) && ($env{'form.locarg'})) { $genpwd=$env{'form.locarg'}; } + } elsif ($env{'form.login'} eq 'lti') { + $amode='lti'; } if ($amode =~ /^krb/) { if (! defined($genpwd) || $genpwd eq '') { @@ -4224,10 +4698,14 @@ sub upfile_drop_add { \@statuses,\@poss_roles); &gather_userinfo($context,'view',\%userlist,$indexhash,\%info, \%cstr_roles,$permission); - } } } + if ($datatoken eq '') { + $r->print('

'.&mt('Error').': '. + &mt('Invalid datatoken').'

'); + return 'missingdata'; + } if ( $domain eq &LONCAPA::clean_domain($domain) && ($amode ne '')) { ####################################### @@ -4241,6 +4719,8 @@ sub upfile_drop_add { $r->print('

'.&mt('Adding/Modifying Users')."

\n

\n"); } $r->rflush; + my (%got_role_approvals,%got_instdoms,%process_by,%instdoms, + %pending,%reject,%notifydc); my %counts = ( user => 0, @@ -4297,8 +4777,10 @@ sub upfile_drop_add { my $newuserdom = $env{'request.role.domain'}; map { $cancreate{$_} = &can_create_user($newuserdom,$context,$_); } keys(%longtypes); # Get new users list - my (%existinguser,%userinfo,%disallow,%rulematch,%inst_results,%idinst_results,%alerts,%checkuname); + my (%existinguser,%userinfo,%disallow,%rulematch,%inst_results,%alerts,%checkuname, + %showpasswdrules,$haspasswdmap); my $counter = -1; + my (%willtrust,%trustchecked); foreach my $line (@userdata) { $counter ++; my @secs; @@ -4346,6 +4828,28 @@ sub upfile_drop_add { '"'.$entries{$fields{'domain'}}.'"', $fname,$mname,$lname,$gen); next; + } elsif ($entries{$fields{'domain'}} ne $domain) { + my $possdom = $entries{$fields{'domain'}}; + if ($context eq 'course' || $setting eq 'course') { + unless ($trustchecked{$possdom}) { + $willtrust{$possdom} = &Apache::lonnet::will_trust('enroll',$domain,$possdom); + $trustchecked{$possdom} = 1; + } + } elsif ($context eq 'author') { + unless ($trustchecked{$possdom}) { + $willtrust{$possdom} = &Apache::lonnet::will_trust('othcoau',$domain,$possdom); + } + if ($willtrust{$possdom}) { + $willtrust{$possdom} = &Apache::lonnet::will_trust('coaurem',$possdom,$domain); + } + } + unless ($willtrust{$possdom}) { + $disallow{$counter} = + &mt('Unacceptable domain [_1] for user [_2] [_3] [_4] [_5]', + '"'.$possdom.'"', + $fname,$mname,$lname,$gen); + next; + } } my $username = $entries{$fields{'username'}}; my $userdomain = $entries{$fields{'domain'}}; @@ -4425,12 +4929,44 @@ sub upfile_drop_add { } } # determine user password - my $password = $genpwd; + my $password; + my $passwdfromfile; if (defined($fields{'ipwd'})) { if ($entries{$fields{'ipwd'}}) { $password=$entries{$fields{'ipwd'}}; + $passwdfromfile = 1; + if ($env{'form.login'} eq 'int') { + my $uhome=&Apache::lonnet::homeserver($username,$userdomain); + if (($uhome eq 'no_host') || ($changeauth)) { + my @brokepwdrules = + &Apache::loncommon::check_passwd_rules($domain,$password); + if (@brokepwdrules) { + $disallow{$counter} = &mt('[_1]: Password included in file for this user did not meet requirements.', + ''.$username.''); + map { $showpasswdrules{$_} = 1; } @brokepwdrules; + next; + } + } + } } } + unless ($passwdfromfile) { + if ($env{'form.login'} eq 'int') { + if (@genpwdfail) { + my $uhome=&Apache::lonnet::homeserver($username,$userdomain); + if (($uhome eq 'no_host') || ($changeauth)) { + $disallow{$counter} = &mt('[_1]: No specific password in file for this user; default password did not meet requirements', + ''.$username.''); + unless ($haspasswdmap) { + map { $showpasswdrules{$_} = 1; } @genpwdfail; + $haspasswdmap = 1; + } + } + next; + } + } + $password = $genpwd; + } # determine user role my $role = ''; if (defined($fields{'role'})) { @@ -4498,7 +5034,7 @@ sub upfile_drop_add { &mt('The user does not already exist, and you may not create a new user in a different domain.'); next; } else { - unless ($password || $env{'form.login'} eq 'loc') { + unless (($password ne '') || ($env{'form.login'} eq 'loc') || ($env{'form.login'} eq 'lti')) { $disallow{$counter} = &mt('[_1]: This is a new user but no default password was provided, and the authentication type requires one.', ''.$username.''); @@ -4507,7 +5043,7 @@ sub upfile_drop_add { } $checkid = 1; $newuser = 1; - $checkuname{$username.':'.$newuserdom} = { 'newuser' => 1, 'id' => 1 }; + $checkuname{$username.':'.$newuserdom} = { 'newuser' => $newuser, 'id' => $id }; } else { if ($context eq 'course' || $context eq 'author') { if ($userdomain eq $domain ) { @@ -4566,34 +5102,41 @@ sub upfile_drop_add { } # end of foreach (@userdata) if ($counter > -1) { my $total = $counter + 1; - my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($r,$total); my %checkids; - if (keys(%existinguser)) { - foreach my $dom (keys(%existinguser)) { - if (ref($existinguser{$dom}) eq 'HASH') { - my %idhash = &Apache::lonnet::idrget($dom,keys(%{$existinguser{$dom}})); - foreach my $username (keys(%{$existinguser{$dom}})) { - if ($idhash{$username} ne $existinguser{$dom}{$username}) { - $checkids{$username.':'.$dom} = { 'id' => $existinguser{$dom}{$username} }; + if ((keys(%existinguser)) || (keys(%checkuname))) { + $r->print(&mt('Please be patient -- checking for institutional data ...')); + $r->rflush(); + if (keys(%existinguser)) { + foreach my $dom (keys(%existinguser)) { + if (ref($existinguser{$dom}) eq 'HASH') { + my %idhash = &Apache::lonnet::idrget($dom,keys(%{$existinguser{$dom}})); + foreach my $username (keys(%{$existinguser{$dom}})) { + if ($idhash{$username} ne $existinguser{$dom}{$username}) { + $checkids{$username.':'.$dom} = { + 'id' => $existinguser{$dom}{$username}, + }; + } + } + if (keys(%checkids)) { + &Apache::loncommon::user_rule_check(\%checkids,{ 'id' => 1 }, + \%alerts,\%rulematch, + \%inst_results,\%curr_rules, + \%got_rules); } - } - if (keys(%checkids)) { - &Apache::loncommon::user_rule_check(\%checkids,{ 'id' => 1 }, - \%alerts,\%rulematch, - \%idinst_results,\%curr_rules, - \%got_rules); } } } + if (keys(%checkuname)) { + &Apache::loncommon::user_rule_check(\%checkuname,{ 'username' => 1, 'id' => 1, }, + \%alerts,\%rulematch,\%inst_results, + \%curr_rules,\%got_rules); + } + $r->print(' '.&mt('done').'

'); + $r->rflush(); } - if (keys(%checkuname)) { - &Apache::loncommon::user_rule_check(\%checkuname,{ 'username' => 1 }, - \%alerts,\%rulematch,\%inst_results, - \%curr_rules,\%got_rules); - } + my %prog_state = &Apache::lonhtmlcommon::Create_PrgWin($r,$total); $r->print('

    '); for (my $i=0; $i<=$counter; $i++) { - &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state); if ($disallow{$i}) { $r->print('
  • '.$disallow{$i}.'
  • '); } elsif (ref($userinfo{$i}) eq 'HASH') { @@ -4627,6 +5170,40 @@ sub upfile_drop_add { } } } + if (ref($inst_results{$user}) eq 'HASH') { + if ($inst_results{$user}{'firstname'} ne '') { + $fname = $inst_results{$user}{'firstname'}; + } + if ($inst_results{$user}{'middlename'} ne '') { + $mname = $inst_results{$user}{'middlename'}; + } + if ($inst_results{$user}{'lasttname'} ne '') { + $lname = $inst_results{$user}{'lastname'}; + } + if ($inst_results{$user}{'permanentemail'} ne '') { + $email = $inst_results{$user}{'permanentemail'}; + } + if ($inst_results{$user}{'id'} ne '') { + $id = $inst_results{$user}{'id'}; + $checkid = 0; + } + if (ref($inst_results{$user}{'inststatus'}) eq 'ARRAY') { + $inststatus = join(':',@{$inst_results{$user}{'inststatus'}}); + } + } + if (($checkid) && ($id ne '')) { + if (ref($alerts{'id'}) eq 'HASH') { + if (ref($alerts{'id'}{$userdomain}) eq 'HASH') { + if ($alerts{'id'}{$userdomain}{$username}) { + $r->print('
  • '. + &mt('[_1]: has a student/employee ID matching the format at your institution, but the ID is not found by your directory service.', + ''.$username.'').'
    '. + &mt('Consequently, the user was not created.').'
  • '); + next; + } + } + } + } my $usertype = 'unofficial'; if (ref($rulematch{$user}) eq 'HASH') { if ($rulematch{$user}{'username'}) { @@ -4639,18 +5216,17 @@ sub upfile_drop_add { &mt('[_1]: The user does not exist, and you are not permitted to create users of type: [_2].',''.$username.'',$showtype).''); next; } - } - if ($id ne '') { + } elsif ($id ne '') { if (exists($checkids{$user})) { $checkid = 1; if (ref($alerts{'id'}) eq 'HASH') { if (ref($alerts{'id'}{$userdomain}) eq 'HASH') { - if ($alerts{'id'}{$userdomain}{$id}) { + if ($alerts{'id'}{$userdomain}{$username}) { $r->print('
  • '. - &mt('[_1]: has a student/employee ID matching the format at your institution, but the ID is found by your directory service.', + &mt('[_1]: has a student/employee ID matching the format at your institution, but the ID is not found by your directory service.', ''.$username.'').'
    '. - &mt('Consequently, the user was not created.').'
  • '); - next; + &mt('Consequently, the ID was not changed.').''); + $id = ''; } } } @@ -4661,12 +5237,33 @@ sub upfile_drop_add { my (%userres,%authres,%roleres,%idres); my $singlesec = ''; if ($role eq 'st') { + if (($context eq 'domain') && ($changeauth eq 'Yes') && (!$newuser)) { + if ((&Apache::lonnet::allowed('mau',$userdomain)) && + (&Apache::lonnet::homeserver($username,$userdomain) ne 'no_host')) { + if ((($amode =~ /^krb4|krb5|internal$/) && $password ne '') || + ($amode eq 'localauth')) { + $authresult = + &Apache::lonnet::modifyuserauth($userdomain,$username,$amode,$password); + } + } + } my $sec; if (ref($userinfo{$i}{'sections'}) eq 'ARRAY') { if (@secs > 0) { $sec = $secs[0]; } } + if ($userdomain ne $env{'request.role.domain'}) { + my $item = "/$crsdom/$crsnum" ; + if ($sec ne '') { + $item .= "/$sec"; + } + $item .= '_st'; + next if (&restricted_dom($context,$item,$userdomain,$username,$role,$startdate, + $enddate,$crsdom,$crsnum,$sec,$credits,\%process_by, + \%instdoms,\%got_role_approvals,\%got_instdoms,\%reject, + \%pending,\%notifydc)); + } &modifystudent($userdomain,$username,$cid,$sec, $desiredhost,$context); $roleresult = @@ -4678,16 +5275,33 @@ sub upfile_drop_add { '',$context,$inststatus,$credits); $userresult = $roleresult; } else { - if ($role ne '') { + my $possrole; + if ($role ne '') { if ($context eq 'course' || $setting eq 'course') { if ($customroles{$role}) { $role = 'cr_'.$env{'user.domain'}.'_'. $env{'user.name'}.'_'.$role; } - if (($role ne 'cc') && ($role ne 'co')) { + $possrole = $role; + if ($possrole =~ /^cr_/) { + $possrole =~ s{_}{/}g; + } + if (($role ne 'cc') && ($role ne 'co')) { if (@secs > 1) { $multiple = 1; + my $prefix = "/$crsdom/$crsnum"; foreach my $sec (@secs) { + if ($userdomain ne $env{'request.role.domain'}) { + my $item = $prefix; + if ($sec ne '') { + $item .= "/$sec"; + } + $item .= '_'.$possrole; + next if (&restricted_dom($context,$item,$userdomain,$username,$possrole, + $startdate,$enddate,$crsdom,$crsnum,$sec, + $credits,\%process_by,\%instdoms,\%got_role_approvals, + \%got_instdoms,\%reject,\%pending,\%notifydc)); + } ($userres{$sec},$authres{$sec},$roleres{$sec},$idres{$sec}) = &modifyuserrole($context,$setting, $changeauth,$cid,$userdomain,$username, @@ -4701,17 +5315,29 @@ sub upfile_drop_add { $singlesec = $secs[0]; } } + } else { + $possrole = $role; } - if (!$multiple) { - ($userresult,$authresult,$roleresult,$idresult) = - &modifyuserrole($context,$setting, - $changeauth,$cid,$userdomain,$username, - $id,$amode,$password,$fname, - $mname,$lname,$gen,$singlesec, - $env{'form.forceid'},$desiredhost, - $email,$role,$enddate,$startdate, - $checkid,$inststatus); + } + if (!$multiple) { + if (($userdomain ne $env{'request.role.domain'}) && ($role ne '')) { + my $item = "/$crsdom/$crsnum"; + if ($singlesec ne '') { + $item .= "/$singlesec"; + } + $item .= '_'.$possrole; + next if (&restricted_dom($context,$item,$userdomain,$username,$possrole,$startdate,$enddate, + $crsdom,$crsnum,$singlesec,$credits,\%process_by,\%instdoms, + \%got_role_approvals,\%got_instdoms,\%reject,\%pending,\%notifydc)); } + ($userresult,$authresult,$roleresult,$idresult) = + &modifyuserrole($context,$setting, + $changeauth,$cid,$userdomain,$username, + $id,$amode,$password,$fname, + $mname,$lname,$gen,$singlesec, + $env{'form.forceid'},$desiredhost, + $email,$role,$enddate,$startdate, + $checkid,$inststatus); } } if ($multiple) { @@ -4729,8 +5355,9 @@ sub upfile_drop_add { $username,$userdomain,\%userchg); } } - $r->print('
'); + &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,'last user'); } # end of loop + $r->print(''); &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); } # Flush the course logs so reverse user roles immediately updated @@ -4739,7 +5366,9 @@ sub upfile_drop_add { "

\n"); if ($counts{'role'} > 0) { $r->print("

\n". - &mt('Roles added for [quant,_1,user].',$counts{'role'}).' '.&mt('If a user is currently logged-in to LON-CAPA, any new roles which are active will be available when the user next logs in.')."

\n"); + &mt('Roles added for [quant,_1,user].',$counts{'role'}).' '. + &mt('If a user is currently logged-in to LON-CAPA, any new roles which are active will be available when the user next logs in.'). + "

\n"); } else { $r->print('

'.&mt('No roles added').'

'); } @@ -4749,6 +5378,13 @@ sub upfile_drop_add { $counts{'auth'})."

\n"); } $r->print(&print_namespacing_alerts($domain,\%alerts,\%curr_rules)); + $r->print(&passwdrule_alerts($domain,\%showpasswdrules)); + if (keys(%reject)) { + $r->print(&print_roles_rejected($context,\%reject)); + } + if (keys(%pending)) { + $r->print(&print_roles_queued($context,\%pending,\%notifydc)); + } ##################################### # Display list of students to drop # ##################################### @@ -4757,10 +5393,9 @@ sub upfile_drop_add { # Get current classlist my $classlist = &Apache::loncoursedata::get_classlist(); if (! defined($classlist)) { - $r->print(''. - ''. - '

'.&mt('There are no students with current/future access to the course.').'

'. - ''."\n"); + $r->print('

'. + &mt('There are no students with current/future access to the course.'). + '

'."\n"); } elsif (ref($classlist) eq 'HASH') { # Remove the students we just added from the list of students. foreach my $line (@userdata) { @@ -4776,9 +5411,7 @@ sub upfile_drop_add { } } } # end of unless - if ($env{'form.fullup'} ne 'yes') { - $r->print(''); - } + return 'ok'; } sub print_namespacing_alerts { @@ -4821,6 +5454,42 @@ sub print_namespacing_alerts { } } +sub passwdrule_alerts { + my ($domain,$passwdrules) = @_; + my $warning; + if (ref($passwdrules) eq 'HASH') { + my %showrules = %{$passwdrules}; + if (keys(%showrules)) { + my %passwdconf = &Apache::lonnet::get_passwdconf($domain); + $warning = ''.&mt('Password requirement(s) unmet for one or more users:').'
    '; + if ($showrules{'min'}) { + my $min = $passwdconf{'min'}; + if ($min eq '') { + $min = $Apache::lonnet::passwdmin; + } + $warning .= '
  • '.&mt('minimum [quant,_1,character]',$min).'
  • '; + } + if ($showrules{'max'}) { + $warning .= '
  • '.&mt('maximum [quant,_1,character]',$passwdconf{'max'}).'
  • '; + } + if ($showrules{'uc'}) { + $warning .= '
  • '.&mt('contain at least one upper case letter').'
  • '; + } + if ($showrules{'lc'}) { + $warning .= '
  • '.&mt('contain at least one lower case letter').'
  • '; + } + if ($showrules{'num'}) { + $warning .= '
  • '.&mt('contain at least one number').'
  • '; + } + if ($showrules{'spec'}) { + $warning .= '
  • '.&mt('contain at least one non-alphanumeric').'
  • '; + } + $warning .= '
'; + } + } + return $warning; +} + sub user_change_result { my ($r,$userresult,$authresult,$roleresult,$idresult,$counts,$flushc, $username,$userdomain,$userchg) = @_; @@ -4909,7 +5578,8 @@ sub update_user_list { if ($context eq 'course') { $crstype = &Apache::loncommon::course_type(); } - my @changelist; + my (@changelist,%got_role_approvals,%got_instdoms,%process_by,%instdoms, + %pending,%reject,%notifydc); if ($choice eq 'drop') { @changelist = &Apache::loncommon::get_env_multiple('form.droplist'); } else { @@ -4939,7 +5609,7 @@ sub update_user_list { foreach my $item (@changelist) { my ($role,$uname,$udom,$cid,$sec,$scope,$result,$type,$locktype, @sections,$scopestem,$singlesec,$showsecs,$warn_singlesec, - $nothingtodo,$keepnosection,$credits); + $nothingtodo,$keepnosection,$credits,$instsec,$cdom,$cnum); if ($choice eq 'drop') { ($uname,$udom,$sec) = split(/:/,$item,-1); $role = 'st'; @@ -4952,9 +5622,12 @@ sub update_user_list { $scope = $scopestem.'/'.$sec; } } elsif ($context eq 'course') { - ($uname,$udom,$role,$sec,$type,$locktype,$credits) = - split(/\:/,$item); + ($uname,$udom,$role,$sec,$type,$locktype,$credits,$instsec) = + split(/\:/,$item,8); + $instsec = &unescape($instsec); $cid = $env{'request.course.id'}; + $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; $scopestem = '/'.$cid; $scopestem =~s/\_/\//g; if ($sec eq '') { @@ -4965,15 +5638,21 @@ sub update_user_list { } elsif ($context eq 'author') { ($uname,$udom,$role) = split(/\:/,$item,-1); $scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'}; + $cdom = $env{'user.domain'}; + $cnum = $env{'user.name'}; } elsif ($context eq 'domain') { if ($setting eq 'domain') { ($role,$uname,$udom) = split(/\:/,$item,-1); $scope = '/'.$env{'request.role.domain'}.'/'; + $cdom = $env{'request.role.domain'}; } elsif ($setting eq 'author') { ($uname,$udom,$role,$scope) = split(/\:/,$item); + (undef,$cdom,$cnum) = split(/\//,$scope); } elsif ($setting eq 'course') { - ($uname,$udom,$role,$cid,$sec,$type,$locktype,$credits) = - split(/\:/,$item); + ($uname,$udom,$role,$cid,$sec,$type,$locktype,$credits,$instsec) = + split(/\:/,$item,9); + ($cdom,$cnum) = split('_',$cid); + $instsec = &unescape($instsec); $scope = '/'.$cid; $scope =~s/\_/\//g; if ($sec ne '') { @@ -4995,7 +5674,7 @@ sub update_user_list { $end = $now; if ($role eq 'st') { $result = - &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits); + &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec); } else { $result = &Apache::lonnet::revokerole($udom,$uname,$scope,$role, @@ -5003,7 +5682,7 @@ sub update_user_list { } } elsif ($choice eq 'delete') { if ($role eq 'st') { - &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$now,$start,$type,$locktype,$cid,'',$context,$credits); + &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$now,$start,$type,$locktype,$cid,'',$context,$credits,$instsec); } $result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$now, @@ -5014,24 +5693,34 @@ sub update_user_list { $start = $startdate; $end = $enddate; } + my $id = $scope.'_'.$role; if ($choice eq 'reenable') { + next if (&restricted_dom($context,$id,$udom,$uname,$role,$now,$end,$cdom,$cnum, + $sec,$credits,\%process_by,\%instdoms,\%got_role_approvals, + \%got_instdoms,\%reject,\%pending,\%notifydc)); if ($role eq 'st') { - $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits); + $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec); } else { $result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end, $now,'','',$context); } } elsif ($choice eq 'activate') { + next if (&restricted_dom($context,$id,$udom,$uname,$role,$now,$end,$cdom,$cnum, + $sec,$credits,\%process_by,\%instdoms,\%got_role_approvals, + \%got_instdoms,\%reject,\%pending,\%notifydc)); if ($role eq 'st') { - $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits); + $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec); } else { $result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end, $now,'','',$context); } } elsif ($choice eq 'chgdates') { + next if (&restricted_dom($context,$id,$udom,$uname,$role,$start,$end,$cdom,$cnum, + $sec,$credits,\%process_by,\%instdoms,\%got_role_approvals, + \%got_instdoms,\%reject,\%pending,\%notifydc)); if ($role eq 'st') { - $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits); + $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec); } else { $result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end, $start,'','',$context); @@ -5101,7 +5790,7 @@ sub update_user_list { } else { if ($role eq 'st') { $result = - &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,undef,$end,$start,$type,$locktype,$cid,'',$context,$credits); + &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,undef,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec); } else { my $newscope = $scopestem; $result = &Apache::lonnet::assignrole($udom,$uname,$newscope,$role,$end,$start,'','',$context); @@ -5115,7 +5804,7 @@ sub update_user_list { foreach my $newsec (@newsecs) { if (!grep(/^\Q$newsec\E$/,@retained)) { if ($role eq 'st') { - $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$newsec,$end,$start,$type,$locktype,$cid,'',$context,$credits); + $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$newsec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec); if (@newsecs > 1) { my $showsingle; if ($newsec eq '') { @@ -5239,6 +5928,12 @@ sub update_user_list { $r->print(&make_dates_default($startdate,$enddate,$context,$crstype)); } } + if (keys(%reject)) { + $r->print(&print_roles_rejected($context,\%reject)); + } + if (keys(%pending)) { + $r->print(&print_roles_queued($context,\%pending,\%notifydc)); + } my $linktext = &mt('Display User Lists'); if ($choice eq 'drop') { $linktext = &mt('Display current class roster'); @@ -5322,7 +6017,7 @@ END } sub set_login { - my ($dom,$authformkrb,$authformint,$authformloc) = @_; + my ($dom,$authformkrb,$authformint,$authformloc,$authformlti) = @_; my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom); my $response; my ($authnum,%can_assign) = @@ -5344,19 +6039,24 @@ sub set_login { ''. &Apache::loncommon::end_data_table_row()."\n"; } + if ($can_assign{'lti'}) { + $response .= &Apache::loncommon::start_data_table_row(). + ''. + &Apache::loncommon::end_data_table_row()."\n"; + } $response .= &Apache::loncommon::end_data_table(); } return $response; } sub course_sections { - my ($sections_count,$role,$current_sec) = @_; + my ($sections_count,$role,$current_sec,$disabled) = @_; my $output = ''; my @sections = (sort {$a <=> $b} keys(%{$sections_count})); my $numsec = scalar(@sections); my $is_selected = ' selected="selected"'; if ($numsec <= 1) { - $output = ''."\n". ' '."\n"; if ($current_sec eq 'none') { $output .= @@ -5379,7 +6079,7 @@ sub course_sections { my $multiple = 4; if (scalar(@sections) < 4) { $multiple = scalar(@sections); } if ($role eq 'st') { - $output .= '>'."\n". + $output .= $disabled.'>'."\n". ' '."\n"; if ($current_sec eq 'none') { $output .= @@ -5389,7 +6089,7 @@ sub course_sections { ' \n"; } } else { - $output .= 'multiple="multiple" size="'.$multiple.'">'."\n"; + $output .= 'multiple="multiple" size="'.$multiple.'"'.$disabled.'>'."\n"; } foreach my $sec (@sections) { if ($current_sec eq $sec) { @@ -5606,6 +6306,9 @@ sub can_create_user { my $cancreate = 1; if (&Apache::lonnet::allowed('mau',$dom)) { return $cancreate; + } elsif ($context eq 'domain') { + $cancreate = 0; + return $cancreate; } if (ref($domconf{'usercreation'}) eq 'HASH') { if (ref($domconf{'usercreation'}{'cancreate'}) eq 'HASH') { @@ -5685,6 +6388,70 @@ sub can_modify_userinfo { return %canmodify; } +sub can_change_internalpass { + my ($uname,$udom,$crstype,$permission) = @_; + my $canchange; + if (&Apache::lonnet::allowed('mau',$udom)) { + $canchange = 1; + } elsif ((ref($permission) eq 'HASH') && ($permission->{'mip'}) && + ($udom eq $env{'request.role.domain'})) { + unless ($env{'course.'.$env{'request.course.id'}.'.internal.nopasswdchg'}) { + my ($cnum,$cdom) = &get_course_identity(); + if ((&Apache::lonnet::is_course_owner($cdom,$cnum)) && ($udom eq $env{'user.domain'})) { + my @userstatuses = ('default'); + my %userenv = &Apache::lonnet::userenvironment($udom,$uname,'inststatus'); + if ($userenv{'inststatus'} ne '') { + @userstatuses = split(/:/,$userenv{'inststatus'}); + } + my $noupdate = 1; + my %passwdconf = &Apache::lonnet::get_passwdconf($cdom); + if (ref($passwdconf{'crsownerchg'}) eq 'HASH') { + if (ref($passwdconf{'crsownerchg'}{'for'}) eq 'ARRAY') { + foreach my $status (@userstatuses) { + if (grep(/^\Q$status\E$/,@{$passwdconf{'crsownerchg'}{'for'}})) { + undef($noupdate); + last; + } + } + } + } + if ($noupdate) { + return; + } + my %owned = &Apache::lonnet::courseiddump($cdom,'.',1,'.', + $env{'user.name'}.':'.$env{'user.domain'}, + undef,undef,undef,'.'); + my %roleshash = &Apache::lonnet::get_my_roles($uname,$udom,'userroles', + ['active','future']); + foreach my $key (keys(%roleshash)) { + my ($name,$domain,$role) = split(/:/,$key); + if ($role eq 'st') { + next if (($name eq $cnum) && ($domain eq $cdom)); + if ($owned{$domain.'_'.$name}) { + if (ref($owned{$domain.'_'.$name}) eq 'HASH') { + if ($owned{$domain.'_'.$name}{'nopasswdchg'}) { + $noupdate = 1; + last; + } + } + } else { + $noupdate = 1; + last; + } + } else { + $noupdate = 1; + last; + } + } + unless ($noupdate) { + $canchange = 1; + } + } + } + } + return $canchange; +} + sub check_usertype { my ($dom,$uname,$rules,$curr_rules,$got_rules) = @_; my $usertype; @@ -5748,7 +6515,7 @@ sub roles_by_context { } elsif ($context eq 'author') { @allroles = ('ca','aa'); } elsif ($context eq 'domain') { - @allroles = ('li','ad','dg','sc','au','dc'); + @allroles = ('li','ad','dg','dh','da','sc','au','dc'); } return @allroles; } @@ -5806,6 +6573,23 @@ sub get_permission { } } } + if ($env{'request.course.id'}) { + my $user; + if (($env{'user.name'} ne '') && ($env{'user.domain'} ne '')) { + $user = $env{'user.name'}.':'.$env{'user.domain'}; + } + if (($user ne '') && ($env{'course.'.$env{'request.course.id'}.'.internal.courseowner'} eq + $user)) { + $permission{'owner'} = 1; + if (&Apache::lonnet::allowed('mip',$env{'request.course.id'})) { + $permission{'mip'} = 1; + } + } elsif (($user ne '') && ($env{'course.'.$env{'request.course.id'}.'.internal.co-owners'} ne '')) { + if (grep(/^\Q$user\E$/,split(/,/,$env{'course.'.$env{'request.course.id'}.'.internal.co-owners'}))) { + $permission{'co-owner'} = 1; + } + } + } } elsif ($context eq 'author') { $permission{'cusr'} = &authorpriv($env{'user.name'},$env{'request.role.domain'}); $permission{'view'} = $permission{'cusr'}; @@ -5825,11 +6609,20 @@ sub get_permission { if (&Apache::lonnet::allowed('ccr',$env{'request.role.domain'})) { $permission{'custom'} = 1; } - $permission{'view'} = $permission{'cusr'}; + if (&Apache::lonnet::allowed('vac',$env{'request.role.domain'})) { + $permission{'activity'} = 1; + } + if (&Apache::lonnet::allowed('vur',$env{'request.role.domain'})) { + $permission{'view'} = 1; + } + if (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'})) { + $permission{'owner'} = 1; + } } my $allowed = 0; - foreach my $perm (values(%permission)) { - if ($perm) { $allowed=1; last; } + foreach my $key (keys(%permission)) { + next if (($key eq 'owner') || ($key eq 'co-owner')); + if ($permission{$key}) { $allowed=1; last; } } return (\%permission,$allowed); } @@ -5878,7 +6671,7 @@ sub get_course_identity { } sub dc_setcourse_js { - my ($formname,$mode,$context,$showcredits) = @_; + my ($formname,$mode,$context,$showcredits,$domain) = @_; my ($dc_setcourse_code,$authen_check); my $cctext = &Apache::lonnet::plaintext('cc'); my $cotext = &Apache::lonnet::plaintext('co'); @@ -5887,7 +6680,7 @@ sub dc_setcourse_js { if ($mode eq 'upload') { $role = 'courserole'; } else { - $authen_check = &verify_authen($formname,$context); + $authen_check = &verify_authen($formname,$context,$domain); } $dc_setcourse_code = (<<"SCRIPTTOP"); $authen_check @@ -6031,12 +6824,14 @@ ENDSCRIPT } sub verify_authen { - my ($formname,$context) = @_; + my ($formname,$context,$domain) = @_; my %alerts = &authcheck_alerts(); my $finish = "return 'ok';"; if ($context eq 'author') { $finish = "document.$formname.submit();"; } + my ($numrules,$intargjs) = + &Apache::loncommon::passwd_validation_js('argpicked',$domain); my $outcome = <<"ENDSCRIPT"; function auth_check() { @@ -6070,6 +6865,7 @@ function auth_check() { break; case 'int': alertmsg = '$alerts{'ipass'}'; + break; case 'fsys': alertmsg = '$alerts{'ipass'}'; break; @@ -6083,6 +6879,11 @@ function auth_check() { alert(alertmsg); return; } + } else if (logintype == 'int') { + var numrules = $numrules; + if (numrules > 0) { +$intargjs + } } $finish } @@ -6201,6 +7002,8 @@ sub get_extended_type { } if ($crstype eq 'Community') { $type = 'community'; + } elsif ($crstype eq 'Placement') { + $type = 'placement'; } elsif ($settings{'internal.coursecode'}) { $type = 'official'; } elsif ($settings{'internal.textbook'}) { @@ -6259,5 +7062,447 @@ sub selfenrollment_administration { return (\@in_course,\@in_domain); } +sub custom_role_header { + my ($context,$crstype,$templaterolerefs,$prefix) = @_; + my %lt = &Apache::lonlocal::texthash( + sele => 'Select a Template', + ); + my ($context_code,$button_code); + if ($context eq 'domain') { + $context_code = &custom_coursetype_switch($crstype,$prefix); + } + if (ref($templaterolerefs) eq 'ARRAY') { + foreach my $role (@{$templaterolerefs}) { + my $display = 'inline'; + if (($context eq 'domain') && ($role eq 'co')) { + $display = 'none'; + } + $button_code .= &make_button_code($role,$crstype,$display,$prefix).' '; + } + } + return <<"END"; +
+
+$lt{'sele'} +$button_code +
+$context_code +
+END +} + +sub custom_coursetype_switch { + my ($crstype,$prefix) = @_; + my ($checkedcourse,$checkedcommunity); + if ($crstype eq 'Community') { + $checkedcommunity = ' checked="checked"'; + } else { + $checkedcourse = ' checked="checked"'; + } + my %lt = &Apache::lonlocal::texthash( + cont => 'Context', + cour => 'Course', + comm => 'Community', + ); + return <<"END"; +
+
+$lt{'cont'} +   + +
+
+END +} + +sub custom_role_table { + my ($crstype,$full,$levels,$levelscurrent,$prefix,$add_class,$id) = @_; + return unless ((ref($full) eq 'HASH') && (ref($levels) eq 'HASH') && + (ref($levelscurrent) eq 'HASH')); + my %lt=&Apache::lonlocal::texthash ( + 'prv' => "Privilege", + 'crl' => "Course Level", + 'dml' => "Domain Level", + 'ssl' => "System Level"); + my %cr = ( + course => '_c', + domain => '_d', + system => '_s', + ); + + my $output=&Apache::loncommon::start_data_table($add_class,$id). + &Apache::loncommon::start_data_table_header_row(). + ''. + &Apache::loncommon::end_data_table_header_row(); + foreach my $priv (sort(keys(%{$full}))) { + my $privtext = &Apache::lonnet::plaintext($priv,$crstype); + $output .= &Apache::loncommon::start_data_table_row(). + ''; + foreach my $type ('course','domain','system') { + if (($type eq 'system') && ($priv eq 'bre') && ($crstype eq 'Community')) { + $output .= ''; + } else { + $output .= ''; + } + } + $output .= &Apache::loncommon::end_data_table_row(); + } + $output .= &Apache::loncommon::end_data_table(); + return $output; +} + +sub custom_role_privs { + my ($privs,$full,$levels,$levelscurrent)= @_; + return unless ((ref($privs) eq 'HASH') && (ref($full) eq 'HASH') && + (ref($levels) eq 'HASH') && (ref($levelscurrent) eq 'HASH')); + my %cr = ( + course => 'cr:c', + domain => 'cr:d', + system => 'cr:s', + ); + foreach my $type ('course','domain','system') { + foreach my $item (split(/\:/,$Apache::lonnet::pr{$cr{$type}})) { + my ($priv,$restrict)=split(/\&/,$item); + if (!$restrict) { $restrict='F'; } + $levels->{$type}->{$priv}=$restrict; + if ($privs->{$type}=~/\:$priv/) { + $levelscurrent->{$type}->{$priv}=1; + } + $full->{$priv}=1; + } + } + return; +} + +sub custom_template_roles { + my ($context,$crstype) = @_; + my @template_roles = ("in","ta","ep"); + if (($context eq 'domain') || ($context eq 'domprefs')) { + push(@template_roles,"ad"); + } + push(@template_roles,"st"); + if ($context eq 'domain') { + unshift(@template_roles,('co','cc')); + } else { + if ($crstype eq 'Community') { + unshift(@template_roles,'co'); + } else { + unshift(@template_roles,'cc'); + } + } + return @template_roles; +} + +sub custom_roledefs_js { + my ($context,$crstype,$formname,$full,$templaterolesref,$jsback) = @_; + my $button_code = "\n"; + my $head_script = "\n"; + my (%roletitlestr,$rolenamestr); + my %role_titles = ( + Course => [], + Community => [], + ); + $head_script .= ''."\n"; + return $head_script; +} + +# -------------------------------------------------------- +sub make_script_template { + my ($role,$crstype,$formname) = @_; + my $return_script = 'function set_'.$role.'(prefix) {'."\n"; + my (%full_by_level,%role_priv); + foreach my $level ('c','d','s') { + foreach my $item (split(/\:/,$Apache::lonnet::pr{'cr:'.$level})) { + next if (($level eq 's') && ($crstype eq 'Community') && ($item eq 'bre&S')); + my ($priv,$restrict)=split(/\&/,$item); + $full_by_level{$level}{$priv}=1; + } + $role_priv{$level} = {}; + my @temp = split(/:/,$Apache::lonnet::pr{$role.':'.$level}); + foreach my $priv (@temp) { + my ($priv_item, $dummy) = split(/\&/,$priv); + $role_priv{$level}{$priv_item} = 1; + } + } + my %to_check = ( + c => ['c','d','s'], + d => ['d','s'], + s => ['s'], + ); + foreach my $level ('c','d','s') { + if (ref($full_by_level{$level}) eq 'HASH') { + foreach my $priv (keys(%{$full_by_level{$level}})) { + my $value = 'false'; + if (ref($to_check{$level}) eq 'ARRAY') { + foreach my $lett (@{$to_check{$level}}) { + if (exists($role_priv{$lett}{$priv})) { + $value = 'true'; + last; + } + } + $return_script .= "document.$formname.elements[prefix+'".$priv."_".$level."'].checked = $value;\n"; + } + } + } + } + $return_script .= '}'."\n"; + return ($return_script); +} +# ---------------------------------------------------------- +sub make_button_code { + my ($role,$crstype,$display,$prefix) = @_; + my $label = &Apache::lonnet::plaintext($role,$crstype); + my $button_code = ''; + return ($button_code); +} + +sub custom_role_update { + my ($rolename,$prefix) = @_; +# ------------------------------------------------------- What can be assigned? + my %privs = ( + c => '', + d => '', + s => '', + ); + foreach my $level (keys(%privs)) { + foreach my $item (split(/\:/,$Apache::lonnet::pr{'cr:'.$level})) { + my ($priv,$restrict)=split(/\&/,$item); + if (!$restrict) { $restrict=''; } + if ($env{'form.'.$prefix.$priv.'_'.$level}) { + $privs{$level} .=':'.$item; + } + } + } + return %privs; +} + +sub adhoc_status_types { + my ($cdom,$context,$role,$selectedref,$othertitle,$usertypes,$types,$disabled) = @_; + my $output = &Apache::loncommon::start_data_table(); + my $numinrow = 3; + my $rem; + if (ref($types) eq 'ARRAY') { + for (my $i=0; $i<@{$types}; $i++) { + if (defined($usertypes->{$types->[$i]})) { + my $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $output .= &Apache::loncommon::end_data_table_row(); + } + $output .= &Apache::loncommon::start_data_table_row(); + } + my $check; + if (ref($selectedref) eq 'ARRAY') { + if (grep(/^\Q$types->[$i]\E$/,@{$selectedref})) { + $check = ' checked="checked"'; + } + } + $output .= ''; + } + } + $rem = @{$types}%($numinrow); + } + my $colsleft = $numinrow - $rem; + if (($rem == 0) && (@{$types} > 0)) { + $output .= &Apache::loncommon::start_data_table_row(); + } + if ($colsleft > 1) { + $output .= ''. + &Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table(); + return $output; +} + +sub adhoc_staff { + my ($access,$context,$role,$selectedref,$adhocref,$disabled) = @_; + my $output; + if (ref($adhocref) eq 'HASH') { + my %by_fullname; + my $numinrow = 4; + my $rem; + my @personnel = keys(%{$adhocref}); + if (@personnel) { + foreach my $person (@personnel) { + my ($uname,$udom) = split(/:/,$person); + my $fullname = &Apache::loncommon::plainname($uname,$udom,'lastname'); + $by_fullname{$fullname} = $person; + } + my @sorted = sort(keys(%by_fullname)); + my $count = scalar(@sorted); + $output = &Apache::loncommon::start_data_table(); + for (my $i=0; $i<$count; $i++) { + my $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $output .= &Apache::loncommon::end_data_table_row(); + } + $output .= &Apache::loncommon::start_data_table_row(); + } + my $check; + my $user = $by_fullname{$sorted[$i]}; + if (ref($selectedref) eq 'ARRAY') { + if (grep(/^\Q$user\E$/,@{$selectedref})) { + $check = ' checked="checked"'; + } + } + if ($i == $count-1) { + my $colsleft = $numinrow - $rem; + if ($colsleft > 1) { + $output .= ''; + if ($i == $count-1) { + $output .= &Apache::loncommon::end_data_table_row(); + } + } + $output .= &Apache::loncommon::end_data_table(); + } + } + return $output; +} + + 1;
'.&mt('manual').'
'; + $cellentry = '
'.&mt('manual').'
'; $manualcount ++; if ($in{'lockedtype'}) { - $cellentry .= ''; + $cellentry .= ''; $unlockcount ++; } else { - $cellentry .= ''; + $cellentry .= ''; $lockcount ++; } $cellentry .= '
'; @@ -3056,13 +3475,21 @@ END if ($role eq 'st') { $checkval .= ':'.$in{'type'}.':'. $in{'lockedtype'}.':'. - $in{'credits'}; + $in{'credits'}.':'. + &escape($in{'instsec'}); } } } if ($showcheckbox) { $r->print('
 '.&print_username_link($mode,\%in).''.$in{$item}.'
'.$authformloc.''.$authformlti.''.$lt{'prv'}.''.$lt{'crl'}.''.$lt{'dml'}. + ''.$lt{'ssl'}.''.$privtext.' '. + ($levels->{$type}{$priv}?'{$type}{$priv}?' checked="checked"':'').' />':' '). + ''. + ''; + } else { + $output .= ''; + } + my $defcheck; + if (ref($selectedref) eq 'ARRAY') { + if (grep(/^default$/,@{$selectedref})) { + $defcheck = ' checked="checked"'; + } + } + $output .= ''; + } else { + $output .= ''; + } + } else { + $output .= ''; + } + $output .= '