--- loncom/interface/lonuserutils.pm 2008/12/12 16:53:37 1.73.2.1 +++ loncom/interface/lonuserutils.pm 2012/02/09 22:06:16 1.136.6.3 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Utility functions for managing LON-CAPA user accounts # -# $Id: lonuserutils.pm,v 1.73.2.1 2008/12/12 16:53:37 raeburn Exp $ +# $Id: lonuserutils.pm,v 1.136.6.3 2012/02/09 22:06:16 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); @@ -86,12 +87,12 @@ 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) = @_; + $end,$start,$checkid,$inststatus) = @_; my ($scope,$userresult,$authresult,$roleresult,$idresult); if ($setting eq 'course' || $context eq 'course') { $scope = '/'.$cid; $scope =~ s/\_/\//g; - if ($role ne 'cc' && $sec ne '') { + if (($role ne 'cc') && ($role ne 'co') && ($sec ne '')) { $scope .='/'.$sec; } } elsif ($context eq 'domain') { @@ -124,7 +125,7 @@ sub modifyuserrole { $userresult = &Apache::lonnet::modifyuser($udom,$uname,$uid,$umode,$upass,$first, $middle,$last,$gene,$forceid,$desiredhome, - $email,$role,$start,$end); + $email,$inststatus); if ($userresult eq 'ok') { if ($role ne '') { $role =~ s/_/\//g; @@ -235,7 +236,7 @@ sub domain_roles_select { # domain context # # Role types - my @roletypes = ('domain','author','course'); + my @roletypes = ('domain','author','course','community'); my %lt = &role_type_names(); # # build up the menu information to be passed to @@ -247,6 +248,10 @@ sub domain_roles_select { foreach my $roletype (@roletypes) { # set up the text for this domain $select_menus{$roletype}->{'text'}= $lt{$roletype}; + my $crstype; + if ($roletype eq 'community') { + $crstype = 'Community'; + } # we want a choice of 'default' as the default in the second menu if ($env{'form.roletype'} ne '') { $select_menus{$roletype}->{'default'} = $env{'form.showrole'}; @@ -261,7 +266,7 @@ sub domain_roles_select { @roles = &construction_space_roles(); } else { my $custom = 1; - @roles = &course_roles('domain',undef,$custom); + @roles = &course_roles('domain',undef,$custom,$roletype); } my $order = ['Any',@roles]; $select_menus{$roletype}->{'order'} = $order; @@ -271,14 +276,15 @@ sub domain_roles_select { &mt('Custom role'); } else { $select_menus{$roletype}->{'select2'}->{$role} = - &Apache::lonnet::plaintext($role); + &Apache::lonnet::plaintext($role,$crstype); } } $select_menus{$roletype}->{'select2'}->{'Any'} = &mt('Any'); } my $result = &Apache::loncommon::linked_select_forms ('studentform',(' 'x3).&mt('Role: '),$env{'form.roletype'}, - 'roletype','showrole',\%select_menus,['domain','author','course']); + 'roletype','showrole',\%select_menus, + ['domain','author','course','community']); return $result; } @@ -290,7 +296,8 @@ sub hidden_input { } sub print_upload_manager_header { - my ($r,$datatoken,$distotal,$krbdefdom,$context,$permission)=@_; + my ($r,$datatoken,$distotal,$krbdefdom,$context,$permission,$crstype, + $can_assign)=@_; my $javascript; # if (! exists($env{'form.upfile_associate'})) { @@ -304,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 @@ -327,13 +334,16 @@ sub print_upload_manager_header { my $javascript_validations = &javascript_validations('upload',$krbdefdom,$password_choice,undef, $env{'request.role.domain'},$context, - $groupslist); - my $checked=(($env{'form.noFirstLine'})?' checked="checked" ':''); - $r->print(&mt('Total number of records found in file: [_1]',''.$distotal.''). - "
\n"); + $groupslist,$crstype); + my $checked=(($env{'form.noFirstLine'})?' checked="checked"':''); + $r->print('

' + .&mt('Total number of records found in file: [_1]' + ,''.$distotal.'') + ."

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

'. &mt('Identify fields in uploaded list')."

