--- loncom/interface/lonuserutils.pm 2010/07/09 14:40:20 1.120 +++ loncom/interface/lonuserutils.pm 2012/02/08 16:27:28 1.136.6.1 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Utility functions for managing LON-CAPA user accounts # -# $Id: lonuserutils.pm,v 1.120 2010/07/09 14:40:20 raeburn Exp $ +# $Id: lonuserutils.pm,v 1.136.6.1 2012/02/08 16:27:28 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -47,7 +47,8 @@ sub modifystudent { # this one. If $csec is defined, drop them from all other sections of # this course and add them to section $csec my ($cnum,$cdom) = &get_course_identity($courseid); - my %roles = &Apache::lonnet::dump('roles',$udom,$unam); + my $extra = &Apache::lonnet::freeze_escape({'skipcheck' => 1}); + my %roles = &Apache::lonnet::dump('roles',$udom,$unam,'.',undef,$extra); my ($tmp) = keys(%roles); # Bail out if we were unable to get the students roles return "$1" if ($tmp =~ /^(con_lost|error|no_such_host)/i); @@ -295,7 +296,8 @@ sub hidden_input { } sub print_upload_manager_header { - my ($r,$datatoken,$distotal,$krbdefdom,$context,$permission,$crstype)=@_; + my ($r,$datatoken,$distotal,$krbdefdom,$context,$permission,$crstype, + $can_assign)=@_; my $javascript; # if (! exists($env{'form.upfile_associate'})) { @@ -309,9 +311,9 @@ sub print_upload_manager_header { } } if ($env{'form.upfile_associate'} eq 'reverse') { - $javascript=&upload_manager_javascript_reverse_associate(); + $javascript=&upload_manager_javascript_reverse_associate($can_assign); } else { - $javascript=&upload_manager_javascript_forward_associate(); + $javascript=&upload_manager_javascript_forward_associate($can_assign); } # # Deal with restored settings @@ -532,6 +534,7 @@ END if (message!='') { message+='\\n'; } + message+='$alert{'section'}'; } if (foundemail==0) { if (message!='') { @@ -585,6 +588,46 @@ END ############################################################### ############################################################### sub upload_manager_javascript_forward_associate { + my ($can_assign) = @_; + my ($auth_update,$numbuttons,$argreset); + if (ref($can_assign) eq 'HASH') { + if ($can_assign->{'krb4'} || $can_assign->{'krb5'}) { + $argreset .= " vf.krbarg.value='';\n"; + $numbuttons ++ ; + } + if ($can_assign->{'int'}) { + $argreset .= " vf.intarg.value='';\n"; + $numbuttons ++; + } + if ($can_assign->{'loc'}) { + $argreset .= " vf.locarg.value='';\n"; + $numbuttons ++; + } + if (!$can_assign->{'int'}) { + my $warning = &mt('You may not specify an initial password for each user, as this is only available when new users use LON-CAPA internal authentication.\n'). + &mt('Your current role does not have rights to create users with that authentication type.'); + $auth_update = <<"END"; + // Currently the initial password field is only supported for internal auth + // (see bug 6368). + if (nw==9) { + eval('vf.f'+tf+'.selectedIndex=0;') + alert('$warning'); + } +END + } elsif ($numbuttons > 1) { + $auth_update = <<"END"; + // If we set the password, make the password form below correspond to + // the new value. + if (nw==9) { + changed_radio('int',document.studentform); + set_auth_radio_buttons('int',document.studentform); +$argreset + } + +END + } + } + return(<{'krb4'} || $can_assign->{'krb5'}) { + $argreset .= " vf.krbarg.value='';\n"; + $numbuttons ++ ; + } + if ($can_assign->{'int'}) { + $argreset .= " vf.intarg.value='';\n"; + $numbuttons ++; + } + if ($can_assign->{'loc'}) { + $argreset .= " vf.locarg.value='';\n"; + $numbuttons ++; + } + if (!$can_assign->{'int'}) { + my $warning = &mt('You may not specify an initial password, as this is only available when new users use LON-CAPA internal authentication.\n'). + &mt('Your current role does not have rights to create users with that authentication type.'); + $auth_update = <<"END"; + // Currently the initial password field is only supported for internal auth + // (see bug 6368). + if (tf==8 && nw!=0) { + eval('vf.f'+tf+'.selectedIndex=0;') + alert('$warning'); + } +END + } elsif ($numbuttons > 1) { + $auth_update = <<"END"; + // initial password specified, pick internal authentication + if (tf==8 && nw!=0) { + changed_radio('int',document.studentform); + set_auth_radio_buttons('int',document.studentform); +$argreset + } + +END + } + } + return(<=2) && (tf<=5) && (nw!=0)) { eval('vf.f1.selectedIndex=0;') } - // intial password specified, pick internal authentication - if (tf==8 && nw!=0) { - changed_radio('int',document.studentform); - set_auth_radio_buttons('int',document.studentform); - vf.krbarg.value=''; - vf.intarg.value=''; - vf.locarg.value=''; - } + $auth_update } function clearpwd(vf) { @@ -978,8 +1047,9 @@ sub print_upload_manager_form { my ($krbdef,$krbdefdom) = &Apache::loncommon::get_kerberos_defaults($defdom); # + my ($authnum,%can_assign) = &Apache::loncommon::get_assignable_auth($defdom); &print_upload_manager_header($r,$datatoken,$distotal,$krbdefdom,$context, - $permission,$crstype); + $permission,$crstype,\%can_assign); my $i; my $keyfields; if ($total>=0) { @@ -2251,6 +2321,7 @@ END 'type' => "enroll type/action", 'email' => "e-mail address", 'photo' => "photo", + 'lastlogin' => "last login" 'extent' => "extent", 'pr' => "Proceed", 'ca' => "check all", @@ -2299,6 +2370,9 @@ END push(@cols,'groups'); } push(@cols,'email'); + if ($context eq 'course') { + push(@cols,'lastlogin'); + } } my $rolefilter = $env{'form.showrole'}; @@ -2523,6 +2597,11 @@ END Future => 'Future', Expired => 'Expired', ); + # If this is for a single course get last course "log-in". + my %crslogins; + if ($context eq 'course') { + %crslogins=&Apache::lonnet::dump('nohist_crslastlogin',$cdom,$cnum); + } # Get groups, role, permanent e-mail so we can sort on them if # necessary. foreach my $user (keys(%{$userlist})) { @@ -2659,15 +2738,23 @@ END $in{'clicker'} = $clickers; my $role = $in{'role'}; $in{'role'}=&Apache::lonnet::plaintext($sdata->[$index{'role'}],$crstype); - if (! defined($in{'start'}) || $in{'start'} == 0) { - $in{'start'} = &mt('none'); - } else { - $in{'start'} = &Apache::lonlocal::locallocaltime($in{'start'}); + unless ($mode eq 'excel') { + if (! defined($in{'start'}) || $in{'start'} == 0) { + $in{'start'} = &mt('none'); + } else { + $in{'start'} = &Apache::lonlocal::locallocaltime($in{'start'}); + } + if (! defined($in{'end'}) || $in{'end'} == 0) { + $in{'end'} = &mt('none'); + } else { + $in{'end'} = &Apache::lonlocal::locallocaltime($in{'end'}); + } } - if (! defined($in{'end'}) || $in{'end'} == 0) { - $in{'end'} = &mt('none'); - } else { - $in{'end'} = &Apache::lonlocal::locallocaltime($in{'end'}); + if ($context eq 'course') { + my $lastlogin = $crslogins{$in{'username'}.':'.$in{'domain'}.':'.$in{'section'}.':'.$role}; + if ($lastlogin ne '') { + $in{'lastlogin'} = &Apache::lonlocal::locallocaltime($lastlogin); + } } if ($mode eq 'view' || $mode eq 'html' || $mode eq 'autoenroll' || $mode eq 'pickauthor') { $r->print(&Apache::loncommon::start_data_table_row()); @@ -2769,16 +2856,6 @@ END } elsif ($mode eq 'csv') { next if (! defined($CSVfile)); # no need to bother with $linkto - if (! defined($in{'start'}) || $in{'start'} == 0) { - $in{'start'} = &mt('none'); - } else { - $in{'start'} = &Apache::lonlocal::locallocaltime($in{'start'}); - } - if (! defined($in{'end'}) || $in{'end'} == 0) { - $in{'end'} = &mt('none'); - } else { - $in{'end'} = &Apache::lonlocal::locallocaltime($in{'end'}); - } my @line = (); foreach my $item (@cols) { push @line,&Apache::loncommon::csv_translate($in{$item}); @@ -2788,9 +2865,9 @@ END my $col = 0; foreach my $item (@cols) { if ($item eq 'start' || $item eq 'end') { - if (defined($item) && $item != 0) { + if ((defined($in{$item})) && ($in{$item} != 0)) { $excel_sheet->write($row,$col++, - &Apache::lonstathelpers::calc_serial($in{item}), + &Apache::lonstathelpers::calc_serial($in{$item}), $format->{'date'}); } else { $excel_sheet->write($row,$col++,'none'); @@ -3922,15 +3999,22 @@ sub upfile_drop_add { $r->print($groupwarn.'
'); } } - my (%curr_rules,%got_rules,%alerts); + my (%curr_rules,%got_rules,%alerts,%cancreate); my %customroles = &my_custom_roles($crstype); my @permitted_roles = - &roles_on_upload($context,$setting,$crstype,%customroles); + &roles_on_upload($context,$setting,$crstype,%customroles); + my %longtypes = &Apache::lonlocal::texthash( + official => 'Institutional', + unofficial => 'Non-institutional', + ); + my $newuserdom = $env{'request.role.domain'}; + map { $cancreate{$_} = &can_create_user($newuserdom,$context,$_); } keys(%longtypes); # Get new users list foreach my $line (@userdata) { my @secs; my %entries=&Apache::loncommon::record_sep($line); # Determine user name + $entries{$fields{'username'}} =~ s/^\s+|\s+$//g; unless (($entries{$fields{'username'}} eq '') || (!defined($entries{$fields{'username'}}))) { my ($fname, $mname, $lname,$gen) = ('','','',''); @@ -3951,13 +4035,20 @@ sub upfile_drop_add { $gen=$entries{$fields{'gen'}}; } } + if ($entries{$fields{'username'}} ne &LONCAPA::clean_username($entries{$fields{'username'}})) { + my $nowhitespace; + if ($entries{$fields{'username'}} =~ /\s/) { + $nowhitespace = ' - '.&mt('usernames may not contain spaces.'); + } $r->print('
'. &mt('[_1]: Unacceptable username for user [_2] [_3] [_4] [_5]', - ''.$entries{$fields{'username'}}.'',$fname,$mname,$lname,$gen)); + ''.$entries{$fields{'username'}}.'',$fname,$mname,$lname,$gen). + $nowhitespace); next; } else { + $entries{$fields{'domain'}} =~ s/^\s+|\s+$//g; if ($entries{$fields{'domain'}} ne &LONCAPA::clean_domain($entries{$fields{'domain'}})) { $r->print('
'. ''.$entries{$fields{'domain'}}. @@ -4016,6 +4107,7 @@ sub upfile_drop_add { # determine email address my $email=''; if (defined($fields{'email'})) { + $entries{$fields{'email'}} =~ s/^\s+|\s+$//g; if (defined($entries{$fields{'email'}})) { $email=$entries{$fields{'email'}}; unless ($email=~/^[^\@]+\@[^\@]+$/) { $email=''; } @@ -4062,7 +4154,7 @@ sub upfile_drop_add { $role = $defaultrole; } # Clean up whitespace - foreach (\$id,\$fname,\$mname,\$lname,\$gen) { + foreach (\$id,\$fname,\$mname,\$lname,\$gen,\$inststatus) { $$_ =~ s/(\s+$|^\s+)//g; } # check against rules @@ -4071,20 +4163,52 @@ sub upfile_drop_add { my (%rulematch,%inst_results,%idinst_results); my $uhome=&Apache::lonnet::homeserver($username,$userdomain); if ($uhome eq 'no_host') { - next if ($userdomain ne $domain); + if ($userdomain ne $newuserdom) { + if ($context eq 'course') { + $r->print('
'. + &mt('[_1]: The domain specified ([_2]) is different to that of the course.', + ''.$username.'',$userdomain).'
'); + } elsif ($context eq 'author') { + $r->print(&mt('[_1]: The domain specified ([_2]) is different to that of the author.', + ''.$username.'',$userdomain).'
'); + } else { + $r->print(&mt('[_1]: The domain specified ([_2]) is different to that of your current role.', + ''.$username.'',$userdomain).'
'); + } + $r->print(&mt('The user does not already exist, and you may not create a new user in a different domain.')); + next; + } $checkid = 1; $newuser = 1; + my $user = $username.':'.$newuserdom; my $checkhash; my $checks = { 'username' => 1 }; - $checkhash->{$username.':'.$domain} = { 'newuser' => 1, }; + $checkhash->{$username.':'.$newuserdom} = { 'newuser' => 1, }; &Apache::loncommon::user_rule_check($checkhash,$checks, \%alerts,\%rulematch,\%inst_results,\%curr_rules, \%got_rules); if (ref($alerts{'username'}) eq 'HASH') { - if (ref($alerts{'username'}{$domain}) eq 'HASH') { - next if ($alerts{'username'}{$domain}{$username}); + if (ref($alerts{'username'}{$newuserdom}) eq 'HASH') { + if ($alerts{'username'}{$newuserdom}{$username}) { + $r->print('
'. + &mt('[_1]: matches the username format at your institution, but is not known to 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'}) { + $usertype = 'official'; + } + } + unless ($cancreate{$usertype}) { + my $showtype = $longtypes{$usertype}; + $r->print('
'. + &mt('[_1]: The user does not exist, and you are not permitted to create users of type: [_2].',''.$username.'',$showtype)); + next; + } } else { if ($context eq 'course' || $context eq 'author') { if ($userdomain eq $domain ) { @@ -4135,7 +4259,12 @@ sub upfile_drop_add { \%got_rules); if (ref($alerts{'id'}) eq 'HASH') { if (ref($alerts{'id'}{$userdomain}) eq 'HASH') { - next if ($alerts{'id'}{$userdomain}{$id}); + if ($alerts{'id'}{$userdomain}{$id}) { + $r->print(&mt('[_1]: has a student/employee ID matching the format at your institution, but the ID is found by your directory service.', + ''.$username.'').'
'. + &mt('Consequently, the user was not created.')); + next; + } } } } @@ -4230,7 +4359,7 @@ sub upfile_drop_add { } } # end of foreach (@userdata) # Flush the course logs so reverse user roles immediately updated - &Apache::lonnet::flushcourselogs(); + $r->register_cleanup(\&Apache::lonnet::flushcourselogs); $r->print("

