# The LearningOnline Network # Allow users to self-enroll in a course # # $Id: selfenroll.pm,v 1.13 2008/07/17 20:08:17 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # package Apache::selfenroll; use strict; use Apache::Constants qw(:common); use Apache::lonnet; use Apache::loncommon; use Apache::lonlocal; use Apache::createaccount; use LONCAPA qw(:DEFAULT :match); sub handler { my $r = shift; &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; if ($r->header_only) { return OK; } my $handle = &Apache::lonnet::check_for_valid_session($r); my $lonidsdir=$r->dir_config('lonIDsDir'); my $lonhost = $r->dir_config('lonHostID'); if ($handle ne '') { &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle); } &Apache::lonacc::get_posted_cgi($r); &Apache::lonlocal::get_language_handle($r); &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['courseid']); my $js = &Apache::createaccount::catreturn_js(); my $desc; my ($coursechk,$courseid) = &validate_course_id($env{'form.courseid'}); if ($coursechk ne 'ok') { &page_header($r,$courseid,$js); $r->print('

'.&mt('Self-enrollment error').'

'. ''. &mt('Invalid domain or course number').''); &page_footer($r); return OK; } my $now = time; if ($env{'form.phase'} eq 'login') { $js .= "\n".&Apache::createaccount::javascript_setforms($now); } my %coursehash = &Apache::lonnet::coursedescription($courseid); my $cdom = $coursehash{'domain'}; my $cnum = $coursehash{'num'}; my $desc = $coursehash{'description'}; &page_header($r,$courseid,$js,$desc); my $include = $r->dir_config('lonIncludes'); if ($env{'form.phase'} eq 'login') { my $jsh=Apache::File->new($include."/londes.js"); $r->print(<$jsh>); } my ($canenroll,$selfenroll_types,$selfenroll_registered,@cancreate, $knownuser,$selfenroll_access_start,$selfenroll_access_end, $selfenroll_section,$selfenroll_future,%curr_role,$cdomdesc); $selfenroll_types = $coursehash{'internal.selfenroll_types'}; $selfenroll_registered = $coursehash{'internal.selfenroll_registered'}; $selfenroll_section = $coursehash{'internal.selfenroll_section'}; $selfenroll_access_start = $coursehash{'internal.selfenroll_start_access'}; $selfenroll_access_end = $coursehash{'internal.selfenroll_end_access'}; if ($selfenroll_types ne '') { my $start = $coursehash{'internal.selfenroll_start_date'}; my $end = $coursehash{'internal.selfenroll_end_date'}; if (($start > 0 && $start < $now) && (($end == 0) || ($end > 0 && $end > $now))) { $canenroll = 1; } elsif (($end == 0) || ($end > 0 && $end > $now)) { if ($start > $now) { $selfenroll_future = &Apache::lonlocal::locallocaltime($start); } } } $knownuser = &user_is_known(); if (!$canenroll) { $r->print('

'.&mt('Self-enrollment unavailable').'

'. &mt('Self-enrollment is not currently available for this course.'). '