\n"); - $r->print(&mt('Enter as many fields as you can.
The system will inform you and bring you back to this page,
if the data selected are insufficient to add users.')."
\n"); + $r->print(&mt('Enter as many fields as you can.').'
'. + &mt('The system will inform you and bring you back to this page, [_1]if the data selected are insufficient to add users.','
')."
\n"); $r->print(&hidden_input('action','upload'). &hidden_input('state','got_file'). &hidden_input('associate',''). @@ -341,21 +351,24 @@ sub print_upload_manager_header { &hidden_input('fileupload',$env{'form.fileupload'}). &hidden_input('upfiletype',$env{'form.upfiletype'}). &hidden_input('upfile_associate',$env{'form.upfile_associate'})); - $r->print('
'; } return $output; } @@ -1554,7 +1794,7 @@ sub section_group_filter { $env{'form.'.$name{$item}} = 'all'; } if ($item eq 'sec') { - if ($env{'form.showrole'} eq 'cc') { + if (($env{'form.showrole'} eq 'cc') || ($env{'form.showrole'} eq 'co')) { $env{'form.'.$name{$item}} = 'none'; } my %sections_count = &Apache::loncommon::get_sections($cdom,$cnum); @@ -1565,11 +1805,11 @@ 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) { - $currsel = ' selected="selected" '; + $currsel = ' selected="selected"'; } $markup .= ' '."\n"; } $markup .= ''."\n"; - $output .= (' 'x3).''; + $output .= (' 'x3).'' + .'' + .' '; } } return $output; @@ -1633,7 +1875,8 @@ sub gather_userinfo { $indexhash,$uniqid,$userlist); } } - } elsif ($env{'form.roletype'} eq 'course') { + } elsif (($env{'form.roletype'} eq 'course') || + ($env{'form.roletype'} eq 'community')) { ($userdata{'username'},$userdata{'domain'},$userdata{'role'}) = split(/:/,$item); if (ref($rolehash->{$item}) eq 'HASH') { @@ -1683,7 +1926,8 @@ sub gather_userinfo { sub build_user_record { my ($context,$userdata,$userinfo,$indexhash,$record_key,$userlist) = @_; next if ($userdata->{'start'} eq '-1' && $userdata->{'end'} eq '-1'); - if (!(($context eq 'domain') && ($env{'form.roletype'} eq 'course'))) { + if (!(($context eq 'domain') && (($env{'form.roletype'} eq 'course') + && ($env{'form.roletype'} eq 'community')))) { &process_date_info($userdata); } my $username = $userdata->{'username'}; @@ -1752,7 +1996,7 @@ function setCourseCat(formname) { if (formname.Year.options[formname.Year.selectedIndex].value == -1) { return; } - courseSet('Year'); + courseSet('$codetitles[0]'); for (var j=0; j 1) { my @course_ids = split(/&&/,$env{'form.courselist'}); @@ -1908,15 +2156,15 @@ sub aggregate_user_info { sub process_date_info { my ($userdata) = @_; my $now = time; - $userdata->{'status'} = &mt('Active'); + $userdata->{'status'} = 'Active'; if ($userdata->{'start'} > 0) { if ($now < $userdata->{'start'}) { - $userdata->{'status'} = &mt('Future'); + $userdata->{'status'} = 'Future'; } } if ($userdata->{'end'} > 0) { if ($now > $userdata->{'end'}) { - $userdata->{'status'} = &mt('Expired'); + $userdata->{'status'} = 'Expired'; } } return; @@ -1948,9 +2196,10 @@ sub show_users_list { $sortby = 'username'; } my $setting = $env{'form.roletype'}; - my ($cid,$cdom,$cnum,$classgroups,$displayphotos,$displayclickers); + my ($cid,$cdom,$cnum,$classgroups,$displayphotos,$displayclickers,$crstype); if ($context eq 'course') { $cid = $env{'request.course.id'}; + $crstype = &Apache::loncommon::course_type(); ($cnum,$cdom) = &get_course_identity($cid); ($classgroups) = &Apache::loncoursedata::get_group_memberships( $userlist,$keylist,$cdom,$cnum); @@ -1968,6 +2217,7 @@ sub show_users_list { if ($env{'course.'.$cid.'.internal.showphoto'}) { $r->print(' '); } @@ -1983,6 +2234,12 @@ function photowindow(photolink) { END } + } elsif ($context eq 'domain') { + if ($setting eq 'community') { + $crstype = 'Community'; + } elsif ($setting eq 'course') { + $crstype = 'Course'; + } } if ($mode ne 'autoenroll' && $mode ne 'pickauthor') { my $check_uncheck_js = &Apache::loncommon::check_uncheck_jscript(); @@ -1991,6 +2248,7 @@ END $r->print(< +// $date_sec_selector @@ -2050,21 +2320,25 @@ END 'status' => "status", 'role' => "role", 'type' => "enroll type/action", - 'email' => "email address", + 'email' => "e-mail address", 'photo' => "photo", + 'lastlogin' => "last login", 'extent' => "extent", 'pr' => "Proceed", 'ca' => "check all", 'ua' => "uncheck all", 'ac' => "Action to take for selected users", 'link' => "Behavior of clickable username link for each user", - 'aboutme' => "Display a user's personal page", + 'aboutme' => "Display a user's personal information page", 'owin' => "Open in a new window", 'modify' => "Modify a user's information", + 'track' => "View a user's recent activity", 'clicker' => "Clicker-ID", ); if ($context eq 'domain' && $env{'form.roletype'} eq 'course') { $lt{'extent'} = &mt('Course(s): description, section(s), status'); + } elsif ($context eq 'domain' && $env{'form.roletype'} eq 'community') { + $lt{'extent'} = &mt('Communities: description, section(s), status'); } elsif ($context eq 'author') { $lt{'extent'} = &mt('Author'); } @@ -2076,43 +2350,53 @@ END if ($context eq 'course') { push(@cols,'section'); } - if (!($context eq 'domain' && $env{'form.roletype'} eq 'course')) { + if (!($context eq 'domain' && ($env{'form.roletype'} eq 'course') + && ($env{'form.roletype'} eq 'community'))) { push(@cols,('start','end')); } if ($env{'form.showrole'} eq 'Any' || $env{'form.showrole'} eq 'cr') { push(@cols,'role'); } if ($context eq 'domain' && ($env{'form.roletype'} eq 'author' || - $env{'form.roletype'} eq 'course')) { + $env{'form.roletype'} eq 'course' || + $env{'form.roletype'} eq 'community')) { push (@cols,'extent'); } if (($statusmode eq 'Any') && - (!($context eq 'domain' && $env{'form.roletype'} eq 'course'))) { + (!($context eq 'domain' && (($env{'form.roletype'} eq 'course') + || ($env{'form.roletype'} eq 'community'))))) { push(@cols,'status'); } if ($context eq 'course') { push(@cols,'groups'); } push(@cols,'email'); + if ($context eq 'course') { + push(@cols,'lastlogin'); + } } my $rolefilter = $env{'form.showrole'}; if ($env{'form.showrole'} eq 'cr') { $rolefilter = &mt('custom'); } elsif ($env{'form.showrole'} ne 'Any') { - $rolefilter = &Apache::lonnet::plaintext($env{'form.showrole'}); + $rolefilter = &Apache::lonnet::plaintext($env{'form.showrole'},$crstype); } my $results_description; if ($mode ne 'autoenroll') { $results_description = &results_header_row($rolefilter,$statusmode, - $context,$permission,$mode); + $context,$permission,$mode,$crstype); $r->print(''.$results_description.'

