--- loncom/interface/lonuserutils.pm 2010/01/14 17:02:49 1.110 +++ loncom/interface/lonuserutils.pm 2010/09/20 00:21:08 1.125 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Utility functions for managing LON-CAPA user accounts # -# $Id: lonuserutils.pm,v 1.110 2010/01/14 17:02:49 bisitz Exp $ +# $Id: lonuserutils.pm,v 1.125 2010/09/20 00:21:08 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -295,7 +295,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 +310,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 @@ -585,6 +586,31 @@ END ############################################################### ############################################################### sub upload_manager_javascript_forward_associate { + my ($can_assign) = @_; + my $auth_update; + if (ref($can_assign) eq 'HASH') { + if (keys(%{$can_assign}) > 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); +END + } + if ($can_assign->{'krb4'} || $can_assign->{'krb5'}) { + $auth_update .= " vf.krbarg.value='';\n"; + } + if ($can_assign->{'int'}) { + $auth_update .= " vf.intarg.value='';\n"; + } + if ($can_assign->{'loc'}) { + $auth_update .= " vf.locarg.value='';\n"; + } + $auth_update .= " + }\n"; + } + return(< 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); +END + } + if ($can_assign->{'krb'}) { + $auth_update .= " vf.krbarg.value='';\n"; + } + if ($can_assign->{'int'}) { + $auth_update .= " vf.intarg.value='';\n"; + } + if ($can_assign->{'loc'}) { + $auth_update .= " vf.locarg.value='';\n"; + } + $auth_update .= " + }\n"; + } 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 +1012,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) { @@ -1448,8 +1483,12 @@ sub print_userlist { } if (!(($context eq 'domain') && (($env{'form.roletype'} eq 'course') || ($env{'form.roletype'} eq 'community')))) { - $r->print(' '.&list_submit_button(&mt('Update Display')). - "\n

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

\n" + .'

' + .&list_submit_button(&mt('Update Display')) + ."

\n" + ); } my ($indexhash,$keylist) = &make_keylist_array(); my (%userlist,%userinfo,$clearcoursepick); @@ -1468,9 +1507,11 @@ sub print_userlist { $title = &mt('Select Communities'); $warning = &mt('Warning: data retrieval for multiple communities can take considerable time, as this operation is not currently optimized.'); } + my @standardnames = &Apache::loncommon::get_standard_codeitems(); my $courseform = &Apache::lonhtmlcommon::course_selection($formname,$numcodes, - $codetitles,$idlist,$idlist_titles,$crstype); + $codetitles,$idlist,$idlist_titles,$crstype, + \@standardnames); $r->print('

'.&Apache::lonhtmlcommon::start_pick_box()."\n". &Apache::lonhtmlcommon::start_pick_box()."\n". &Apache::lonhtmlcommon::row_title($title,'LC_oddrow_value')."\n". @@ -1688,7 +1729,7 @@ sub role_filter { $role_select .= ''; $output = ''; + .' '; } return $output; } @@ -1728,7 +1769,7 @@ sub section_group_filter { } if (@options > 0) { my $currsel; - $markup = ''."\n"; foreach my $option ('all','none',@options) { $currsel = ''; if ($env{'form.'.$name{$item}} eq $option) { @@ -1743,7 +1784,9 @@ sub section_group_filter { $markup .= ''."\n"; } $markup .= ''."\n"; - $output .= (' 'x3).''; + $output .= (' 'x3).'' + .'' + .' '; } } return $output; @@ -1917,7 +1960,7 @@ function setCourseCat(formname) { if (formname.Year.options[formname.Year.selectedIndex].value == -1) { return; } - courseSet('Year'); + courseSet('$codetitles[0]'); for (var j=0; j{'username'}; } else { $output = '{'username'}','$in->{'domain'}'".')" />'. + "'$in->{'username'}','$in->{'domain'}'".')">'. $in->{'username'}.''; } return $output; @@ -3167,9 +3210,9 @@ ENDJS my %lt = &Apache::lonlocal::texthash ( chac => 'Access dates to apply for selected users', chse => 'Changes in section affiliation to apply to selected users', - fors => 'For student roles changing the section, will result in a section switch as students may only be in one section of a course at a time.', - forn => 'For a role in a course that is not a student role, a user may have roles in more than one section of a course at a time.', - reta => "Retain each user's current section affiliations?", + fors => 'For student roles, changing the section will result in a section switch as students may only be in one section of a course at a time.', + forn => 'For a course role that is not "student", users may have roles in more than one section at a time.', + reta => "Retain each user's current section affiliations?", dnap => '(Does not apply to student roles).', ); my ($date_items,$headertext); @@ -3187,14 +3230,13 @@ ENDJS $permission,$crstype); } $output .= '