\n

\n".&mt('Processed [quant,_1,user].',$counts{'user'}). "

\n"); if ($counts{'role'} > 0) { @@ -4686,12 +4815,12 @@ sub update_user_list { } } elsif ($choice eq 'chgdates') { $r->print(&mt("$result_text{'ok'}{$choice} role of '[_1]' in [_2] for '[_3]' - [_4]",$plrole,$extent, - ''.&Apache::loncommon::plainname($uname.':'.$udom).'', + ''.&Apache::loncommon::plainname($uname,$udom).'', $dates).'
'); $count ++; } else { $r->print(&mt("$result_text{'ok'}{$choice} role of '[_1]' in [_2] for '[_3]'.",$plrole,$extent, - ''.&Apache::loncommon::plainname($uname.':'.$udom).''). + ''.&Apache::loncommon::plainname($uname,$udom).''). '
'); $count ++; } @@ -4699,7 +4828,7 @@ sub update_user_list { $r->print( &mt("Error $result_text{'error'}{$choice} [_1] in [_2] for '[_3]': [_4].", $plrole,$extent, - ''.&Apache::loncommon::plainname($uname.':'.$udom).'', + ''.&Apache::loncommon::plainname($uname,$udom).'', $result).'
'); } } @@ -4722,7 +4851,7 @@ sub update_user_list { $r->print('