'); } my ($output,$actionselect,%canchange,%canchangesec); if ($mode eq 'html' || $mode eq 'view' || $mode eq 'autoenroll' || $mode eq 'pickauthor') { if ($mode ne 'autoenroll' && $mode ne 'pickauthor') { if ($permission->{'cusr'}) { - $actionselect = &select_actions($context,$setting,$statusmode,$formname); + unless (($context eq 'domain') && + (($setting eq 'course') || ($setting eq 'community'))) { + $actionselect = + &select_actions($context,$setting,$statusmode,$formname); + } } $r->print(< @@ -2123,7 +2407,7 @@ END END if ($actionselect) { $output .= <<"END"; -
$lt{'ac'} +
$lt{'ac'} $actionselect

 
@@ -2132,9 +2416,9 @@ END if ($env{'form.showrole'} eq 'Any') { my $custom = 1; if ($context eq 'domain') { - @allroles = &roles_by_context($setting,$custom); + @allroles = &roles_by_context($setting,$custom,$crstype); } else { - @allroles = &roles_by_context($context,$custom); + @allroles = &roles_by_context($context,$custom,$crstype); } } else { @allroles = ($env{'form.showrole'}); @@ -2168,12 +2452,18 @@ END } } } - $output .= '
'.$lt{'link'}.''. + $output .= '
'.$lt{'link'}.''. ''; my @linkdests = ('aboutme'); 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'); + } + $output .= '
'; my $usernamelink = $env{'form.usernamelink'}; if ($usernamelink eq '') { @@ -2182,15 +2472,15 @@ END foreach my $item (@linkdests) { my $checkedstr = ''; if ($item eq $usernamelink) { - $checkedstr = ' checked="checked" '; + $checkedstr = ' checked="checked"'; } - $output .= '
'; + $output .= '
'; } my $checkwin; if ($env{'form.userwin'}) { - $checkwin = 'checked = "checked"'; + $checkwin = ' checked="checked"'; } - $output .= '
'.$lt{'owin'}.'
'; + $output .= ''.$lt{'owin'}.'
'; } $output .= "\n".''."\n". &Apache::loncommon::start_data_table(). @@ -2200,11 +2490,7 @@ END $lt{'type'} "; } else { - if ($mode eq 'pickauthor') { - $output .= "\n".' '."\n"; - } else { - $output .= "\n".''.&mt('Count').''."\n"; - } + $output .= "\n".' '."\n"; if ($actionselect) { $output .= ''.&mt('Select').''."\n"; } @@ -2259,9 +2545,13 @@ END time.'_'.rand(1000000000).'.csv'; unless ($CSVfile = Apache::File->new('>/home/httpd'.$CSVfilename)) { $r->log_error("Couldn't open $CSVfilename for output $!"); - $r->print(&mt('Problems occurred in writing the CSV file. ' - .'This error has been logged. ' - .'Please alert your LON-CAPA administrator.')); + $r->print( + '

' + .&mt('Problems occurred in writing the CSV file.') + .' '.&mt('This error has been logged.') + .' '.&mt('Please alert your LON-CAPA administrator.') + .'