'.$headertext.'

'. - '
'."\n". + ''."\n". $date_items; if ($context eq 'course' && $env{'form.bulkaction'} eq 'chgsec') { my ($cnum,$cdom) = &get_course_identity(); - my $crstype = &Apache::loncommon::course_type(); if ($crstype eq 'Community') { - $lt{'fors'} = &mt('For member roles changing the section, will result in a section switch as members may only be in one section of a community at a time.'); - $lt{'forn'} = &mt('For a role in a community that is not a member role, a user may have roles in more than one section of a community at a time.'); + $lt{'fors'} = &mt('For member roles, changing the section will result in a section switch, as members may only be in one section of a community at a time.'); + $lt{'forn'} = &mt('For a community role that is not "member", users may have roles in more than one section at a time.'); $lt{'dnap'} = &mt('(Does not apply to member roles).'); } my $info; @@ -3235,7 +3277,7 @@ sub section_picker { my ($cdom,$cnum,$role,$rowtitle,$permission,$context,$mode,$crstype) = @_; my %sections_count = &Apache::loncommon::get_sections($cdom,$cnum); my $sections_select .= &course_sections(\%sections_count,$role); - my $secbox = '

'.&Apache::lonhtmlcommon::start_pick_box()."\n"; + my $secbox = '

'.&Apache::lonhtmlcommon::start_pick_box()."\n"; if ($mode eq 'upload') { my ($options,$cb_script,$coursepick) = &default_role_selector($context,1,$crstype); @@ -3248,7 +3290,7 @@ sub section_picker { ''.&mt('Existing sections')."\n". '
'.$sections_select.''. &mt('New section').'
'."\n". - ''."\n". + ''."\n". ''."\n". ''."\n"; } else { @@ -3257,7 +3299,7 @@ sub section_picker { $env{'request.course.sec'}; } $secbox .= &Apache::lonhtmlcommon::row_closure(1)."\n". - &Apache::lonhtmlcommon::end_pick_box().'

