File:  [LON-CAPA] / loncom / enrollment / localenroll.pm
Revision 1.64: download - view: text, annotated - select for diffs
Sun Feb 27 01:43:14 2022 UTC (2 years, 9 months ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, version_2_11_X, version_2_11_5_msu, version_2_11_5, version_2_11_4_uiuc, version_2_11_4_msu, version_2_11_4, HEAD
- Domain configuration to allow authentication of an alternate username, if
  username entered differs from real username in a predictable way,
  e.g., username entered in log-in page was email address (userid@example.tld)
  instead of just userid.

# functions to glue school database system into Lon-CAPA for 
# automated enrollment
# $Id: localenroll.pm,v 1.64 2022/02/27 01:43:14 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/
#

=pod

=head1 NAME

localenroll

=head1 SYNOPSIS

This is part of the LearningOnline Network with CAPA project
described at http://www.lon-capa.org.


=head1 NOTABLE SUBROUTINES

=cut

package localenroll;

use strict;

=pod

=over
 
=item run()
 set this to return 1 if you want the auto enrollment to run

 Beginning with LON-CAPA version 2.4, use of this routine is
 deprecated.  Whether or not Autoenroll.pl should run is set
 by the Domain Coordinator via "Set domain configuration",
 provided in the Domain Management section of the Main menu. 

=cut

sub run() {
    my $dom = shift;
    return 0;
}


=pod

=item fetch_enrollment()

 connects to the institutional classlist data source,
 reads classlist data and stores in an XML file
 in /home/httpd/perl/tmp/

 classlist files are named as follows:

 DOMAIN_COURSE_INSTITUTIONALCODE_classlist.xml

 e.g., msu_43551dedcd43febmsul1_fs03nop590001_classlist.xml
 where DOMAIN = msu  COURSE = 43551dedcd43febmsul1 
 INSTITUTIONALCODE = fs03nop590001 
 (MSU's course naming scheme - fs03 = Fall semester 2003, nop =
 department name, 590 = course number, 001 = section number.)

 fetch_enrollment requires three arguments -
 $dom - DOMAIN e.g., msu
 $affiliatesref - a reference to a hash of arrays that contains LON-CAPA 
 courses that are to be updated as keys, and institutional coursecodes 
 contributing enrollment to that LON-CAPA course as elements in each array.
 $replyref - a reference to a hash that contains LON-CAPA courses
 that are to be updated as keys, and the total enrollment count in all 
 affiliated sections, as determined from institutional data as hash elements. 

 As an example, if fetch_enrollment is called to retrieve institutional
 classlists for a single LON-CAPA course - 43551dedcd43febmsul1 which 
 corresponds to fs03nop590, sections 001, 601 and 602 , and the course
 also accommodates enrollment from a crosslisted course in the ost
 department - fs03ost580002:

 the affiliatesref would be a reference to %affiliates which would be:

 @{$affiliates{'43551dedcd43febmsul1'}} =
   ("fs03nop590001","fs03nop590601","fs03nop590602","fs03ost580002");

 fetch_enrollment would create four files in /home/httpd/perl/tmp/.
 msu_43551dedcd43febmsul1_fs03nop590001_classlist.xml
 msu_43551dedcd43febmsul1_fs03nop590601_classlist.xml
 msu_43551dedcd43febmsul1_fs03nop590602_classlist.xml
 msu_43551dedcd43febmsul1_fs03ost580002_classlist.xml

 In each file, student data would be stored in the following format
 
 <student username="smith">
  <autharg>MSU.EDU</autharg>
  <authtype>krb4</authtype>
  <email>smith@msu.edu</email>
  <enddate></enddate>
  <firstname>John</firstname>
  <generation>II</generation>
  <groupID>fs03nop590001</groupID>
  <lastname>Smith</lastname>
  <middlename>D</middlename>
  <startdate></startdate>
  <studentID>A12345678</studentID>
  <credits></credits>
  <inststatus></inststatus>
 </student>
 
 with the following at the top of the file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE text>
<students>

 (all comment - s removed)

 and a closing:
</students>

 The <startdate> and the <enddate> are the activation date and expiration date
 for this student's role. If they are absent, then the default access start and
 default access end dates are used. The default access dates can be set when 
 the course is created, and can be modified using the Automated Enrollment
 Manager, or via the 'Upload a class list','Enroll a single student' or 
 'Modify student data' utilities in the Enrollment Manager, by checking the 
 'make these dates the default for future enrollment' checkbox. If no default 
 dates have been set, then the student role will be active immediately, and will 
 remain active until the role is explicitly expired using ENRL -> Drop students. 
 If dates are to included in the XML file, they should be in the format
 YYYY:MM:DD:HH:MM:SS (: separators required).

 The <credits> tag need only be used if the credits earned by the students will 
 be different from the default for the course. The course default is set when the
 course is created and can be modifed by a Domain Coordinator via "View or
 modify a course or community" on the DC's Main Menu screen.

 A value for <inststatus> should be the institutional status used for students,
 and should be one of the types defined in the "Institutional user types"
 section in the domain config screen for:
 "Default authentication/language/timezone/portal/types" 

 If no status types are defined for the domain this tag can be omitted.
 If Autoupdate.pl is enabled in your domain, updates to the institutional 
 status set here will be updated by Autoupdate.pl, should changes occur.

 If there were 10 students in fs03nop590001, 5 students in fs03nop59o601, 
 8 students in fs03nop590602, and 2 students in fs03ost580002,
 then $$reply{'43551dedcd43febmsul1'} = 25

 The purpose of the %reply hash is to detect cases where the institutional 
 enrollment is 0 (most likely due to a problem with the data source).
 In such a case, the LON-CAPA course roster is left unchanged (i.e., no
 students are expired, even if automated drops is enabled.
 
 fetch_enrollment should return a 0 or 1, depending on whether a connection
 could be established to the institutional data source.
 0 is returned if no connection could be made.
 1 is returned if connection was successful

 A return of 1 is required for the calling modules to perform LON-CAPA
 roster changes based on the contents of the XML classlist file(s), e,g,,
 msu_43551dedcd43febmsul1_fs03nop590001_classlist.xml

 XML classlist files are temporary. They are deleted after the enrollment 
 update process in the calling module is complete.


=cut

sub fetch_enrollment {
    my ($dom,$affiliatesref,$replyref) = @_;
    foreach my $crs (sort keys %{$affiliatesref}) {
        $$replyref{$crs} = 0;
    }
    my $okflag = 0;
    return $okflag;
}

=pod

=item get_sections()

 This is called by the Automated Enrollment Manager interface
 (lonpopulate.pm) to create an array of valid sections for 
 a specific institutional coursecode.
 e.g., for MSU coursecode: fs03nop590
 ("001","601","602") would be returned

 If the array returned contains at least one element, then 
 the interface offered to the course coordinator, lists
 official sections and provides a checkbox to use to
 select enrollment in the LON-CAPA course from each official section.  

 get_sections takes two arguments - (a) the institutional coursecode
 (in the MSU case this is a concatenation of semester code, department
 and course number), and (b) the LON-CAPA domain that contains the course. 
 
 If there is no access to official course sections at your institution,
 then an empty array is returned, and the Automated Enrollment Manager
 interface will allow the course coordinator to enter section numbers
 in text boxes.
 


=cut

sub get_sections {
    my ($coursecode,$dom) = @_;
    my @secs = ();
    return @secs;
}

=pod

=item new_course()

 This is called by loncreatecourse.pm and 
 lonpopulate.pm to record that fact that a new course section
 has been added to LON-CAPA that requires access to institutional data
 At MSU, this is required, as institutional classlists can only made
 available to faculty who are officially assigned to a course.
 
 The new_course subroutine is used to check that the course owner
 of the LON-CAPA course is permitted to access the institutional
 classlist for any course sections and crosslisted classes that
 the course coordinator wishes to have affiliated with the course.
 
 If access is permitted, then 'ok' is returned.
 The course section or crosslisted course will only be added to the list of
 affiliates if 'ok' is returned.

 new_course takes three required arguments -
 (a) the institutional courseID (in the MSU case this is a concatenation of 
 semester code, department code, course number, and section number
 e.g., fs03nop590001).
 (b) the course owner. This is the LON-CAPA username and domain of the course 
 coordinator assigned to the course when it is first created, in the form
 username:domain
 (c) the LON-CAPA domain that contains the course

 new_course also takes optional fourth and fifth arguments -
 (d) the course co-owners, as a comma-separated list of username:domain for
 any co-owners.
 (e) database handle (might be set when new_course() is called by check_section
 routine within localenroll.pm).

=cut

sub new_course  {
    my ($course_id,$owner,$dom,$coowners) = @_;
    my $outcome = 'ok';
    return $outcome;
}

=pod

=item validate_courseID()

 This is called whenever a new course section or crosslisted course
 is being affiliated with a LON-CAPA course (i.e., by loncreatecourse.pm
 and the Automated Enrollment Manager in lonpopulate.pm).
 A check is made that the courseID that the course coordinator wishes
 to affiliate with the course is valid according to the institutional
 schedule of official classes 

 A valid courseID is confirmed by returning 'ok'

 validate_courseID takes two arguments -
 (a) the institutional courseID (in the MSU case this is a concatenation of
 semester code, department code, course number, and section number
 e.g., fs03nop590001).
 (b) the LON-CAPA domain that contains the course

=cut  

sub validate_courseID {
    my ($course_id,$dom) = @_;
    my $outcome = 'ok';
    return $outcome;   
}

=pod

=item validate_instcode()

This is called when a request is being made for an official course.
A check is made that the institutional code for which a course is
is being requested is valid according to the institutional
schedule of official classes.

If the username of the course owner is provided, a more restrictive
test is used, namely that the requestor is listed as instructor of
record for the course in the institution's course schedule/database.

validate_instcode takes three arguments -
 (a) the LON-CAPA domain that will contain the course
 (b) the institutional code (in the MSU case this is a concatenation of
 semester code, department code, and course number, e.g., fs03nop590.
 (c) an optional institutional username for the course owner.

An array is returned containing (a) the result of the check for a valid 
instcode, (b) an (optional) course description, and (c) the default credits
earned by students when completing this course. If no institutional credits
value is available, the default credits for the course can be set via the
course request form, or via XML in a batch file, of via the web form used
by the Domain Coordinator to create new courses one at a time.

A valid instcode is confirmed by returning 'valid'.

If no course description is available, '' should be set as
the value of the second item in the returned array.

=cut

sub validate_instcode {
    my ($dom,$instcode,$owner) = @_;
    my $outcome = '';
    my $description = '';
    my $credits = '';
    return ($outcome,$description,$credits);
}

=pod

=item validate_crosslist_access()

This is called for an official course to check whether a course
with the institutional code can have access to enrollment data
from a cross-listed institutional section code, given a co-owner.

validate_crosslist_access() takes four arguments -
(a) the course's LON-CAPA domain 
(b) the institional course code assigned to the course
(c) the institutional course section code for the crosslisting
(d) the co-owner to check for affiliation with the crosslisting 
    (username:domain).

A combination of (a), (b), (c) and (d) with access to enrollment 
data, as per institutional policies, is confirmed by returning 'valid'.

=cut

sub validate_crosslist_access {
    my ($dom,$instcode,$inst_xlist,$coowner) = @_;
    my $outcome = '';
    return $outcome;
}

=pod

=item validate_crsreq()

This is used to check whether a course request should be processed
automatically, or held in a queue pending administrative action at
the institution.

Course requests will trigger this check if the process type has been set 
to 'validate' for the course type (official, unofficial, textbook, 
placement or community) and the requestor's affiliation.  Whether
"validate" is an available option in the Domain Configuration menu 
is controlled by auto_courserequest_checks(). 
One scenario is where the request is for an official course, in which case
a check could be made that the requestor is listed as instructor of 
record for the course in the institution's course schedule/database.

Other scenarios are possible, and the routine can be customized according
to whatever rules a domain wishes to implement to run validations against
given the data passed in to the routine.

validate_crsreq takes seven arguments -
 (a) the LON-CAPA domain that will contain the course.
 (b) the username:domain for the course owner.
 (c) the course type (official, unofficial,textbook, placement or community)
 (d) a comma-separated list of institutional affiliations of 
     the course owner.
 (e) the institutional code (in the MSU case this is a concatenation of
 semester code, department code, and course number, e.g., fs03nop590).
 (f) a comma-separated list of institutional sections included in
     the course request (only applicable to official courses).
 (g) an optional reference to a hash of custom form data.
     The custom form data will come from crsreq_updates(), with one
     additional item: $custominfo->{'_LC_clonefrom'}, provided internally
     (the courseID of the LON-CAPA course being cloned).

A valid courserequest is confirmed by returning 'process'.
The following can be returned: process, rejected, pending, approval or 
error (with error condition - no :), followed by a : and then an optional message. 

(a) process  - the requestor is the recorded instructor - create the course

(b) rejected - the requestor should never be requesting this course, reject the
    request permanently

(c) pending - the requestor is not the recorded instructor, but could
    become so after administrative action at the institution. Put the
    request in a queue and, if an official course, check 
    localenroll:validate_instcode() periodically until the status changes to 
    "valid".

(d) approval - the request will be held pending review by a Domain Coordinator.

(e) error (followed by the error condition).

=cut

sub validate_crsreq {
    my ($dom,$owner,$crstype,$inststatuslist,$instcode,$instseclist,$custominfo) = @_;
    my $outcome = 'approval';
    return $outcome;
}

=pod 

=item crsreq_checks()

This is used to determine whether the "validate" option should appear in the
possible choices for course request processing in the Domain Configuration 
menu for Course Requests. Ultimately it is called by domainprefs.pm (via: 
lonnet -> lond -> localenroll.pm) The domain configuration menu includes 
a table where columns are course type (official, unofficial, textbook,
placement or community) and rows are institutional affiliations 
(e.g., Faculty, Staff, Student etc.).

crsreq_checks() takes three arguments: $dom, $reqtypes, $validations.
$dom - the domain for which validation options are needed.
$reqtypes - ref to an ARRAY of course types (i.e., official, unofficial and community.
$validations - ref to a hash of a hash which will determine whether "validate"
will be one of the possible choices for each course type (outer hash key),
and institutional type (inner hash key).

For example to allow validate to be a choice for official classes for Faculty,
req_checks would include:

$validations{'official'}{'Faculty'} = 1;

This routine is closely tied to validate_crsreq(). "Validate" should not be
a possible choice in the domain configuration menu for a particular course
type/institutional affiliation, unless a corresponding validation code has
been implemented in validate_crsreq().

For example at MSU, official courses requested by Faculty will be validated
against the official schedule of classes to check that the requestor is one
of the instructors of record for the course.  In this case validate_crsreq()
includes a call to validate_instcode().

=cut

sub crsreq_checks {
    my ($dom,$reqtypes,$validations) = @_;
    if ((ref($reqtypes) eq 'ARRAY') && (ref($validations) eq 'HASH')) {
        my (%usertypes,@order);
        if (&inst_usertypes($dom,\%usertypes,\@order) eq 'ok') {
            foreach my $type (@{$reqtypes}) {
                foreach my $inst_type (@order) {
                    $validations->{$type}{$inst_type} = 0;
                }
            }
        }
    }
    return 'ok';
}

=pod

=item crsreq_updates()

This is used to customize the LON-CAPA course request process.
There are two hash references: $incoming, and $outgoing; $incoming can
contain additional information collected from the requester, whereas $outgoing
can contain custom items to send back to lonrequestcourse.pm, which creates the
HTML displayed to the user during a course request.

Different key-value pairs may be returned to lonrequestcourse.pm in the $outgoing
hashref depending on the current action.  The available actions are:
review, prevalidate, process, created and queued.

One scenario would be to return HTML markup in: $outgoing->{'reviewweb'},
i.e., where the action is 'review', to prompt the user to provide additional
information as part of the course request, at the request review stage, 
(i.e,, the page which contains the button used to submit a completed course request).

The HTML could contain form elements (e.g., radio buttons etc.). The value(s)
selected by the requester in those form elements will be available in the incoming
hashref, for a subsequent action, if the corresponding keys have been included
in $outgoing->{'formitems'}, i.e., $outgoing will be hash of a hash.  If a
particular form item will the single valued, the value set for the key in the 
inner hash in $outgoing should be 1, otherwise, if it will be multi-valued,
the value should be multiple.

The $outgoing hashref can contain a 'formitems' key for both the prevalidate
and process actions, as calls to localenroll::crsreq_update() can originate
in lonrequestcourse::process_request() for both of those actions.

The retrieved form values are passed to localenroll::validate_crsreq() as the
optional seventh arg (a hashref) -- $custominfo.

=cut

sub crsreq_updates {
    my ($cdom,$cnum,$crstype,$action,$ownername,$ownerdomain,$fullname,$title,
        $code,$accessstart,$accessend,$incoming,$outgoing) = @_;
    unless (ref($outgoing) eq 'HASH') {
        return 'fail';
    }
    my %extrainfo;
    if (ref($incoming) eq 'HASH') {
        %extrainfo = %{$incoming};
    }
    if ($action eq 'review') {
        $outgoing->{'reviewweb'} = '';
    } elsif ($action eq 'prevalidate') {
        $outgoing->{'formitems'} = {}; # key=>value, where key is form element name
                                       #             and value is multiple, if there
                                       #             are multiple form elements with
                                       #             the same name.
    } elsif ($action eq 'process') {
        $outgoing->{'formitems'} = {}; # key=>value, where key is form element name
                                       #             and value is multiple, if there
                                       #             are multiple form elements with
                                       #             the same name.
    } elsif ($action eq 'created') {
        $outgoing->{'createdweb'} = '';
        $outgoing->{'createdmsg'} = [{
                                     mt => '',
                                     args => [],
                                    }];
        $outgoing->{'createdactions'} = {
                                            environment => {},
                                        };
                                        # environment can contain key=>value for
                                        # items to set in the course environment.
                                        # These would be items which are NOT included
                                        # in the items set via options in the course
                                        # request form. Currently self-enrollment
                                        # settings are the only ones allowed, i.e.,
                                        # internal.selfenroll_types internal.selfenroll_registered
                                        # internal.selfenroll_section internal.selfenroll_start_access 
                                        # internal.selfenroll_end_access internal.selfenroll_limit
                                        # internal.selfenroll_cap internal.selfenroll_approval
                                        # internal.selfenroll_notifylist
    } elsif ($action eq 'queued') {
        $outgoing->{'queuedmsg'} = [{
                                     mt   => '',
                                     args => [],
                                    }];
        $outgoing->{'queuedweb'} = '';
    }
    return 'ok'
}

=pod

=item export_grades()
 
This routine can be customized to push grade information to some other gradebook,
LCMS, or administrative system external to LON-CAPA.

export_grades() takes five arguments -
(a) the LON-CAPA course ID
(b) the LON-CAPA course domain
(c) a hash reference containing the following: 
    scope    => scope of the grades (e.g., course, map or resource).
    instcode => institutional course code (if an official course)
    crstype  => course type -- Course, Community or Placement
    context  => calling context, e.g., "completion" when a student completes a placement test.
(d) a perl data structure (hash of a hash) containing the grade data.
    in the outer hash, the keys are student's username:domain
    in the inner hash, keys are:  
    id        => student/employee ID
    lastname  => student's last name
    firstname => student's first name
    email     => student's "permannent" e-mail address
    section   => student's LON-CAPA course section
    total     => total points earned
    bytitle   => reference to a hash (keys are question titles, values are points
    bysymb    => reference to a hash (keys are symbs, i.e., unique resource identifiers).
(e) reference to a hash which will contain information to return.
    keys will be the student's username:domain. Value of 1 to show grades pushed 
    successfully. 

=cut

sub export_grades {
    my ($cnum,$cdom,$hashref,$dataref,$outgoing) = @_;
    my %info;
    if (ref($hashref) eq 'HASH') {
        %info = %{$hashref};
    }
    if ((ref($dataref) eq 'HASH') && (ref($outgoing) eq 'HASH')) {
        foreach my $key (keys(%{$dataref})) {
            $outgoing->{$key} = 1;
        }
        return 'ok';
    } else {
        return 'error';
    }
}

=pod

=item check_instclasses()

 This is used to supply information about which instituional course sections
 and cross-listings are available to supply enrollment data, given the current
 list of owner and co-owners. The data are used to populate the column titled:
 "Auto-enrollment of registered students" when showing full detailed for a course
 in the course catalog.

 This subroutine takes four arguments -

 (a) $owners - comma-separated list of username:domain for course owner 
     and co-owners.
 (b) $dom - domain of course.
 (c) $classes - reference to hash of institutional course sections and
     crosslistings for which access to enrollment data is being checked.
 (d) $validated - reference to hash which will be populated with all
     keys from incoming $classes hashref, for which one or more of the
     owner/co-owners has rights to access enrollment data. For each
     key included in $validated hashref, corresponding value will be set to 1.
  
 The subroutine returns 'ok' if there is no processing error.

=cut


sub check_instclasses {
    my ($owners,$dom,$classes,$validated) = @_;
    if ((ref($classes) eq 'HASH') && (ref($validated) eq 'HASH')) {
        foreach my $class (keys(%{$classes})){
            if (&check_section($class,$owners,$dom) eq 'ok') {
                $validated->{$class} = 1;
            }
        }
    }
    return 'ok';
}

=pod

=item instsec_reformat()

 Inputs: $dom, $action, $instsecref

 $dom is the course's domain
 $action is either: clutter or declutter
 $instsecref is a reference to a hash, in which each key is
 course num:course code, and each value is either an array of 
 institutional sections, or (in the case of crosslisted courses)
 an array of institutional course sections.

 Returns: ok

 Side effects: will modify the items in the array as determined by
 code implemented for the domain.  Modification will differ depending
 on whether the action is clutter or declutter.

 The idea is that "clutter" will modify the name of the section such
 that a concatenation of institutional code then (modified) section
 will result in a string that other customized routines in localenroll.pm
 can separate without ambiguity into instituional code then (real)
 institutional section using a regular expression.

 Conversely, "declutter" will modify the name of an already modified
 item such that display of the concatenated string (e.g., for a 
 crosslisting in the course catalog) does not include the "added"
 characters used to eliminate ambiguity. 

 Examples (MSU):

 Starting in Fall 2021 at MSU, institution section numbers are no
 longer guaranteed to be three digit numbers (including leading zeroes).

 So, for example the course code: fs21phy183b might have sections:
 001, 002, LEC1, LEC2, and be crosslisted with fs21phy233b (with 
 sections: 730, LEC3, LEC4).

 The sections: LEC1, and LEC2 should be changed to _LEC1, and _LEC2
 before creating the inner keys in the %affiliates hash of a hash,
 passed to fetch_enrollment() in Enrollment.pm.  They will however
 be stored in the course's environment as LEC1 and LEC2.

 For the crosslistings, LEC3 and LEC4 should be changed to 
 _LEC3 and _LEC4 before storing in the course's environment.db file.

 In both cases when it comes time to extract the various components
 of an institutional section code (i.e., the concatenated string) in
 fetch_enrollment(), for example, the regexp used at MSU would be:
 
 if ($class =~ m/^([suf]s)(\d{2})(\w{2,4})(\d{3,4}[A-Za-z]?)(\d{3}|_[A-Za-z0-9]{1,5})$/) {
     my ($sem,$yr,$subj,$crse,$sec) = ($1,$2,$3,$4,$5);

 The three digit sections would match the \d{3} and the other sections
 (LEC1, LEC2 etc.) would match the _[A-Za-z0-9]{1,5}.

 The customization in &instsec_reformat() would be:

     if ($action eq 'clutter') {
         unless ($item =~ /^\d{3}$/) {
             $item = '_'.$item;
         }
     } elsif ($action eq 'declutter') {
         if ($item =~ /^([suf]s\d{2}\w{2,4}\d{3,4}[A-Za-z]?)(\d{3}|_[A-Za-z0-9]{1,5})$/) {
             my ($instcode,$instsec) = ($1,$2);
             $instsec =~ s/^_//;
             $item = $instcode.$instsec;
         } elsif ($item =~ /^_[A-Za-z0-9]{1,5}$/) {
             $item =~ s/^_//;
         }
     }

=cut

sub instsec_reformat {
    my ($dom,$action,$instsecref) = @_;
    if ((ref($instsecref) eq 'HASH') &&
        (($action eq 'clutter') || ($action eq 'declutter'))) {
        foreach my $key (keys(%{$instsecref})) {
            if (ref($instsecref->{$key}) eq 'ARRAY') {
                foreach my $sec (@{$instsecref->{$key}}) {
                    if ($action eq 'clutter') {
                        # modify the section, as needed.
                        next;
                    } elsif ($action eq 'declutter') {
                        # modify the section, as needed.
                        next;
                    }
                }
            }
        }
    }
    return 'ok';
}

=pod

=item create_password()

 This is called when the authentication method set for the automated 
 enrollment process when enrolling new users in the domain is "localauth".
 This could be signalled for the specific user by the value of localauth
 for the <authtype> tag from the classlist.xml files, or if this is blank,
 the default authtype, set by the domain coordinator when creating the course
 with loncreatecourse.pm.
  
 create_password takes three arguments -
 (a) $authparam - the value of <autharg> from the classlist.xml files,
 or if this blank, the default autharg, set by the domain coordinator when 
 creating the course with loncreatecourse.pm
 (b) $dom - the domain of the new user.
 (c) $username - the username of the new user (currently not actually used)

 Four values are returned:
 (a) the value of $authparam - which might have been changed
 (b) a flag to indicate whether a password had been created
 0 means no password created
 1 means password created.  In this case the calling module - Enrollment.pm
 will send the LON-CAPA username and password to the new user's e-mail
 (if one was provided), or to the course owner (if one was not provided and
 the new user was created by the automated process), or to the active
 course coordinator (if the new user was created using the 'update roster
 now' interface included in the Automated Enrollment Manager).  
 (c) a flag to indicate that the authentication method is correct - 'ok'.
 If $authchk is not set to 'ok' then account creation and enrollment of the 
 new user will not occur.
 (d) if a password was created it can be sent along.  This is the password 
 which will be included in the e-mail sent to the new user, or made available    
 to the course owner/course coordinator if no e-mail address is provided. If
 you do not wish to send a password, but want to give instructions on obtaining
 one, you could set $newpasswd as those instructions. (e.g.,
 $newpasswd = '(Please visit room 212, ACNS Bldg. to obtain your password)';
 The value of $newpasswd is NOT written in the user's LON-CAPA passwd file in
 /home/httpd/lonUsers/$dom/a/b/c/abcuser/passwd, which in the case of a user
 employing localauth will contain 'localauth:$authparam'.  If you need to include
 a parameter in the user's passwd file, you should return it as $authparam,
 i.e., the first of the variables returned by create_password().             


=cut 

sub create_password {
    my ($authparam,$dom,$username) = @_;
    my $authchk = 'ok';
    my $newpasswd = '';
    my $create_passwd = 0;
    return ($authparam,$create_passwd,$authchk,$newpasswd);
}

=pod

=item instcode_format()

 Split coursecodes into constituent parts.   
 e.g., INSTITUTIONALCODE = fs03nop590, LON-CAPA COURSEID: 43551dedcd43febmsul1
 (MSU's course naming scheme - fs03 = Fall semester 2003, nop =
 department name, 590 = course number)

 Incoming data:
 $dom (domain)
 $$instcodes{'43551dedcd43febmsul1'} = 'fs03nop590' (hash of courseIDs)
 
 fs03nop590 would be split as follows
 @{$codetitles} = ("year","semester","department","number")
 $$codes{'year'} = '2003'
 $$codes{'semester'} = 'Fall'
 $$codes{'department'} = 'nop'
 $$codes{'number'} = '590'

 requires six arguments:
 domain ($dom)
 reference to hash of institutional course IDs ($instcodes)  
 reference to hash of codes ($codes)
 reference to array of titles ($codetitles)
 reference to hash of abbreviations used in categories
 reference to hash of arrays specifying sort order used in category titles   

 e.g.,     %{$$cat_titles{'Semester'}} = (
                   fs => 'Fall',
                   ss => 'Spring',
                   us => 'Summer');

 e.g., @{$$cat_order{'Semester'}} = ('ss','us','fs'); 
 returns 1 parameter: 'ok' if no processing errors. 

 Detailed help:
 http://yourloncapaserver/adm/help/Institutional_Integration_Course_Codes.hlp

=cut


sub instcode_format () {
    my ($dom,$instcodes,$codes,$codetitles,$cat_titles,$cat_order) = @_;
    my $outcome = 'ok';
    return $outcome;
}

=pod

=item possible_instcodes()

Gather acceptable values for institutional categories to use in course creation request form for official courses.

 requires five arguments:

 domain ($dom)
 reference to array of titles ($codetitles)
 reference to hash of abbreviations used in categories ($cat_titles).
 reference to hash of arrays specifying sort order used in 
           category titles ($cat_order).
 reference to array which will contain order of component parts used 
           in institutional code ($code_order).

 e.g., 
 @{$codetitles} = ('Year','Semester',"Department','Number');

 %{$$cat_titles{'Semester'}} = (
                   fs => 'Fall',
                   ss => 'Spring',
                   us => 'Summer');

 @{$$cat_order{'Semester'}} = ('ss','us','fs');
 @{$code_order} = ('Semester','Year','Department','Number');

 returns 1 parameter: 'ok' if no processing errors.

=cut

sub possible_instcodes {
    my ($dom,$codetitles,$cat_titles,$cat_order,$code_order) = @_;
    @{$codetitles} = ();
    %{$$cat_titles{'Semester'}} = ();
    @{$$cat_order{'Semester'}} = ('ss','us','fs');
    @{$code_order} = ();
    return 'ok';
}


=pod

=item institutional_photos()

 Called when automated enrollment manager is used to update student photos.

 Incoming data: six arguments
 (a) $dom (domain)
 (b) $crs (LONCAPA course number)
 (c) $affiliates: a reference to a hash with the keys set to the 
 institutional course IDs for the course.
 (d) $result: a reference to a hash which will return usernames  
     of students (& separated) in following categories (the keys):
     new, update, missing, same, deleted, noid, nouser. The list 
     includes those students for whom the result of the modification 
     process was either addition of a new photo. update of an
     existing photo, photo was found to be missing from institution's
     data store, photo used is same as before, or photo was 
     deleted from storage on LON-CAPA server housing student's
     information, no student/employee ID was available. 
               
 (e) $action: the type of action needed. (e.g., update, delete);
 (f) $students: a reference to a hash with the keys set to student 
 usernames and domains in the form username:domain, and values set
 to the studentID, if action is required for specific students.  

 returns 1 parameter: 'ok' if no processing errors.
 other course or student specific values can be stored as values
 in the appropriate referenced hashes. 

=cut

sub institutional_photos {
    my ($dom,$crs,$affiliates,$result,$action,$students) = @_;
    my $outcome = 'ok';
    return $outcome;
}

=pod

=item photo_permission()

 Incoming data: three arguments
 (a) $dom (domain)
 (b) $perm_reqd: a reference to a a scalar that is either 'yes'
 if a course owner must indicate acceptance of conditions of use,
 'no' otherwise.
 (c) $conditions: the text of the conditions of use.
    
 returns 1 parameter: 'ok' if no processing errors.
 $$perm_reqd is set to 'yes' or 'no'
 $$agreement is set to conditions of use - plain text string
             which will be displayed in a textarea in a web form.


=cut

sub photo_permission {
   my ($dom,$perm_reqd,$conditions) = @_;
   $$perm_reqd = 'no';
   $$conditions = '';
   my $outcome = 'ok';
   return $outcome;
}

=pod

=item manager_photo_update()

 Incoming data: one argument
 (a) $dom (domain)

 returns 2 parameters: update (0 or 1), and comment.
 Called by automated enrollment manager, to determine 
 whether "Update Student photos" button will be available,
 and if so, the message (plain text string) that will be displayed
 with the button. 


=cut
                                                                                        
sub manager_photo_update {
    my ($dom) = @_;
    my $update = 0;
    my $comment = '';
    return ($update,$comment);
}

=pod


=item check_section()

 Incoming data: three arguments (+ fourth optional argument)
 (a) $class - institutional class id (coursecode concatanated with section) 
 (b) $owner - course owner (2.2 and later username:domain; pre-2.2 username;
                            2.6 and later - comma-separated list of 
                            username:domain for course owner and co-owners.)
 (c) $dom - domain of course
 (d) $dbh - optional database handle

 returns 1 parameter - $sectioncheck ('ok' or other value). 
 Verifies that at least one of the course owner (or co-owners) have access 
 to classlist for specific class according to institution's SIS
 'ok' if access available  


=cut

sub check_section {
    my ($class,$owner,$dom,$dbh) = @_;
    my $sectioncheck = 'ok';
    return $sectioncheck;
}

=pod

=item instcode_defaults()

 Incoming data: three arguments
 (a) $dom - domain
 (b) $defaults - reference to hash which will contain default regular
                 expression matches for different components of an 
                 institutional course code 
 (c) $code_order - reference to array which will contain order of 
                   component parts used in institutional code.  

 returns 1 parameter - ('ok' or other value).
 Used to construct a regular expression to be used when searching for
 courses based on fragments of an institutional code.
 $defaults contains defaults to use for each component, and code_order
 contains keys of hash in order in which they are to be concatenated.

 e.g., INSTITUTIONALCODE = fs03nop590
 (MSU's course naming scheme - fs  = semester, 03 = year, nop =
 department name, 590 = course number)

     %{$defaults} = (
        'Year' => '\d{2}',
        'Semester' => '^[sfu]s', 
        'Department' => '\w{2,3}',
        'Number' => '\d{3,4}\w?',
     );

     @{$code_order} = ('Semester','Year','Department','Number');

 Detailed help:
 http://yourloncapaserver/adm/help/Institutional_Integration_Course_Codes.hlp

=cut

sub instcode_defaults {
    my ($dom,$defaults,$code_order) = @_;
    return 'ok';
}


=pod

=item allusers_info()

 Incoming data: three arguments
 (a) $dom - domain
 (b) $instusers - reference to hash which will contain hashes, 
                 where keys will be usernames and value will be a 
                 hash of user information. Keys in the inner hash 
                 will be some or all of: lastname,firstname,
                 middlename, generation, id, inststatus - 
                 institutional status (e.g., faculty,staff,student)
                 Values are all scalars except inststatus,
                 which is an array.
 (c) $instids - reference to hash which will contain ID numbers. 
                keys will be unique IDs (student or faculty/staff ID)
                values will be either: scalar (username) or an array 
                if a single ID matches multiple usernames.
 (d) $lc_users - reference to hash containing LON-CAPA usernames in 
                 in domain $dom, as keys. Needed if institutional
                 data source only allows query by username.
 (e) $counts - reference to hash (optional), for use when called 
               from Autoupdate.pl which can contain counts for
               user-specified items retrieved in allusers_info()
               or in custom subroutines which it calls. Key in
               hashref, and count value will be printed to 
               autoupdate.log by Autoupdate.pl.  
                 
 returns 1 parameter - 'ok' if no processing error, or other value 
                       if an error occurred.
 side effects - populates the $instusers and $instids refs to hashes.
                with information for all users from all available 
                institutional datafeeds.


=cut

sub allusers_info {
    my ($dom,$instusers,$instids,$lc_users,$counts) = @_;
    my $outcome = 'ok';
    return $outcome; 
}

=pod

=item get_userinfo()

 Incoming data: four required arguments and additional optional arguments
 Two modes of operation:
 (1) Retrieves institutional data for a single user either by username
     if $uname is included as second argument, or by ID if $id is 
     included as a third argument.  Either (b) or (c) must be provided.
     (g), (h) and (i) will be undefined.
 (2) Retrieves institutional user data from search of an institutional
     directory based on a search. (g) and (h) are required.
     (i) is optional. (b) and (c) will be undefined. 

 (a) $dom - domain
 (b) $uname - username of user
 (c) $id - student/faculty ID of user
 (d) $instusers - reference to hash which will contain info for user
                 as key = value; keys will be one or all of:
                 lastname,firstname,middlename,generation,id,inststatus -
                 institutional status (e.g., faculty,staff,student)
                 Values are all scalars except inststatus,
                 which is an array.
 (e) $instids - reference to hash which will contain ID numbers - 
                 keys will be unique IDs (student or faculty/staff ID)  
                 values will be either: scalar (username) or an array
                 if a single ID matches multiple usernames.
 (f) $types - optional reference to array which contains 
              institutional types to check.
 (g) $srchby - optional if $uname or $id defined, otherwise required.
               Allowed values include: 1. lastfirst, 2. last, 3. uname
               4. email, corresponding to searches by 1. lastname,firstname;
               2. lastname; 3. username; 4. e-mail address
 (h) $srchterm - optional if $uname or $id defined, otherwise required
                String to search for.
 (i) $srchtype - optional. Allowed values: contains, begins (defaults
                to exact match otherwise).

 returns 1 parameter - 'ok' if no processing error, or other value 
                       if an error occurred.
 side effects - populates the $instusers and $instids refs to hashes.
                with information for specified username, or specified
                id, if fifth argument provided, from all available, or 
                specified (e.g., faculty only) institutional datafeeds,
                if sixth argument provided.

 WARNING: You need to set $outcome to 'ok' once you have customized
          this routine to communicate with an instititional 
          directory data source, otherwise institutional directory 
          searches will always be reported as being unavailable
          in domain $dom.

=cut

sub get_userinfo {
    my ($dom,$uname,$id,$instusers,$instids,$types,
        $srchby,$srchterm,$srchtype) = @_;
    my $outcome = 'unavailable';
    return $outcome;
}

=pod

=item get_multusersinfo()

 (a) $dom - domain
 (b) $type - username or id
 (c) $unamenames - reference to hash containing usernames of users
 (d) $instusers - reference to hash which will contain info for user
                 as key = value; keys will be one or all of:
                 lastname,firstname,middlename,generation,id,inststatus -
                 institutional status (e.g., faculty,staff,student)
                 Values are all scalars except inststatus,
                 which is an array.
 (e) $instids - reference to hash which will contain ID numbers -
                 keys will be unique IDs (student or faculty/staff ID)
                 values will be either: scalar (username) or an array
                 if a single ID matches multiple usernames.

 returns 1 parameter - 'ok' if no processing error, or other value
                       if an error occurred.

 side effects - populates the $instusers and $instids refs to hashes.
                with information for specified username, or specified
                id, if fifth argument provided, from all available, or
                specified (e.g., faculty only) institutional datafeeds,
                if sixth argument provided.

 WARNING: You need to set $outcome to 'ok' once you have customized
          this routine to communicate with an instititional
          directory data source, otherwise retrieval of institutional
          user information will always be reported as being unavailable
          in domain $dom.

=cut

sub get_multusersinfo {
    my ($dom,$type,$usernames,$instusers,$instids) = @_;
    my $outcome = 'unavailable'; 
    return $outcome;
}

=pod

=item inst_usertypes() 

 Starting with LON-CAPA 2.11.0 use of this subroutine
 is deprecated. The domain configuration web GUI
 accessible to Domain Coordinators will be used to
 manage institutional types.  If you have previously
 customized this routine, then values set there will
 be used when displaying the "Institutional user types"
 section in the domain config screen for:
 "Default authentication/language/timezone/portal/types".

 Once you have visited that screen and saved the settings,
 configuration thereafter will be via the web GUI of
 values stored in the domain's configuration.db file on
 the primary library server in the domain, and values in
 inst_usertypes() will no longer be consulted.
 
 Incoming data: three arguments
 (a) $dom - domain
 (b) $usertypes - reference to hash which will contain 
                 key = value, where keys are institution 
                 affiliation types (e.g., Faculty, Student etc.)
                 and values are titles (e.g., Faculty/Academic Staff)
 (c) $order - reference to array which will contain the order in
              which institutional types should be shown
              when displaying data tables (e.g., default quotas    
              or updateable user fields (see domainprefs.pm) 
 returns 1 parameter - 'ok' if no processing error, or other value 
                        if an error occurred.


=cut

sub inst_usertypes {
    my ($dom,$usertypes,$order) = @_;
    @{$order} = ();
    %{$usertypes} = ();
    my $outcome = 'ok';
    return $outcome;
}

=pod

=item username_rules()

 Incoming data: three arguments 
 (a) $dom - domain
 (b) $ruleshash - reference to hash containing rules
                  (a hash of a hash)
                  keys of top level hash are short names  
                   (e.g., netid, noncredit) 
                  for each key, value is a hash
                      name => long name for rule  
                      desc => description of rule
                      authtype => (krb5,krb4,int, or loc)
                                 authentication type for rule 
                      authparm => authentication parameter for rule
                      authparmfixed => 1 if authparm used when
                          creating user for rule must be authparm  
                      authmsg => Message to display describing 
                                 authentication to use for this rule

 (c) $rulesorder - reference to array containing rule names 
                   in order to be displayed


  returns 'ok' if no processing error.

=cut

sub username_rules {
    my ($dom,$ruleshash,$rulesorder) = @_;
    my $outcome;
    return $outcome;
}

=pod

=item id_rules()

 Incoming data: three arguments
 (a) $dom - domain
 (b) $ruleshash - reference to hash containing rules
                  (a hash of a hash)
                  keys of top level hash are short names
                   (e.g., netid, noncredit)
                  for each key, value is a hash
                      name => long name for rule
                      desc => description of rule

 (c) $rulesorder - reference to array containing rule names
                   in order to be displayed

  returns 'ok' if no processing error.

=cut

sub id_rules {
    my ($dom,$ruleshash,$rulesorder) = @_;
    my $outcome;
    return $outcome;
}

=pod

=item selfcreate_rules()

 Incoming data: three arguments
 (a) $dom - domain
 (b) $ruleshash - reference to hash containing rules
                  (a hash of a hash)
                  keys of top level hash are short names
                   (e.g., netid)
                  for each key, value is a hash
                      name => long name for rule
                      desc => description of rule

 (c) $rulesorder - reference to array containing rule names
                   in order to be displayed

  returns 'ok' if no processing error.


=cut

sub selfcreate_rules {
    my ($dom,$ruleshash,$rulesorder) = @_;
    my $outcome;
    return $outcome;
}

=pod

=item unamemap_rules()

 Incoming data: three arguments
 (a) $dom - domain
 (b) $ruleshash - reference to hash containing rules
                  (a hash of a hash)
                  keys of top level hash are short names
                   (e.g., netid)
                  for each key, value is a hash
                      name => long name for rule
                      desc => description of rule

 For example: 

        %{$ruleshash} = (
            emailaddress  => {
                               name     => 'Email address to UserID',
                               desc     => 'Extract userID from userID@example.tld',
                             },
                        );
  would enable display of a checkbox for: 'Email address to UserID' in the
  "Available conversions" item in the "Mapping for missing usernames via standard log-in"
  panel available to a Domain Coordinator via:
  Main Menu > Set domain configuration > Display ("Default authentication/language/timezone/portal/types" checked)

 (c) $rulesorder - reference to array containing rule names
                   in order to be displayed

  returns 'ok' if no processing error.

=cut

sub unamemap_rules {
    my ($dom,$ruleshash,$rulesorder) = @_;
    my $outcome;
    return $outcome;
}

=pod

=item username_check() 

 Incoming data: four arguments
 (a) $dom - domain (scalar) 
 (b) $uname - username to compare against rules (scalar)
 (c) $to_check (reference to array of rule names to check)
 (d) $resultshash (reference to hash of results)
                    hash of results for rules checked
                   - keys are rule names
                   - values are: 1 or 0 (for matched or unmatched) 

 returns 'ok' if no processing error.


=cut

sub username_check {
    my ($dom,$uname,$to_check,$resultshash) = @_;
    my $outcome;
    return $outcome; 
}

=pod

=item id_check()

 Incoming data: four arguments
 (a) $dom - domain (scalar)
 (b) $id - ID to compare against rules (scalar)
 (c) $to_check (reference to array of rule names to check)
 (d) $resultshash (reference to hash of results)
                    hash of results for rules checked
                   - keys are rule names
                   - values are: 1 or 0 (for matched or unmatched)

 returns 'ok' if no processing error.


=cut

sub id_check {
    my ($dom,$id,$to_check,$resultshash) = @_;
    my $outcome;
    return $outcome;
}

=pod

=item selfcreate_check()

 Incoming data: four arguments
 (a) $dom - domain (scalar)
 (b) $selfcreatename - e-mail proposed as username (compare against rules - scalar)
 (c) $to_check (reference to array of rule names to check)
 (d) $resultshash (reference to hash of results)
                   hash of results for rules checked
                   - keys are rule names
                   - values are: 1 or 0 (for matched or unmatched)

 returns 'ok' if no processing error.


=cut

sub selfcreate_check {
    my ($dom,$selfcreatename,$to_check,$resultshash) = @_;
    my $outcome;
    return $outcome;
}

=pod

=item unamemap_check()

 Incoming data: four arguments
 (a) $dom - domain (scalar)
 (b) $uname - username entered on log-in page (compare against rules - scalar)
 (c) $to_check (reference to array of rule names to check)
 (d) $resultshash (reference to hash of results)
                   hash of results for rules checked
                   - keys are rule names
                   - values are derived username from substitution operation
                     applied to $uname.

  For example, in the msu domain the rule "msuemail" will replace an MSU
  email address submitted as a username, with the part before the @msu.edu,
  (known as the MSUNetID), which is what is used in LON-CAPA as a username.

  if ($dom eq 'msu') {
      foreach my $item (@{$to_check}) {
          if ($item eq 'msuemail') {
              if ($uname =~ /^(\w{2,8})\@msu\.edu$/) {
                  $resultshash->{$item} = $1;
              }
          }
      }
   }

 returns 'ok' if no processing error.

=cut

sub unamemap_check {
    my ($dom,$uname,$to_check,$resultshash) = @_;
    my $outcome;
    return $outcome;
}

=pod

=item AUTOLOAD()

 Incoming data: none
 Returns ''

 Prevents errors when undefined subroutines are called in this package
 Will allow new routines added in the future to be called from lond etc.
 without the need for customized versions of local*.pm packages to be
 modified to include the new subroutines immediately.

 See "Programming Perl" 3rd ed. pp 296-298.   

=back

=cut

sub AUTOLOAD {
    our $AUTOLOAD;
    return '';
}

1;

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>