' + ); $CSVfile = undef; } # @@ -2303,6 +2593,16 @@ END $grpfilter = 'all'; } } + my %ltstatus = &Apache::lonlocal::texthash( + Active => 'Active', + 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})) { @@ -2330,7 +2630,8 @@ END } } elsif ($env{'form.roletype'} eq 'author') { ($uname,$udom,$role) = split(/:/,$user,-1); - } elsif ($env{'form.roletype'} eq 'course') { + } elsif (($env{'form.roletype'} eq 'course') || + ($env{'form.roletype'} eq 'community')) { ($uname,$udom,$role) = split(/:/,$user); } } else { @@ -2437,16 +2738,24 @@ END if ($clickers!~/\w/) { $clickers='-'; } $in{'clicker'} = $clickers; my $role = $in{'role'}; - $in{'role'}=&Apache::lonnet::plaintext($sdata->[$index{'role'}]); - if (! defined($in{'start'}) || $in{'start'} == 0) { - $in{'start'} = &mt('none'); - } else { - $in{'start'} = &Apache::lonlocal::locallocaltime($in{'start'}); + $in{'role'}=&Apache::lonnet::plaintext($sdata->[$index{'role'}],$crstype); + 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()); @@ -2457,7 +2766,7 @@ END $cellentry = ''.&mt('auto').' '; $autocount ++; } else { - $cellentry = '
'.&mt('manual').'
'; + $cellentry = '
'.&mt('manual').'
'; $manualcount ++; if ($in{'lockedtype'}) { $cellentry .= ''; @@ -2466,7 +2775,7 @@ END $cellentry .= ''; $lockcount ++; } - $cellentry .= '
'; + $cellentry .= '
'; } $r->print("$cellentry\n"); } else { @@ -2502,7 +2811,7 @@ END } } $r->print(''); + 'actionlist" value="'.$checkval.'" />'); } else { $r->print(' '); } @@ -2515,6 +2824,12 @@ END $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}})) { + $showitem = $ltstatus{$in{$item}}; + } + $r->print(''.$showitem.''."\n"); } else { $r->print(''.$in{$item}.''."\n"); } @@ -2531,7 +2846,7 @@ END } if ($env{'course.'.$env{'request.course.id'}.'.internal.showphoto'}) { if ($displayphotos eq 'on' && $role eq 'st' && $in{'photo'} ne '') { - $r->print(' '); + $r->print(' '); } else { $r->print('   '); } @@ -2542,16 +2857,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}); @@ -2561,9 +2866,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'); @@ -2718,7 +3023,7 @@ sub print_username_link { $output = $in->{'username'}; } else { $output = '{'username'}','$in->{'domain'}'".')" />'. + "'$in->{'username'}','$in->{'domain'}'".')">'. $in->{'username'}.''; } return $output; @@ -2729,6 +3034,7 @@ sub role_type_names { 'domain' => 'Domain Roles', 'author' => 'Co-Author Roles', 'course' => 'Course Roles', + 'community' => 'Community Roles', ); return %lt; } @@ -2818,8 +3124,8 @@ sub select_actions { } } if ($choices{'sections'}) { - $output .= ''."\n". - ''."\n"; + $output .= ''."\n". + ''."\n"; } } return $output; @@ -2833,9 +3139,9 @@ sub date_section_javascript { delete => "Check the boxes for any users for whom roles are to be deleted, and click 'Proceed'", none => "Choose an action to take for selected users", ); - my $output = ' - ENDTWO return $output; } sub date_section_selector { - my ($context,$permission) = @_; + my ($context,$permission,$crstype) = @_; my $callingform = $env{'form.callingform'}; my $formname = 'dateselect'; my $groupslist = &get_groupslist(); my $sec_js = &setsections_javascript($formname,$groupslist); my $output = <<"END"; 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?", - dnap => '(Does not apply to student roles).', + 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); if ($env{'form.bulkaction'} eq 'chgsec') { @@ -2961,13 +3270,18 @@ ENDJS } $date_items = &date_setting_table($starttime,undef,$context, $env{'form.bulkaction'},$formname, - $permission); + $permission,$crstype); } $output .= '

'.$headertext.'

'. - '
'."\n". + ''."\n". $date_items; if ($context eq 'course' && $env{'form.bulkaction'} eq 'chgsec') { my ($cnum,$cdom) = &get_course_identity(); + 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 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; if ($env{'form.showrole'} eq 'st') { $output .= '

'.$lt{'fors'}.'

'; @@ -2993,23 +3307,23 @@ ENDJS $info = ''; } my $rowtitle = &mt('New section to assign'); - my $secbox = §ion_picker($cdom,$cnum,$env{'form.showrole'},$rowtitle,$permission,$context); + my $secbox = §ion_picker($cdom,$cnum,$env{'form.showrole'},$rowtitle,$permission,$context,'',$crstype); $output .= $info.$secbox; } $output .= '

'. -'

'."\n". +'