'; + &Apache::lonhtmlcommon::end_pick_box().'
'; return $secbox; } @@ -3398,7 +3440,7 @@ sub results_header_row { if ($rolefilter eq 'Any') { $description .= &mt('All users with co-author roles in domain',$showfilter); } else { - $description .= &mt('All co-authors in domain with [_1] roles',$rolefilter); + $description .= &mt('All co-authors in domain with [_1] roles',$rolefilter); } } } elsif (($env{'form.roletype'} eq 'course') || @@ -3915,10 +3957,15 @@ 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', + ); + map { $cancreate{$_} = &can_create_user($domain,$context,$_); } keys(%longtypes); # Get new users list foreach my $line (@userdata) { my @secs; @@ -4064,9 +4111,16 @@ 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 $domain) { + $r->print('
'. + &mt('[_1]: The domain specified ([_2]) is different to that of the course.', + ''.$username.'',$userdomain).'
'. + &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.':'.$domain; my $checkhash; my $checks = { 'username' => 1 }; $checkhash->{$username.':'.$domain} = { 'newuser' => 1, }; @@ -4075,9 +4129,26 @@ sub upfile_drop_add { \%got_rules); if (ref($alerts{'username'}) eq 'HASH') { if (ref($alerts{'username'}{$domain}) eq 'HASH') { - next if ($alerts{'username'}{$domain}{$username}); + if ($alerts{'username'}{$domain}{$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: $showtype.",''.$username.'')); + next; + } } else { if ($context eq 'course' || $context eq 'author') { if ($userdomain eq $domain ) { @@ -4128,7 +4199,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; + } } } } @@ -4223,7 +4299,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) { @@ -4387,10 +4463,9 @@ sub print_drop_menu { # ================================================================== Phase four sub update_user_list { - my ($r,$context,$setting,$choice) = @_; + my ($r,$context,$setting,$choice,$crstype) = @_; my $now = time; my $count=0; - my $crstype; if ($context eq 'course') { $crstype = &Apache::loncommon::course_type(); } @@ -4405,7 +4480,7 @@ sub update_user_list { 'reenable' => 'Re-enabled', 'activate' => 'Activated', 'chgdates' => 'Changed Access Dates for', - 'chgsec' => 'Changed section for', + 'chgsec' => 'Changed section(s) for', 'drop' => 'Dropped', }, error => {'revoke' => 'revoking', @@ -4422,8 +4497,9 @@ sub update_user_list { ($startdate,$enddate) = &get_dates_from_form(); } foreach my $item (@changelist) { - my ($role,$uname,$udom,$cid,$sec,$scope,$result,$type,$locktype,@sections, - $scopestem); + my ($role,$uname,$udom,$cid,$sec,$scope,$result,$type,$locktype, + @sections,$scopestem,$singlesec,$showsecs,$warn_singlesec, + $nothingtodo,$keepnosection); if ($choice eq 'drop') { ($uname,$udom,$sec) = split(/:/,$item,-1); $role = 'st'; @@ -4522,14 +4598,23 @@ sub update_user_list { } elsif ($choice eq 'chgsec') { my (@newsecs,$revresult,$nochg,@retained); if (($role ne 'cc') && ($role ne 'co')) { - @newsecs = split(/,/,$env{'form.newsecs'}); + my @secs = sort(split(/,/,$env{'form.newsecs'})); + if (@secs) { + my %curr_groups = &Apache::longroup::coursegroups(); + foreach my $sec (@secs) { + next if (($sec =~ /\W/) || ($sec eq 'none') || + (exists($curr_groups{$sec}))); + push(@newsecs,$sec); + } + } } # remove existing section if not to be retained. - if (!$env{'form.retainsec'}) { + if (!$env{'form.retainsec'} || ($role eq 'st')) { if ($sec eq '') { if (@newsecs == 0) { - $result = &mt('No change in section assignment (none)'); + $result = 'ok'; $nochg = 1; + $nothingtodo = 1; } else { $revresult = &Apache::lonnet::revokerole($udom,$uname, @@ -4556,13 +4641,23 @@ sub update_user_list { } else { if ($sec eq '') { $nochg = 1; - } else { + $keepnosection = 1; + } else { push(@retained,$sec); } } # add new sections + my (@diffs,@shownew); + if (@retained) { + @diffs = &Apache::loncommon::compare_arrays(\@retained,\@newsecs); + } else { + @diffs = @newsecs; + } if (@newsecs == 0) { - if (!$nochg) { + if ($nochg) { + $result = 'ok'; + $nothingtodo = 1; + } 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); @@ -4571,22 +4666,58 @@ sub update_user_list { $result = &Apache::lonnet::assignrole($udom,$uname,$newscope,$role,$end,$start,'','',$context); } } + $showsecs = &mt('No section'); + } elsif (@diffs == 0) { + $result = 'ok'; + $nothingtodo = 1; } else { - foreach my $newsec (@newsecs) { + 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); + if (@newsecs > 1) { + my $showsingle; + if ($newsec eq '') { + $showsingle = &mt('No section'); + } else { + $showsingle = $newsec; + } + if ($crstype eq 'Community') { + $warn_singlesec = &mt('Although more than one section was indicated, a role was only added for the first section - [_1], as each community member may only be in one section at a time.',''.$showsingle.''); + } else { + $warn_singlesec = &mt('Although more than one section was indicated, a role was only added for the first section - [_1], as each student may only be in one section of a course at a time.',''.$showsingle.''); + } + $showsecs = $showsingle; + last; + } else { + if ($newsec eq '') { + $showsecs = &mt('No section'); + } else { + $showsecs = $newsec; + } + } } else { my $newscope = $scopestem; if ($newsec ne '') { $newscope .= '/'.$newsec; + push(@shownew,$newsec); } $result = &Apache::lonnet::assignrole($udom,$uname, $newscope,$role,$end,$start); + } } } } + unless ($role eq 'st') { + unless ($showsecs) { + my @tolist = sort(@shownew,@retained); + if ($keepnosection) { + push(@tolist,&mt('No section')); + } + $showsecs = join(', ',@tolist); + } + } } } my $extent = $scope; @@ -4597,13 +4728,48 @@ sub update_user_list { } } if ($result eq 'ok' || $result eq 'ok:') { - $r->print(&mt("$result_text{'ok'}{$choice} role of '[_1]' in [_2] for [_3]", - $plrole,$extent,$uname.':'.$udom).'
'); - $count++; + my $dates; + if (($choice eq 'chgsec') || ($choice eq 'chgdates')) { + $dates = &dates_feedback($start,$end,$now); + } + if ($choice eq 'chgsec') { + if ($nothingtodo) { + $r->print(&mt("Section assignment for role of '[_1]' in [_2] for '[_3]' unchanged.",$plrole,$extent,''. + &Apache::loncommon::plainname($uname,$udom). + '').' '); + if ($sec eq '') { + $r->print(&mt('[_1]No section[_2] - [_3]','','',$dates)); + } else { + $r->print(&mt('Section(s): [_1] - [_2]', + ''.$showsecs.'',$dates)); + } + $r->print('
'); + } else { + $r->print(&mt("$result_text{'ok'}{$choice} role of '[_1]' in [_2] for '[_3]' to [_4] - [_5]",$plrole,$extent, + ''.&Apache::loncommon::plainname($uname,$udom).'', + ''.$showsecs.'',$dates).'
'); + $count ++; + } + if ($warn_singlesec) { + $r->print('
'.$warn_singlesec.'
'); + } + } 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).'', + $dates).'
'); + $count ++; + } else { + $r->print(&mt("$result_text{'ok'}{$choice} role of '[_1]' in [_2] for '[_3]'.",$plrole,$extent, + ''.&Apache::loncommon::plainname($uname,$udom).''). + '
'); + $count ++; + } } else { $r->print( - &mt("Error $result_text{'error'}{$choice} [_1] in [_2] for [_3]: [_4].", - $plrole,$extent,$uname.':'.$udom,$result).'
'); + &mt("Error $result_text{'error'}{$choice} [_1] in [_2] for '[_3]': [_4].", + $plrole,$extent, + ''.&Apache::loncommon::plainname($uname,$udom).'', + $result).'
'); } } $r->print(''."\n"); @@ -4619,7 +4785,7 @@ sub update_user_list { } } } - $r->print('