'); if ($selfenroll_types ne '') { if ($selfenroll_future ne '') { if ($selfenroll_types eq '*') { $r->print(&mt('Self-enrollment will become available starting [_1], and will be available to all LON-CAPA users.',$selfenroll_future).'
'); } else { my ($enrolltypes,$longtypes,$alldoms); if ($knownuser) { &get_selfenroll_filters($selfenroll_types,$env{'user.domain'}); my $domdesc = &Apache::lonnet::domain($env{'user.domain'},'description'); if (ref($enrolltypes) eq 'HASH') { if (ref($enrolltypes->{$env{'user.domain'}}) eq 'ARRAY') { if (grep(/^any$/,@{$enrolltypes->{$env{'user.domain'}}})) { $r->print(&mt('Self-enrollment will become available starting [_1], and will be available to all LON-CAPA users at your institution ([_2]).', $selfenroll_future,$domdesc).'
'); } else { if (&user_can_selfenroll($env{'user.domain'}, $env{'user.name'}, $enrolltypes->{$env{'user.domain'}})) { $r->print(&mt('Self-enrollment will become available starting [_1]; please enroll at that time.',$selfenroll_future)); } else { $r->print(&mt('Although self-enrollment will become available starting [_1], you are ineligible for enrollment.',$selfenroll_future).'
'); $r->print(&print_selfenroll_types($longtypes,$env{'user.domain'})); } } } } } else { $r->print(&mt('Self-enrollment will become available starting [_1].', $selfenroll_future).'
'); $r->print(&print_selfenroll_types($longtypes)); } } } } } if ($knownuser) { foreach my $key (keys(%env)) { if ($key =~ m-^user\.role\.st\./$cdom/$cnum/?(\w*)$-) { my $sec = $1; my ($start,$end) = split(/\./,$env{$key}); my $status = 'active'; if (($end) && ($end<=$now)) { $status = 'previous'; } if (($start) && ($now<$start)) { $status = 'future'; } if ($status eq 'active' || $status eq 'future') { $curr_role{'status'} = $status; $curr_role{'section'} = $sec; if ($curr_role{'section'} eq '') { $curr_role{'section'} = &mt('none'); } $curr_role{'start'} = &Apache::lonlocal::locallocaltime($start); $curr_role{'role'} = 'st./'.$cdom.'/'.$cnum; if ($sec ne '') { $curr_role{'role'} .= '/'.$sec; } } } } } if (!$canenroll) { if (keys(%curr_role)) { $r->print(&has_role(%curr_role)); } &page_footer($r); return OK; } @cancreate = &can_create($cdom); my ($form,$login_path,$firsturl,$create_path,$sso_url,$missing_formitem); $form = 'logmein'; $login_path = '/adm/login'; $firsturl= '/adm/selfenroll?courseid='.$courseid; $create_path = '/adm/createaccount'; $sso_url = $r->dir_config('lonSSOReloginServer'); if ($sso_url eq '') { $sso_url = $login_path; } $missing_formitem = &mt('The link to the requested page could not be followed.')."\\n".&mt('The placeholder for the courseID is absent.'); if ($knownuser) { if (keys(%curr_role)) { $r->print('

'.&mt('Self-enrollment unavailable').'

'. ''.&has_role(%curr_role).''); &page_footer($r); return OK; } &process_self_enroll($r,$cdom,$cnum,$selfenroll_types,$selfenroll_registered, $selfenroll_access_start,$selfenroll_access_end, $selfenroll_section,$now); } elsif ($env{'form.phase'} eq 'login') { my $submit_text = &mt('Log in'); $r->print('

'.&mt('Log-in to LON-CAPA').'

'); my $udom = &Apache::lonnet::default_login_domain(); $r->print(&Apache::createaccount::login_box($now,$lonhost,$courseid, $submit_text,$udom,'selfenroll')); $r->print(&mt('You will be able to self-enroll in the course you selected ([_1]) after you have successfully logged in.',''.$desc.'')); &page_footer($r); return OK; } elsif (@cancreate > 0) { $r->print(< function setPath(formname,formaction,item,arg) { var formidx = getFormByName(formname); if (formidx > -1) { if (formaction != '') { document.forms[formidx].action = formaction; } var itemid = getIndexByName(formidx,'setting'); if (itemid > -1) { document.forms[formidx].elements[itemid].name = item; document.forms[formidx].elements[itemid].value = arg; document.forms[formidx].submit(); } else { alert("$missing_formitem"); } } return; } END $r->print('

'.&mt('LON-CAPA account required').'

'. &mt('You need to be logged into LON-CAPA to self-enroll in a course.').''); } else { $r->print(''); my ($enrolltypes,$longtypes,$alldoms) = &get_selfenroll_filters($selfenroll_types); $r->print(&print_selfenroll_types($longtypes)); } } else { my $cdomdesc = &Apache::lonnet::domain($cdom,'description'); $r->print('

'.&mt('LON-CAPA account required').'