'."\n". '
'; return $output; } sub section_picker { - my ($cdom,$cnum,$role,$rowtitle,$permission,$context,$mode) = @_; + 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); + &default_role_selector($context,1,$crstype); $secbox .= &Apache::lonhtmlcommon::row_title(&mt('role'),'LC_oddrow_value'). $options. &Apache::lonhtmlcommon::row_closure(1)."\n"; } @@ -3019,7 +3333,7 @@ sub section_picker { ''.&mt('Existing sections')."\n". '
'.$sections_select.''. &mt('New section').'
'."\n". - ''."\n". + ''."\n". ''."\n". ''."\n"; } else { @@ -3028,31 +3342,55 @@ 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; } sub results_header_row { - my ($rolefilter,$statusmode,$context,$permission,$mode) = @_; + my ($rolefilter,$statusmode,$context,$permission,$mode,$crstype) = @_; my ($description,$showfilter); if ($rolefilter ne 'Any') { $showfilter = $rolefilter; } if ($context eq 'course') { if ($mode eq 'csv' || $mode eq 'excel') { - $description = &mt('Course - [_1]:',$env{'course.'.$env{'request.course.id'}.'.description'}).' '; + if ($crstype eq 'Community') { + $description = &mt('Community - [_1]:',$env{'course.'.$env{'request.course.id'}.'.description'}).' '; + } else { + $description = &mt('Course - [_1]:',$env{'course.'.$env{'request.course.id'}.'.description'}).' '; + } } if ($statusmode eq 'Expired') { - $description .= &mt('Users in course with expired [_1] roles',$showfilter); + if ($crstype eq 'Community') { + $description .= &mt('Users in community with expired [_1] roles',$showfilter); + } else { + $description .= &mt('Users in course with expired [_1] roles',$showfilter); + } } elsif ($statusmode eq 'Future') { - $description .= &mt('Users in course with future [_1] roles',$showfilter); + if ($crstype eq 'Community') { + $description .= &mt('Users in community with future [_1] roles',$showfilter); + } else { + $description .= &mt('Users in course with future [_1] roles',$showfilter); + } } elsif ($statusmode eq 'Active') { - $description .= &mt('Users in course with active [_1] roles',$showfilter); + if ($crstype eq 'Community') { + $description .= &mt('Users in community with active [_1] roles',$showfilter); + } else { + $description .= &mt('Users in course with active [_1] roles',$showfilter); + } } else { if ($rolefilter eq 'Any') { - $description .= &mt('All users in course'); + if ($crstype eq 'Community') { + $description .= &mt('All users in community'); + } else { + $description .= &mt('All users in course'); + } } else { - $description .= &mt('All users in course with [_1] roles',$rolefilter); + if ($crstype eq 'Community') { + $description .= &mt('All users in community with [_1] roles',$rolefilter); + } else { + $description .= &mt('All users in course with [_1] roles',$rolefilter); + } } } my $constraint; @@ -3060,7 +3398,7 @@ sub results_header_row { if ($viewablesec ne '') { if ($env{'form.showrole'} eq 'st') { $constraint = &mt('only users in section "[_1]"',$viewablesec); - } elsif ($env{'form.showrole'} ne 'cc') { + } elsif (($env{'form.showrole'} ne 'cc') && ($env{'form.showrole'} ne 'co')) { $constraint = &mt('only users affiliated with no section or section "[_1]"',$viewablesec); } if (($env{'form.grpfilter'} ne 'all') && ($env{'form.grpfilter'} ne '')) { @@ -3145,22 +3483,31 @@ 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') { + } elsif (($env{'form.roletype'} eq 'course') || + ($env{'form.roletype'} eq 'community')) { my $coursefilter = $env{'form.coursepick'}; - if ($coursefilter eq 'category') { - my $instcode = &instcode_from_coursefilter(); - if ($instcode eq '.') { + if ($env{'form.roletype'} eq 'course') { + if ($coursefilter eq 'category') { + my $instcode = &instcode_from_coursefilter(); + if ($instcode eq '.') { + $description .= &mt('All courses in domain').' - '; + } else { + $description .= &mt('Courses in domain with institutional code: [_1]',$instcode).' - '; + } + } elsif ($coursefilter eq 'selected') { + $description .= &mt('Selected courses in domain').' - '; + } elsif ($coursefilter eq 'all') { $description .= &mt('All courses in domain').' - '; - } else { - $description .= &mt('Courses in domain with institutional code: [_1]',$instcode).' - '; } - } elsif ($coursefilter eq 'selected') { - $description .= &mt('Selected courses in domain').' - '; - } elsif ($coursefilter eq 'all') { - $description .= &mt('All courses in domain').' - '; + } elsif ($env{'form.roletype'} eq 'community') { + if ($coursefilter eq 'selected') { + $description .= &mt('Selected communities in domain').' - '; + } elsif ($coursefilter eq 'all') { + $description .= &mt('All communities in domain').' - '; + } } if ($statusmode eq 'Expired') { $description .= &mt('users with expired [_1] roles',$showfilter); @@ -3197,7 +3544,7 @@ sub viewable_section { ################################################# ################################################# sub show_drop_list { - my ($r,$classlist,$nosort,$permission) = @_; + my ($r,$classlist,$nosort,$permission,$crstype) = @_; my $cid = $env{'request.course.id'}; my ($cnum,$cdom) = &get_course_identity($cid); if (! exists($env{'form.sortby'})) { @@ -3215,10 +3562,12 @@ sub show_drop_list {

- + END my ($indexhash,$keylist) = &make_keylist_array(); my $studentcount = 0; @@ -3241,7 +3590,11 @@ END } } if (!$studentcount) { - $r->print(&mt('There are no students to drop.')); + if ($crstype eq 'Community') { + $r->print(&mt('There are no members to drop.')); + } else { + $r->print(&mt('There are no students to drop.')); + } return; } my ($classgroups) = &Apache::loncoursedata::get_group_memberships( @@ -3249,11 +3602,16 @@ END my %lt=&Apache::lonlocal::texthash('usrn' => "username", 'dom' => "domain", 'sn' => "student name", + 'mn' => "member name", 'sec' => "section", 'start' => "start date", 'end' => "end date", 'groups' => "active groups", ); + my $nametitle = $lt{'sn'}; + if ($crstype eq 'Community') { + $nametitle = $lt{'mn'}; + } if ($nosort) { $r->print(&Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row()); @@ -3262,7 +3620,7 @@ END $lt{'usrn'} $lt{'dom'} ID - $lt{'sn'} + $nametitle $lt{'sec'} $lt{'start'} $lt{'end'} @@ -3281,7 +3639,7 @@ END ID - $lt{'sn'} + $nametitle $lt{'sec'} @@ -3336,7 +3694,7 @@ END # $r->print(&Apache::loncommon::start_data_table_row()); $r->print(<<"END"); - + $username $domain $id @@ -3351,14 +3709,23 @@ END $r->print(&Apache::loncommon::end_data_table().'
'); %lt=&Apache::lonlocal::texthash( 'dp' => "Drop Students", + 'dm' => "Drop Members", 'ca' => "check all", 'ua' => "uncheck all", ); + my $btn = $lt{'dp'}; + if ($crstype eq 'Community') { + $btn = $lt{'dm'}; + } $r->print(<<"END"); -

-   - -

+

+

+   + +

+

+ +

END return; } @@ -3369,24 +3736,43 @@ END sub print_first_users_upload_form { my ($r,$context) = @_; my $str; - $str = ''; + $str = ''; $str .= ''; - $str .= ''; - $str .= "

".&mt('Upload a file containing information about users')."

\n"; - $str .= &Apache::loncommon::upfile_select_html(); - $str .= '

'; - $str .= &Apache::loncommon::help_open_topic("Course_Create_Class_List", - &mt("How do I create a users list from a spreadsheet")). - "
\n"; - $str .= &Apache::loncommon::help_open_topic("Course_Convert_To_CSV", - &mt("How do I create a CSV file from a spreadsheet")); - $str .= "

\n"; - $str .= "

\n"; - $str .= '

\n"; - $str .= ''."
\n"; - $str .= &Apache::loncommon::end_page(); + $str .= ''; + + $str .= '

'.&mt('Upload a file containing information about users').'

'."\n"; + + # Excel and CSV Help + $str .= '
' + .&Apache::loncommon::help_open_topic("Course_Create_Class_List", + &mt("How do I create a users list from a spreadsheet")) + .'
'."\n" + .&Apache::loncommon::help_open_topic("Course_Convert_To_CSV", + &mt("How do I create a CSV file from a spreadsheet")) + .'

'."\n"; + $str .= &Apache::lonhtmlcommon::start_pick_box() + .&Apache::lonhtmlcommon::row_title(&mt('File')); + if (&Apache::lonlocal::current_language() ne 'en') { + if ($context eq 'course') { + $str .= '

'."\n" + .&mt('Please upload an UTF8 encoded file to ensure a correct character encoding in your classlist.')."\n" + .'

'."\n"; + } + } + $str .= &Apache::loncommon::upfile_select_html() + .&Apache::lonhtmlcommon::row_closure() + .&Apache::lonhtmlcommon::row_title( + '') + .'' + .&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box(); + + $str .= '

' + .'' + .'

'; + $r->print($str); return; } @@ -3430,11 +3816,25 @@ sub upfile_drop_add { 'ipwd_choice' => 'scalar', 'email_choice' => 'scalar', 'role_choice' => 'scalar', - 'domain_choice' => 'scalar'}); + 'domain_choice' => 'scalar', + 'inststatus_choice' => 'scalar'}); # + my ($cid,$crstype,$setting); + if ($context eq 'domain') { + $setting = $env{'form.roleaction'}; + } + if ($env{'request.course.id'} ne '') { + $cid = $env{'request.course.id'}; + $crstype = &Apache::loncommon::course_type(); + } 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); + } + } my ($startdate,$enddate) = &get_dates_from_form(); if ($env{'form.makedatesdefault'}) { - $r->print(&make_dates_default($startdate,$enddate,$context)); + $r->print(&make_dates_default($startdate,$enddate,$context,$crstype)); } # Determine domain and desired host (home server) my $defdom=$env{'request.role.domain'}; @@ -3486,9 +3886,8 @@ sub upfile_drop_add { $amode = ''; # This causes the loop below to be skipped } } - my ($cid,$defaultsec,$defaultrole,$setting); + my ($defaultsec,$defaultrole); if ($context eq 'domain') { - $setting = $env{'form.roleaction'}; if ($setting eq 'domain') { $defaultrole = $env{'form.defaultrole'}; } elsif ($setting eq 'course') { @@ -3501,28 +3900,22 @@ sub upfile_drop_add { $defaultrole = $env{'form.defaultrole'}; $defaultsec = $env{'form.sections'}; } - if ($env{'request.course.id'} ne '') { - $cid = $env{'request.course.id'}; - } elsif ($setting eq 'course') { - if (&Apache::lonnet::is_course($env{'form.dcdomain'},$env{'form.dccourse'})) { - $cid = $env{'form.dcdomain'}.'_'.$env{'form.dccourse'}; - } - } # Check to see if user information can be changed my @userinfo = ('firstname','middlename','lastname','generation', 'permanentemail','id'); my %canmodify; if (&Apache::lonnet::allowed('mau',$domain)) { + push(@userinfo,'inststatus'); foreach my $field (@userinfo) { $canmodify{$field} = 1; } } my (%userlist,%modifiable_fields,@poss_roles); my $secidx = &Apache::loncoursedata::CL_SECTION(); - my @courseroles = &roles_by_context('course',1); + my @courseroles = &roles_by_context('course',1,$crstype); if (!&Apache::lonnet::allowed('mau',$domain)) { if ($context eq 'course' || $context eq 'author') { - @poss_roles = &curr_role_permissions($context); + @poss_roles = &curr_role_permissions($context,'','',$crstype); my @statuses = ('active','future'); my ($indexhash,$keylist) = &make_keylist_array(); my %info; @@ -3561,6 +3954,8 @@ sub upfile_drop_add { } else { $r->print('

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

\n

\n"); } + $r->rflush; + my %counts = ( user => 0, auth => 0, @@ -3605,14 +4000,22 @@ sub upfile_drop_add { $r->print($groupwarn.'
'); } } - my (%curr_rules,%got_rules,%alerts); - my %customroles = &my_custom_roles(); - my @permitted_roles = &roles_on_upload($context,$setting,%customroles); + my (%curr_rules,%got_rules,%alerts,%cancreate); + my %customroles = &my_custom_roles($crstype); + my @permitted_roles = + &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) = ('','','',''); @@ -3633,13 +4036,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)); + &mt('[_1]: Unacceptable username for user [_2] [_3] [_4] [_5]', + ''.$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'}}. @@ -3656,10 +4066,10 @@ sub upfile_drop_add { $entries{$fields{'sec'}} =~ s/\W//g; my $item = $entries{$fields{'sec'}}; if ($item eq "none" || $item eq 'all') { - $r->print('
'.&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]" - this is a reserved word.',$username,$fname,$mname,$lname,$gen,$item)); + $r->print('
'.&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]" - this is a reserved word.',''.$username.'',$fname,$mname,$lname,$gen,$item)); next; } elsif (exists($curr_groups{$item})) { - $r->print('
'.&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]" - this is a course group.',$username,$fname,$mname,$lname,$gen,$item).' '.&mt('Section names and group names must be distinct.')); + $r->print('
'.&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]" - this is a course group.',''.$username.'',$fname,$mname,$lname,$gen,$item).' '.&mt('Section names and group names must be distinct.')); next; } else { push(@secs,$item); @@ -3671,7 +4081,7 @@ sub upfile_drop_add { if (ref($userlist{$username.':'.$userdomain}) eq 'ARRAY') { my $currsec = $userlist{$username.':'.$userdomain}[$secidx]; if ($currsec ne $env{'request.course.sec'}) { - $r->print('
'.&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]".',$username,$fname,$mname,$lname,$gen,$secs[0]).'
'); + $r->print('
'.&mt('[_1]: Unable to enroll user [_2] [_3] [_4] [_5] in a section named "[_6]".',''.$username.'',$fname,$mname,$lname,$gen,$secs[0]).'
'); if ($currsec eq '') { $r->print(&mt('This user already has an active/future student role in the course, unaffiliated to any section.')); @@ -3698,9 +4108,18 @@ 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=''; } } + unless ($email=~/^[^\@]+\@[^\@]+$/) { $email=''; } + } + } + # determine affiliation + my $inststatus=''; + if (defined($fields{'inststatus'})) { + if (defined($entries{$fields{'inststatus'}})) { + $inststatus=$entries{$fields{'inststatus'}}; + } } # determine user password my $password = $genpwd; @@ -3721,8 +4140,13 @@ sub upfile_drop_add { } if ($role eq '') { my $rolestr = join(', ',@permitted_roles); - $r->print('
'. - &mt('[_1]: You do not have permission to add the requested role [_2] for the user.',$entries{$fields{'username'}},$entries{$fields{'role'}}).'
'.&mt('Allowable role(s) is/are: [_1].',$rolestr)."\n"); + $r->print('
' + .&mt('[_1]: You do not have permission to add the requested role [_2] for the user.' + ,''.$entries{$fields{'username'}}.'' + ,$entries{$fields{'role'}}) + .'
' + .&mt('Allowable role(s) is/are: [_1].',$rolestr)."\n" + ); next; } } @@ -3731,7 +4155,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 @@ -3740,20 +4164,79 @@ 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; + if ($username =~/^[^\@]+\@[^\@]+$/) { + if ($email eq '') { + $email = $username; + } + my $lc_email; + if ($username eq $email) { + $lc_email = lc($email); + } + my $lc_username = lc($username); + if ($lc_username ne $username) { + if ($username eq $email) { + $email = $lc_username; + } + $username = $lc_username; + $uhome=&Apache::lonnet::homeserver($username,$userdomain); + if ($uhome ne 'no_host') { + $newuser = 0; + } + } + } + } + my $user = $username.':'.$newuserdom; + if ($newuser) { my $checkhash; my $checks = { 'username' => 1 }; - $checkhash->{$username.':'.$domain} = { 'newuser' => 1, }; + $checkhash->{$user} = { '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}; + if ($usertype eq 'unofficial') { + $r->print('
'. + &mt("[_1]: The user does not exist, and the new user's username must be an e-mail address.",''.$username.'')); + } else { + $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 ) { @@ -3778,7 +4261,7 @@ sub upfile_drop_add { } } my @newinfo = (\$fname,\$mname,\$lname,\$gen,\$email,\$id); - for (my $i=0; $i<@userinfo; $i++) { + for (my $i=0; $i<@newinfo; $i++) { if (${$newinfo[$i]} ne '') { if (!$canmodify{$userinfo[$i]}) { ${$newinfo[$i]} = ''; @@ -3804,7 +4287,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; + } } } } @@ -3827,7 +4315,7 @@ sub upfile_drop_add { $fname,$mname,$lname,$gen,$sec,$enddate, $startdate,$env{'form.forceid'}, $desiredhost,$email,'manual','',$cid, - '',$context); + '',$context,$inststatus); $userresult = $roleresult; } else { if ($role ne '') { @@ -3836,7 +4324,7 @@ sub upfile_drop_add { $role = 'cr_'.$env{'user.domain'}.'_'. $env{'user.name'}.'_'.$role; } - if ($role ne 'cc') { + if (($role ne 'cc') && ($role ne 'co')) { if (@secs > 1) { $multiple = 1; foreach my $sec (@secs) { @@ -3847,7 +4335,7 @@ sub upfile_drop_add { $mname,$lname,$gen,$sec, $env{'form.forceid'},$desiredhost, $email,$role,$enddate, - $startdate,$checkid); + $startdate,$checkid,$inststatus); } } elsif (@secs > 0) { $singlesec = $secs[0]; @@ -3862,7 +4350,8 @@ sub upfile_drop_add { $id,$amode,$password,$fname, $mname,$lname,$gen,$singlesec, $env{'form.forceid'},$desiredhost, - $email,$role,$enddate,$startdate,$checkid); + $email,$role,$enddate,$startdate, + $checkid,$inststatus); } } if ($multiple) { @@ -3882,15 +4371,15 @@ sub upfile_drop_add { } else { if ($context eq 'course') { $r->print('
'. - &mt('[_1]: Unable to enroll. No password specified.',$username) + &mt('[_1]: Unable to enroll. No password specified.',''.$username.'') ); } elsif ($context eq 'author') { $r->print('
'. - &mt('[_1]: Unable to add co-author. No password specified.',$username) + &mt('[_1]: Unable to add co-author. No password specified.',''.$username.'') ); } else { $r->print('
'. - &mt('[_1]: Unable to add user. No password specified.',$username) + &mt('[_1]: Unable to add user. No password specified.',''.$username.'') ); } } @@ -3898,7 +4387,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) { @@ -3993,7 +4482,7 @@ sub user_change_result { if ($userresult =~ /^error:(.+)$/) { my $error = $1; $r->print('
'. - &mt('[_1]: Unable to add/modify: [_2]',$username.':'.$userdomain,$error)); + &mt('[_1]: Unable to add/modify: [_2]',''.$username.':'.$userdomain.'',$error)); } } else { $counts->{'user'} ++; @@ -4003,7 +4492,7 @@ sub user_change_result { if ($authresult =~ /^error:(.+)$/) { my $error = $1; $r->print('
'. - &mt('[_1]: Unable to modify authentication: [_2]',$username.':'.$userdomain,$error)); + &mt('[_1]: Unable to modify authentication: [_2]',''.$username.':'.$userdomain.'',$error)); } } else { $counts->{'auth'} ++; @@ -4013,7 +4502,7 @@ sub user_change_result { if ($roleresult =~ /^error:(.+)$/) { my $error = $1; $r->print('
'. - &mt('[_1]: Unable to add role: [_2]',$username.':'.$userdomain,$error)); + &mt('[_1]: Unable to add role: [_2]',''.$username.':'.$userdomain.'',$error)); } } else { $counts->{'role'} ++; @@ -4036,14 +4525,24 @@ sub user_change_result { # ========================================================= Menu Phase Two Drop sub print_drop_menu { - my ($r,$context,$permission) = @_; - $r->print('

'.&mt("Drop Students").'

'."\n". + my ($r,$context,$permission,$crstype) = @_; + my $heading; + if ($crstype eq 'Community') { + $heading = &mt("Drop Members"); + } else { + $heading = &mt("Drop Students"); + } + $r->print('

'.$heading.'

'."\n". '
'."\n"); my $classlist = &Apache::loncoursedata::get_classlist(); if (! defined($classlist)) { - $r->print(&mt('There are no students currently enrolled.')."\n"); + if ($crstype eq 'Community') { + $r->print(&mt('There are no members currently enrolled.')."\n"); + } else { + $r->print(&mt('There are no students currently enrolled.')."\n"); + } } else { - &show_drop_list($r,$classlist,'nosort',$permission); + &show_drop_list($r,$classlist,'nosort',$permission,$crstype); } $r->print('
'. &Apache::loncommon::end_page()); return; @@ -4052,9 +4551,12 @@ 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; + if ($context eq 'course') { + $crstype = &Apache::loncommon::course_type(); + } my @changelist; if ($choice eq 'drop') { @changelist = &Apache::loncommon::get_env_multiple('form.droplist'); @@ -4066,7 +4568,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', @@ -4083,8 +4585,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'; @@ -4125,7 +4628,7 @@ sub update_user_list { } } } - my $plrole = &Apache::lonnet::plaintext($role); + my $plrole = &Apache::lonnet::plaintext($role,$crstype); my $start = $env{'form.'.$item.'_start'}; my $end = $env{'form.'.$item.'_end'}; if ($choice eq 'drop') { @@ -4182,15 +4685,24 @@ sub update_user_list { } } elsif ($choice eq 'chgsec') { my (@newsecs,$revresult,$nochg,@retained); - if ($role ne 'cc') { - @newsecs = split(/,/,$env{'form.newsecs'}); + if (($role ne 'cc') && ($role ne 'co')) { + 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, @@ -4217,13 +4729,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); @@ -4232,22 +4754,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; @@ -4258,13 +4816,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"); @@ -4280,17 +4873,17 @@ 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.

')); + $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') { - $r->print(&make_dates_default($startdate,$enddate,$context)); + $r->print(&make_dates_default($startdate,$enddate,$context,$crstype)); } } my $linktext = &mt('Display User Lists'); @@ -4300,6 +4893,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)}); @@ -4377,7 +4991,7 @@ sub course_sections { my $output = ''; my @sections = (sort {$a <=> $b} keys %{$sections_count}); my $numsec = scalar(@sections); - my $is_selected = ' selected="selected" '; + my $is_selected = ' selected="selected"'; if ($numsec <= 1) { $output = '