--- loncom/interface/lonuserutils.pm 2017/01/28 03:48:44 1.182 +++ loncom/interface/lonuserutils.pm 2024/09/01 02:33:40 1.184.4.10.2.9 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Utility functions for managing LON-CAPA user accounts # -# $Id: lonuserutils.pm,v 1.182 2017/01/28 03:48:44 raeburn Exp $ +# $Id: lonuserutils.pm,v 1.184.4.10.2.9 2024/09/01 02:33:40 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -103,7 +103,7 @@ sub modifystudent { sub modifyuserrole { my ($context,$setting,$changeauth,$cid,$udom,$uname,$uid,$umode,$upass, $first,$middle,$last,$gene,$sec,$forceid,$desiredhome,$email,$role, - $end,$start,$checkid,$inststatus) = @_; + $end,$start,$checkid,$inststatus,$emptyok) = @_; my ($scope,$userresult,$authresult,$roleresult,$idresult); if ($setting eq 'course' || $context eq 'course') { $scope = '/'.$cid; @@ -114,7 +114,11 @@ sub modifyuserrole { } elsif ($context eq 'domain') { $scope = '/'.$env{'request.role.domain'}.'/'; } elsif ($context eq 'author') { - $scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'}; + if ($env{'request.role'} =~ m{^ca\.(/$match_domain/$match_username)$}) { + $scope = $1; + } else { + $scope = '/'.$env{'user.domain'}.'/'.$env{'user.name'}; + } } if ($context eq 'domain') { my $uhome = &Apache::lonnet::homeserver($uname,$udom); @@ -134,6 +138,25 @@ sub modifyuserrole { generation => $gene, id => $uid, ); + + # When "Update ID in user's course(s)" and "Force change of existing ID" + # checkboxes both checked, prevent replacement of name information + # in classlist.db file(s) for the user's course(s) with blank(s), + # in the case where the uploaded csv file was without column(s) for + # the particular field. Fields are: First Name, Middle Names/Initials, + # Last Name (or the composite: Last Name, First Names), and Generation. + + my %emptyallowed; + if ((ref($emptyok) eq 'HASH') && (keys(%{$emptyok}) > 0)) { + %emptyallowed = %{$emptyok}; + } + foreach my $field (keys(%userupdate)) { + if ($userupdate{$field} eq '') { + unless ($emptyallowed{$field}) { + delete($userupdate{$field}); + } + } + } $idresult = &propagate_id_change($uname,$udom,\%userupdate); } } @@ -438,7 +461,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". @@ -510,7 +533,7 @@ END "; } elsif ($mode eq 'modifycourse') { $auth_checks .= " - if (vf.elements[current.argfield].value == null || vf.elements[current.argfield].value == '') { + if ((current.argfield !== null) && (current.argfield !== undefined) && (current.argfield !== '') && (vf.elements[current.argfield].value == null || vf.elements[current.argfield].value == '')) { "; } if ( ($mode eq 'createcourse') || ($mode eq 'modifycourse') ) { @@ -531,19 +554,25 @@ 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 } @@ -641,6 +674,7 @@ END $section_checks.$authheader; return $result; } + ############################################################### ############################################################### sub upload_manager_javascript_forward_associate { @@ -1121,8 +1155,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'}){ @@ -1206,6 +1247,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 { @@ -1466,6 +1508,14 @@ sub construction_space_roles { foreach my $role (@allroles) { if (&Apache::lonnet::allowed('c'.$role,$env{'user.domain'}.'/'.$env{'user.name'})) { push(@roles,$role); + } elsif ($env{'request.role'} =~ m{^ca\./($match_domain)/($match_username)$}) { + my ($audom,$auname) = ($1,$2); + if (($role eq 'ca') || ($role eq 'aa')) { + if ((&Apache::lonnet::allowed('v'.$role,,$audom.'/'.$auname)) && + ($env{"environment.internal.manager./$audom/$auname"})) { + push(@roles,$role); + } + } } } return @roles; @@ -1569,6 +1619,24 @@ sub print_userlist { if (! exists($env{'form.sortby'})) { $env{'form.sortby'} = 'username'; } + my ($showstart,$showend); + if (($env{'form.Status'} eq '') && ($env{'form.phase'} eq '') && + ($env{'form.showrole'} eq '') && ($context eq 'course') && + ($env{'request.course.id'} ne '') && + ($env{'course.'.$env{'request.course.id'}.'.internal.coursecode'} ne '')) { + my $now = time; + my $startaccess = $env{'course.'.$env{'request.course.id'}.'.default_enrollment_start_date'}; + my $endaccess = $env{'course.'.$env{'request.course.id'}.'.default_enrollment_end_date'}; + if (($startaccess) && ($startaccess > $now)) { + $env{'form.Status'} = 'Future'; + $showstart = 1; + } + if (($endaccess ne '') && ($endaccess != 0) && ($endaccess < $now)) { + $env{'form.Status'} = 'Expired'; + undef($showstart); + $showend = 1; + } + } if ($env{'form.Status'} !~ /^(Any|Expired|Active|Future)$/) { $env{'form.Status'} = 'Active'; } @@ -1636,7 +1704,7 @@ sub print_userlist { $r->print(§ion_group_filter($cnum,$cdom)); } $r->print('
'. - &column_checkboxes($context,$mode,$formname,$showcredits). + &column_checkboxes($context,$mode,$formname,$showcredits,$showstart,$showend). '
'); if ($env{'form.phase'} eq '') { $r->print('
'. @@ -1722,12 +1790,31 @@ sub print_userlist { } else { my (%cstr_roles,%dom_roles); if ($context eq 'author') { - # List co-authors and assistant co-authors my @possroles = &roles_by_context($context); - %cstr_roles = &Apache::lonnet::get_my_roles(undef,undef,undef, - \@statuses,\@possroles); - &gather_userinfo($context,$format,\%userlist,$indexhash,\%userinfo, - \%cstr_roles,$permission); + my @allowedroles; + # List co-authors and assistant co-authors + my ($auname,$audom); + if ($env{'request.role'} =~ m{^ca\./($match_domain)/($match_username)$}) { + ($audom,$auname) = ($1,$2); + foreach my $role (@possroles) { + if ((&Apache::lonnet::allowed('v'.$role,"$audom/$auname")) || + (&Apache::lonnet::allowed('c'.$role,"$audom/$auname"))) { + push(@allowedroles,$role); + } + } + } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) { + if ($1 eq $env{'user.domain'}) { + $auname = $env{'user.name'}; + $audom = $env{'user.domain'}; + } + @allowedroles = @possroles; + } + if (($auname ne '') && ($audom ne '')) { + %cstr_roles = &Apache::lonnet::get_my_roles($auname,$audom,undef, + \@statuses,\@allowedroles); + &gather_userinfo($context,$format,\%userlist,$indexhash,\%userinfo, + \%cstr_roles,$permission); + } } elsif ($context eq 'domain') { if ($env{'form.roletype'} eq 'domain') { if (grep(/^authorusage$/,@cols)) { @@ -2018,14 +2105,17 @@ sub get_cols_array { push(@cols,'photo'); } if ($context eq 'domain') { - push (@cols,('authorusage','authorquota','extent')); + push(@cols,('authorusage','authorquota','extent')); + } + if ($context eq 'author') { + push(@cols,'manager'); } } return @cols; } sub column_checkboxes { - my ($context,$mode,$formname,$showcredits) = @_; + my ($context,$mode,$formname,$showcredits,$showstart,$showend) = @_; my @cols = &get_cols_array($context,$mode,$showcredits); my @showncols = &Apache::loncommon::get_env_multiple('form.showcol'); my (%disabledchk,%unchecked); @@ -2038,11 +2128,21 @@ sub column_checkboxes { if ($showcredits) { $unchecked{'credits'} = 1; } - } elsif ($context eq 'domain') { + my %curr_groups = &Apache::longroup::coursegroups(); + unless (keys(%curr_groups)) { + $unchecked{'groups'} = 1; + } + } elsif ($context eq 'domain') { $unchecked{'extent'} = 1; } - $unchecked{'start'} = 1; - $unchecked{'end'} = 1; + if ($showstart) { + $unchecked{'lastlogin'} = 1; + } else { + $unchecked{'start'} = 1; + } + unless ($showend) { + $unchecked{'end'} = 1; + } } else { if ($env{'form.Status'} ne 'Any') { $disabledchk{'status'} = 1; @@ -2059,6 +2159,11 @@ sub column_checkboxes { } elsif ($env{'form.roletype'} eq 'domain') { $disabledchk{'extent'} = 1; } + } elsif ($context eq 'author') { + if (($env{'form.Status'} eq 'Expired') || + ($env{'form.showrole'} eq 'aa')) { + $disabledchk{'manager'} = 1; + } } } my $numposs = scalar(@cols); @@ -2150,6 +2255,7 @@ sub get_column_names { 'ca' => "check all", 'ua' => "uncheck all", 'clicker' => "clicker-ID", + 'manager' => "co-author manager", ); if ($context eq 'domain' && $env{'form.roletype'} eq 'course') { $lt{'extent'} = &mt('course(s): description, section(s), status'); @@ -2174,6 +2280,7 @@ sub gather_userinfo { ($userdata{'username'},$userdata{'domain'},$userdata{'role'}) = split(/:/,$item); ($userdata{'start'},$userdata{'end'})=split(/:/,$rolehash->{$item}); + next if (($userdata{'username'} eq '') && ($userdata{'domain'} eq '')); &build_user_record($context,\%userdata,$userinfo,$indexhash, $item,$userlist); } elsif ($context eq 'course') { @@ -2279,7 +2386,6 @@ sub build_user_record { sub courses_selector { my ($cdom,$formname) = @_; - my %coursecodes = (); my %codes = (); my @codetitles = (); my %cat_titles = (); @@ -2292,14 +2398,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); @@ -2336,7 +2443,8 @@ function setCourseCat(formname) { } courseSet('$codetitles[1]'); for (var j=0; j{'instsec'} = &Apache::loncoursedata::CL_INSTSEC(); $index->{'authorquota'} = &Apache::loncoursedata::CL_AUTHORQUOTA(); $index->{'authorusage'} = &Apache::loncoursedata::CL_AUTHORUSAGE(); + $index->{'manager'} = &Apache::loncoursedata::CL_CAMANAGER(); foreach my $key (keys(%{$index})) { $keylist->[$index->{$key}] = $key; } @@ -2507,6 +2616,9 @@ sub show_users_list { (($env{'form.showrole'} eq 'Any') || ($env{'form.showrole'} eq 'au'))) { push(@sortable,('authorusage','authorquota')); } + if ($context eq 'author') { + push(@sortable,'manager'); + } } if ($mode eq 'pickauthor') { @sortable = ('username','fullname','email','status'); @@ -2757,7 +2869,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') { @@ -2824,6 +2936,7 @@ END foreach my $idx (@$keylist) { $index{$idx} = $i++; } + my $now = time; my $usercount = 0; my ($secfilter,$grpfilter); if ($context eq 'course') { @@ -2841,10 +2954,21 @@ END Future => 'Future', Expired => 'Expired', ); - # If this is for a single course get last course "log-in". - my %crslogins; + my (%crslogins,%camanagers); if ($context eq 'course') { + # If this is for a single course get last course "log-in". %crslogins=&Apache::lonnet::dump('nohist_crslastlogin',$cdom,$cnum); + } elsif ($context eq 'author') { + my $authormanagers; + if ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)$}) { + my %envhash = &Apache::lonnet::userenvironment($1,$2,'authormanagers'); + $authormanagers = $envhash{'authormanagers'}; + } else { + $authormanagers = $env{'environment.authormanagers'}; + } + if ($authormanagers ne '') { + map { $camanagers{$_.':ca'} = 1; } split(/,/,$authormanagers); + } } # Get groups, role, permanent e-mail so we can sort on them if # necessary. @@ -2944,6 +3068,16 @@ END } } } + if ($context eq 'author') { + if (($camanagers{$user}) && + ((!defined($userlist->{$user}->[$index{'end'}])) || + ($userlist->{$user}->[$index{'end'}] == 0) || + ($userlist->{$user}->[$index{'end'}] > $now))) { + $userlist->{$user}->[$index{'manager'}] = &mt('Yes'); + } else { + $userlist->{$user}->[$index{'manager'}] = &mt('No'); + } + } my %emails = &Apache::loncommon::getemails($uname,$udom); if ($emails{'permanentemail'} =~ /\S/) { $userlist->{$user}->[$index{'email'}] = $emails{'permanentemail'}; @@ -3101,7 +3235,14 @@ END } if ($showcheckbox) { $r->print(''); + 'actionlist" value="'. + &HTML::Entities::encode($checkval,'&<>"').'" />'); + foreach my $item ('start','end') { + $r->print(''); + } + $r->print(''); } else { $r->print(' '); } @@ -3115,8 +3256,6 @@ END foreach my $item (@cols) { if ($item eq 'username') { $r->print(''.&print_username_link($mode,\%in).''); - } elsif (($item eq 'start' || $item eq 'end') && ($actionselect)) { - $r->print(''.$in{$item}.''."\n"); } elsif ($item eq 'status') { my $showitem = $in{$item}; if (defined($ltstatus{$in{$item}})) { @@ -3507,6 +3646,8 @@ END setSections(formname,'$crstype'); if (seccheck == 'ok') { opener.document.$callingform.newsecs.value = formname.sections.value; + } else { + return; } END } else { @@ -3755,10 +3896,16 @@ sub results_header_row { $description .= ' ('.$constraint.')'; } } elsif ($context eq 'author') { + my ($auname,$audom); + if ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)$}) { + ($audom,$auname) = ($1,$2); + } else { + ($audom,$auname) = ($env{'user.domain'},$env{'user.name'}); + } $description = &mt('Author space for [_1]' ,'' - .&Apache::loncommon::plainname($env{'user.name'},$env{'user.domain'}) + .&Apache::loncommon::plainname($auname,$audom) .'') .':  '; if ($statusmode eq 'Expired') { @@ -4091,7 +4238,7 @@ sub print_first_users_upload_form { .&Apache::lonhtmlcommon::end_pick_box(); $str .= '

' - .'' .'

'; @@ -4102,7 +4249,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'}); @@ -4116,10 +4266,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 @@ -4133,17 +4279,42 @@ 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,$oldcrsuserdoms,%emptyok); if ($context eq 'domain') { $setting = $env{'form.roleaction'}; + if (exists($fields{'names'})) { + map { $emptyok{$_} = 1; } ('lastname','firstname','middlename'); + } else { + if (exists($fields{'lname'})) { + $emptyok{'lastname'} = 1; + } + if (exists($fields{'fname'})) { + $emptyok{'firstname'} = 1; + } + if (exists($fields{'mname'})) { + $emptyok{'middlename'} = 1; + } + } + if (exists($fields{'gen'})) { + $emptyok{'generation'} = 1; + } } 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'}; + if (exists($env{'course.'.$cid.'.internal.userdomains'})) { + $oldcrsuserdoms = 1; + } + my %coursedesc = &Apache::lonnet::coursedescription($cid,{ one_time => 1 }); + $env{'course.'.$cid.'.internal.userdomains'} = $coursedesc{'internal.userdomains'}; } } my ($startdate,$enddate) = &get_dates_from_form(); @@ -4164,10 +4335,10 @@ 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('

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