'); $r->print(&mt('You must [_1]log-in[_2] to LON-CAPA with an existing account to be able to enroll in this course, as account creation at this institution ([_3]) is not permitted when self-enrolling.','','',$cdomdesc)); if ($selfenroll_types ne '*') { my ($enrolltypes,$longtypes,$alldoms) = &get_selfenroll_filters($selfenroll_types); $r->print('
'.&print_selfenroll_types($longtypes)); } my $displayurl = &escape($firsturl); $r->print(&mt('Submit a request to the LON-CAPA [_1]helpdesk[_2] for [_3] if you require assistance.','','',$cdomdesc)); } $r->print("\n".'
'."\n". ''."\n". ''."\n". &Apache::lonhtmlcommon::echo_form_input(['backto','courseid','context','phase'])."\n". '
'."\n"); &page_footer($r); return OK; } sub page_header { my ($r,$courseid,$js,$desc) = @_; my $start_page = &Apache::loncommon::start_page('Self-enroll in a LON-CAPA course',$js, {'no_inline_link' => 1,}); $r->print($start_page); &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::createaccount::selfenroll_crumbs($r,$courseid,$desc); $r->print(&Apache::lonhtmlcommon::breadcrumbs('Self-enroll in course')); return; } sub page_footer { my ($r) = @_; $r->print('
'."\n". &Apache::lonhtmlcommon::echo_form_input(['backto','courseid','phase','context']). '
'.&Apache::loncommon::end_page()); return; } sub validate_course_id { my ($courseid) = @_; my ($cdom,$cnum) = ($env{'form.courseid'} =~ /^($match_domain)_($match_courseid)$/); if ($cdom ne '' && $cnum ne '') { if (&Apache::lonnet::is_course($cdom,$cnum)) { return ('ok',$courseid); } } return; } sub user_is_known { my $known = 0; if ($env{'user.name'} ne '' && $env{'user.name'} ne 'public' && $env{'user.domain'} ne '' && $env{'user.domain'} ne 'public') { $known = 1; } return $known; } sub can_create { my ($cdom) = @_; my @cancreate; my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$cdom); if (ref($domconfig{'usercreation'}) eq 'HASH') { if (ref($domconfig{'usercreation'}{'cancreate'}) eq 'HASH') { if ($domconfig{'usercreation'}{'cancreate'}{'selfcreate'} ne 'none') { if (ref($domconfig{'usercreation'}{'cancreate'}{'selfcreate'}) eq 'ARRAY') { @cancreate = @{$domconfig{'usercreation'}{'cancreate'}{'selfcreate'}}; } else { if (($domconfig{'usercreation'}{'cancreate'}{'selfcreate'} ne 'none') && ($domconfig{'usercreation'}{'cancreate'}{'selfcreate'} ne '')) { @cancreate = ($domconfig{'usercreation'}{'cancreate'}{'selfcreate'}); } } } } } return @cancreate; } sub has_role { my (%curr_role) = @_; my $output; if ($curr_role{'status'} eq 'active') { my $rolelink = &jump_to_role($curr_role{'role'}); $output = &mt('You already have an active student role (section: "[_1]") in this course.',$curr_role{'section'}).'
'.$rolelink; } elsif ($curr_role{'status'} eq 'future') { $output = &mt('You have a student role (section: "[_1]") in this course which will become active [_2].',$curr_role{'section'},$curr_role{'start'}); } return $output; } sub process_self_enroll { my ($r,$cdom,$cnum,$selfenroll_types,$selfenroll_registered, $selfenroll_access_start,$selfenroll_access_end,$selfenroll_section,$now) = @_; my $udom = $env{'user.domain'}; my $uname = $env{'user.name'}; my $selfenroll = 0; my ($enrolltypes,$longtypes,$alldoms); if ($selfenroll_types eq '*') { $selfenroll = 1; } else { ($enrolltypes,$longtypes,$alldoms) = &get_selfenroll_filters($selfenroll_types,$udom); if ($alldoms) { $selfenroll = 1; } elsif (ref($enrolltypes) eq 'HASH') { if (ref($enrolltypes->{$udom}) eq 'ARRAY') { if (grep(/^any$/,@{$enrolltypes->{$udom}})) { $selfenroll = 1; } else { $selfenroll = &user_can_selfenroll($udom,$uname,$enrolltypes->{$udom}); } } } } if ($selfenroll) { my $usec = $selfenroll_section; if ($selfenroll_section eq 'none') { $usec = ''; } if ($selfenroll_registered) { my ($registered,$instsec,$message) = &check_registered($cdom,$cnum); $usec = $instsec; if (!$registered) { $r->print('