'.&mt("$result_text{'ok'}{$choice} role(s) for [quant,_1,user,users,no users].",$count).'

'); + $r->print('

'.&mt("$result_text{'ok'}{$choice} for [quant,_1,user role,user roles,no user roles].",$count).'

'); if ($count > 0) { if ($choice eq 'revoke' || $choice eq 'drop') { $r->print('

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

'); @@ -4639,6 +4805,27 @@ sub update_user_list { $r->print(''.$linktext.'
'."\n"); } +sub dates_feedback { + my ($start,$end,$now) = @_; + my $dates; + if ($start < $now) { + if ($end == 0) { + $dates .= &mt('role(s) active now; no end date'); + } elsif ($end > $now) { + $dates = &mt('role(s) active now; ends [_1].',&Apache::lonlocal::locallocaltime($end)); + } else { + $dates = &mt('role(s) expired: [_1].',&Apache::lonlocal::locallocaltime($end)); + } + } else { + if ($end == 0 || $end > $now) { + $dates = &mt('future role(s); starts: [_1].',&Apache::lonlocal::locallocaltime($start)); + } else { + $dates = &mt('role(s) expired: [_1].',&Apache::lonlocal::locallocaltime($end)); + } + } + return $dates; +} + sub classlist_drop { my ($scope,$uname,$udom,$now) = @_; my ($cdom,$cnum) = ($scope=~m{^/($match_domain)/($match_courseid)}); @@ -4819,7 +5006,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 on 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.', @@ -4827,16 +5014,21 @@ sub setsections_javascript { plch => 'Please choose a different section name.', mnot => 'may not be used as a section name, as it is the name of a course group.', secn => 'Section names and group names must be distinct. Please choose a different section name.', + nonw => 'Section names may only contain letters or numbers.', ); $setsection_js .= <<"ENDSECCODE"; function setSections(formname,crstype) { var re1 = /^currsec_/; + var re2 =/\\W/; + var trimleading = /^\\s+/; + var trimtrailing = /\\s+\$/; var groups = new Array($groupslist); for (var i=0;i 0) { - if (formname.elements[i+1].value != "" && formname.elements[i+1].value != null) { - sections = sections + "," + formname.elements[i+1].value; - } - } - else { - sections = formname.elements[i+1].value; - } - var newsecs = formname.elements[i+1].value; - var numsplit; + var newsecs = formname.elements[num+1].value; + var validsecs = new Array(); + var validsecstr = ''; + var badsecs = new Array(); if (newsecs != null && newsecs != "") { - numsplit = newsecs.split(/,/g); - numsec = numsec + numsplit.length; + var numsplit; + if (newsecs.indexOf(',') == -1) { + numsplit = new Array(newsecs); + } else { + numsplit = newsecs.split(/,/g); + } + for (var m=0; m 0) { + alert("$alerts{'nonw'}\\n$alerts{'plch'}"); + return; + } + numsec = numsec + validsecs.length; } - if ((role == 'st') && (numsec > 1)) { if (crstype == 'Community') { alert("$alerts{'inea'} $alerts{'youh'} "+numsec+" $alerts{'secs'}\\n$alerts{'plmo'}"); @@ -4887,25 +5101,36 @@ function setSections(formname,crstype) { alert("$alerts{'inco'} $alerts{'youh'} "+numsec+" $alerts{'secs'}\\n$alerts{'plmo'}"); } return; - } - else { - if (numsplit != null) { - for (var j=0; j