'); $r->print(&Apache::loncommon::end_page()); - return; + return 'invalidhome'; } } # Determine authentication mechanism @@ -4177,6 +4348,7 @@ sub upfile_drop_add { } my $amode = ''; my $genpwd = ''; + my @genpwdfail; if ($env{'form.login'} eq 'krb') { $amode='krb'; $amode.=$env{'form.krbver'}; @@ -4185,6 +4357,8 @@ 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'; @@ -4263,10 +4437,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 '')) { ####################################### @@ -4336,7 +4514,8 @@ 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,%alerts,%checkuname); + my (%existinguser,%userinfo,%disallow,%rulematch,%inst_results,%alerts,%checkuname, + %showpasswdrules,$haspasswdmap); my $counter = -1; foreach my $line (@userdata) { $counter ++; @@ -4464,12 +4643,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'})) { @@ -4740,6 +4951,16 @@ 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) { @@ -4781,16 +5002,16 @@ sub upfile_drop_add { } } } - 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) { + ($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,\%emptyok); } } if ($multiple) { @@ -4812,6 +5033,13 @@ sub upfile_drop_add { } # end of loop $r->print(''); &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state); + if (($context eq 'domain') && ($setting eq 'course')) { + unless ($oldcrsuserdoms) { + if (exists($env{'course.'.$cid.'.internal.userdomains'})) { + delete($env{'course.'.$cid.'.internal.userdomains'}); + } + } + } } # Flush the course logs so reverse user roles immediately updated $r->register_cleanup(\&Apache::lonnet::flushcourselogs); @@ -4819,7 +5047,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').'

'); } @@ -4829,6 +5059,7 @@ sub upfile_drop_add { $counts{'auth'})."

\n"); } $r->print(&print_namespacing_alerts($domain,\%alerts,\%curr_rules)); + $r->print(&passwdrule_alerts($domain,\%showpasswdrules)); ##################################### # Display list of students to drop # ##################################### @@ -4837,10 +5068,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) { @@ -4856,9 +5086,7 @@ sub upfile_drop_add { } } } # end of unless - if ($env{'form.fullup'} ne 'yes') { - $r->print(''); - } + return 'ok'; } sub print_namespacing_alerts { @@ -4901,6 +5129,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:').''; + } + } + return $warning; +} + sub user_change_result { my ($r,$userresult,$authresult,$roleresult,$idresult,$counts,$flushc, $username,$userdomain,$userchg) = @_; @@ -5716,6 +5980,10 @@ sub can_modify_userinfo { my %domconfig = &Apache::lonnet::get_dom('configuration',['usermodification'], $dom); + if (($context eq 'author') && + ($env{'request.role'} =~ m{^(ca|aa)\./$match_domain/$match_username$})) { + $context = 'coauthor'; + } my %canmodify; if (ref($fields) eq 'ARRAY') { foreach my $field (@{$fields}) { @@ -5770,6 +6038,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; @@ -5890,12 +6222,21 @@ sub get_permission { $permission{'selfenrolladmin'} = 1; } } + unless ($permission{'selfenrolladmin'}) { + $permission{'selfenrollview'} = 1; + } } if ($env{'request.course.id'}) { - my $user = $env{'user.name'}.':'.$env{'user.domain'}; + 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; @@ -5903,8 +6244,23 @@ sub get_permission { } } } elsif ($context eq 'author') { - $permission{'cusr'} = &authorpriv($env{'user.name'},$env{'request.role.domain'}); - $permission{'view'} = $permission{'cusr'}; + my $audom = $env{'request.role.domain'}; + my $auname = $env{'user.name'}; + if ((&Apache::lonnet::allowed('cca',"$audom/$auname")) || + (&Apache::lonnet::allowed('caa',"$audom/$auname"))) { + $permission{'author'} = 1; + $permission{'cusr'} = 1; + $permission{'view'} = 1; + } + } elsif ($context eq 'coauthor') { + my ($audom,$auname) = ($env{'request.role'} =~ m{^ca\./($match_domain)/($match_username)$}); + if ((&Apache::lonnet::allowed('vca',"$audom/$auname")) || + (&Apache::lonnet::allowed('vaa',"$audom/$auname"))) { + if ($env{"environment.internal.manager./$audom/$auname"}) { + $permission{'cusr'} = 1; + $permission{'view'} = 1; + } + } } else { my @allroles = &roles_by_context($context); foreach my $role (@allroles) { @@ -5932,8 +6288,9 @@ sub get_permission { } } 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') || ($key eq 'author')); + if ($permission{$key}) { $allowed=1; last; } } return (\%permission,$allowed); } @@ -5946,6 +6303,18 @@ sub authorpriv { || (&Apache::lonnet::allowed('caa',$audom.'/'.$auname))) { return ''; } return 1; } +sub coauthorpriv { + my ($auname,$audom)=@_; + my $uname = $env{'user.name'}; + my $udom = $env{'user.domain'}; + if (((&Apache::lonnet::allowed('vca',"$udom/$uname")) || + (&Apache::lonnet::allowed('vaa',"$udom/$uname"))) && + ($env{"environment.internal.manager./$audom/$auname"})) { + return 1; + } + return ''; +} + sub roles_on_upload { my ($context,$setting,$crstype,%customroles) = @_; my (@possible_roles,@permitted_roles); @@ -5982,7 +6351,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'); @@ -5991,7 +6360,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 @@ -6135,12 +6504,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() { @@ -6174,6 +6545,7 @@ function auth_check() { break; case 'int': alertmsg = '$alerts{'ipass'}'; + break; case 'fsys': alertmsg = '$alerts{'ipass'}'; break; @@ -6187,6 +6559,11 @@ function auth_check() { alert(alertmsg); return; } + } else if (logintype == 'int') { + var numrules = $numrules; + if (numrules > 0) { +$intargjs + } } $finish } @@ -6338,7 +6715,7 @@ sub selfenrollment_administration { } } if ($settings{'internal.selfenrollmgrdc'} ne '') { - my @in_domain = split(/,/,$settings{'internal.selfenrollmgrdc'}); + @in_domain = split(/,/,$settings{'internal.selfenrollmgrdc'}); my @diffs = &Apache::loncommon::compare_arrays(\@in_domain,$possconfigs); unless (@diffs) { return (\@in_course,\@in_domain);