'.&mt('Self-enrollment unavailable').'

'.&mt('Self-enrollment is restricted to students officially registered for this course.').'
'); if ($message) { $r->print($message); } else { $r->print(&mt('As you are not currently registered for this course, self-enrollment is unavailable.')); } return; } } my $enrollresult = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef, undef,undef,$usec,$selfenroll_access_end,$selfenroll_access_start, 'manual',undef,$cdom.'_'.$cnum,$selfenroll); if ($enrollresult eq 'ok') { my (%userroles,%newrole,%newgroups); my $role = 'st'; my $area = '/'.$cdom.'/'.$cnum; my $spec = $role.'.'.$area; if ($usec ne '') { $spec .= '/'.$usec; $area .= '/'.$usec; } &Apache::lonnet::standard_roleprivs(\%newrole,$role,$cdom,$spec,$cnum, $area); &Apache::lonnet::set_userprivs(\%userroles,\%newrole,%newgroups); $userroles{'user.role.'.$spec} = $selfenroll_access_start.'.'.$selfenroll_access_end; &Apache::lonnet::appenv(\%userroles,[$role,'cm']); $r->print('

'.&mt('Enrollment process complete').'

'); if ($selfenroll_access_end && $selfenroll_access_end <= $now) { $r->print(&mt('The end date for access to this course for users who self-enroll has passed.').'
'.&mt('Consequently, although a new role was created for you in the course, it is an inactive role which does not provide access to the course.')); } else { $r->print(&mt('Self-enrollment in this course was successful.').'
'); my $showstart = &Apache::lonlocal::locallocaltime($selfenroll_access_start); my $showend = &Apache::lonlocal::locallocaltime($selfenroll_access_end); if ($selfenroll_access_start && $selfenroll_access_start >$now) { $r->print(&mt('The start date for access to this course for users who self-enroll has yet to be reached.').'
'.&mt('Consequently, although a new role was created for you in the course, you will not be able to select this role until [_1].',$showstart)); } else { my $newrole = 'st./'.$cdom.'/'.$cnum; if ($usec ne '') { $newrole .= '/'.$usec; } my $rolelink = &jump_to_role($newrole); $r->print(&mt('Your new role is available immediately, and will provide access to the course until [_1].',$showend).'

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

'.&mt('Enrollment incomplete').'

'. &mt('Self-enrollment in this course failed.')); if ($enrollresult ne '') { $r->print(''.$enrollresult.''); } } } else { $r->print('

'.&mt('Self-enrollment unavailable').'