'.&mt('Re-enabling will re-activate data for the role.').'

'); } # Flush the course logs so reverse user roles immediately updated - &Apache::lonnet::flushcourselogs(); + $r->register_cleanup(\&Apache::lonnet::flushcourselogs); } if ($env{'form.makedatesdefault'}) { if ($choice eq 'chgdates' || $choice eq 'reenable' || $choice eq 'activate') { @@ -4937,7 +5066,7 @@ sub setsections_javascript { accr => 'A course coordinator role will be added with access to all sections.', acor => 'A coordinator role will be added with access to all sections', inea => 'In each course, each user may only have one student role at a time.', - inec => 'In each community, each user may only have one member role at a time.', + inco => 'In each community, each user may only have one member role at a time.', youh => 'You had selected ', secs => 'sections.', plmo => 'Please modify your selections so they include no more than one section.', @@ -5158,18 +5287,40 @@ sub can_modify_userinfo { } sub check_usertype { - my ($dom,$uname,$rules) = @_; + my ($dom,$uname,$rules,$curr_rules,$got_rules) = @_; my $usertype; - if (ref($rules) eq 'HASH') { - my @user_rules = keys(%{$rules}); - if (@user_rules > 0) { - my %rule_check = &Apache::lonnet::inst_rulecheck($dom,$uname,undef,'username',\@user_rules); - if (keys(%rule_check) > 0) { - $usertype = 'unofficial'; - foreach my $item (keys(%rule_check)) { - if ($rule_check{$item}) { - $usertype = 'official'; - last; + if ((ref($got_rules) eq 'HASH') && (ref($curr_rules) eq 'HASH')) { + if (!$got_rules->{$dom}) { + my %domconfig = &Apache::lonnet::get_dom('configuration', + ['usercreation'],$dom); + if (ref($domconfig{'usercreation'}) eq 'HASH') { + foreach my $item ('username','id') { + if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') { + $curr_rules->{$dom}{$item} = + $domconfig{'usercreation'}{$item.'_rule'}; + } + } + } + $got_rules->{$dom} = 1; + } + if (ref($rules) eq 'HASH') { + my @user_rules; + if (ref($curr_rules->{$dom}{'username'}) eq 'ARRAY') { + foreach my $rule (keys(%{$rules})) { + if (grep(/^\Q$rule\E/,@{$curr_rules->{$dom}{'username'}})) { + push(@user_rules,$rule); + } + } + } + if (@user_rules > 0) { + my %rule_check = &Apache::lonnet::inst_rulecheck($dom,$uname,undef,'username',\@user_rules); + if (keys(%rule_check) > 0) { + $usertype = 'unofficial'; + foreach my $item (keys(%rule_check)) { + if ($rule_check{$item}) { + $usertype = 'official'; + last; + } } } }