'. &mt('You are not permitted to enroll yourself in this course.').'
'); $r->print(&print_selfenroll_types($longtypes)); } return; } sub user_can_selfenroll { my ($udom,$uname,$domenrolltypes) = @_; my $selfenroll = 0; my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'inststatus'); my @inststatuses; if ($userhash{'inststatus'} eq '') { push(@inststatuses,'other'); } else { @inststatuses = split(':',$userhash{'inststatus'}); } foreach my $type (@inststatuses) { if (ref($domenrolltypes) eq 'ARRAY') { if (grep(/^\Q$type\E$/,@{$domenrolltypes})) { $selfenroll = 1; last; } } } return $selfenroll; } sub jump_to_role { my ($role) = @_; my $output = <<"END"; END $output .= ''."\n". &mt('Enter course now').''."\n". '
'."\n". ''."\n". ''."\n". '
'; return $output; } sub get_selfenroll_filters { my ($selfenroll_types,$domain) = @_; my (%enrolltypes,%longtypes,$alldoms); my @selfenrolldoms = split(/;/,$selfenroll_types); foreach my $item (@selfenrolldoms) { my ($selfdom,$type_str) = split(/:/,$item); if ($selfdom eq '*') { $alldoms = 1; last; } if ($domain ne '') { next if ($selfdom ne $domain); } if ($selfdom =~ /^$match_domain$/) { if (&Apache::lonnet::domain($selfdom) ne '') { my @types = split(/,/,$type_str); my @unesc_types = map { &unescape($_); } @types; my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($selfdom); if (ref($usertypes) eq 'HASH') { my $anytype = 1; foreach my $key (keys(%{$usertypes})) { if (!grep(/^\Q$key\E$/,@unesc_types)) { $anytype = 0; last; } } if ($anytype) { if (!(grep(/^other$/,@types))) { $anytype = 0; } } $usertypes->{'any'} = &mt('Any users'); $usertypes->{'other'} = &mt('Others'); my @showtypes; if ($anytype) { @{$enrolltypes{$selfdom}} = ('any'); @showtypes = ('any'); } else { @{$enrolltypes{$selfdom}} = @types; @showtypes = @unesc_types; } @{$longtypes{$selfdom}} = map {$usertypes->{$_}} @showtypes; } } } } return (\%enrolltypes,\%longtypes,$alldoms); } sub print_selfenroll_types { my ($longtypes,$domain) = @_; my $output; if (ref($longtypes) eq 'HASH') { if ($domain ne '') { my $domdesc = &Apache::lonnet::domain($domain,'description'); if (ref($longtypes->{$domain}) eq 'ARRAY') { if (grep(/^any$/,@{$longtypes->{$domain}})) { $output = &mt('Self-enrollment in this course is available to any user affiliated with [_1].',$domdesc); } else { my $status_str = join(', ',@{$longtypes->{$domain}}); $output = &mt('Self-enrollment in this course is only available to users affiliated with [_1] who have the following status: "[_2]".',$domdesc,$status_str); } } else { $output = &mt('Self-enrollment is not currently available for this course for users affiliated with [_1].',$domdesc); } } elsif (keys(%{$longtypes}) > 0) { $output = &mt('Self-enrollment in this course is only available to users affiliated with the following institutions, and who have the required status:').' '."\n"; } else { $output = &mt('Self-enrollment is not currently available for this course.'); } } return $output; } sub check_registered { my ($cdom,$cnum) = @_; my ($registered,$instsec,$message); my %settings = &Apache::lonnet::get('environment',['internal.coursecode', 'internal.sectionnums', 'internal.crosslistings'],$cdom,$cnum); my (@allcourses,%LC_code,%affiliates,%reply); &Apache::loncommon::get_institutional_codes(\%settings,\@allcourses,\%LC_code); if (@allcourses > 0) { @{$affiliates{$cnum}} = @allcourses; my $outcome = &Apache::lonnet::fetch_enrollment_query('updatenow',\%affiliates,\%reply,$cdom,$cnum); if ($outcome eq 'ok') { if ($reply{$cnum} > 0) { foreach my $class (@allcourses) { my %enrolled; my $dir = $Apache::lonnet::perlvar{'lonDaemons'}; my %place = &LONCAPA::Enrollment::place_hash(); &LONCAPA::Enrollment::parse_classlist($dir,$cdom,$cnum,$class, \%place,$LC_code{$class},\%enrolled); if (defined($enrolled{$env{'user.name'}})) { $registered = 1; $instsec = $LC_code{$class}; last; } } } else { $message = &mt('Your registration status could not be verified.'); } } else { $message = &mt('Your registration status could not determined, because a problem occurred retrieving data.'); } } else { $message = &mt('As no institutional course sections are currently associated with this course, your registration status is undetermined.'); } return ($registered,$instsec,$message); } 1;