#!/usr/bin/perl
$|=1;
# Script to complete processing of course/community requests
# for unofficial courses, textbook courses and communities 
# queued pending validation, once validated.
#  
# $Id: createpending.pl,v 1.1 2014/04/16 15:36:38 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

createpending.pl

=head1 SYNOPSIS

CGI script to process pending course/community requests 
and output URL which user will return to if course 
creation successful.

Data expected by createpending.pl are the same fields
as included for a POST to the external validation site,
as specified in the domain configuration for
course request validation, which can be some or all of: 

1. courseID (domain_coursenum)
2. requester's username:domain
3. course type
4. course description

Both 1 and 2 are required, whereas 3 and 4 are optional.

The data can be passed either in a query string or as
POSTed form variables.

=head1 Subroutines

=over 4

=cut

#############################################
#############################################

use strict;

use lib '/home/httpd/lib/perl/';
use LONCAPA::loncgi;
use Apache::lonnet;
use Apache::loncommon();
use Apache::lonuserutils();
use Apache::loncoursequeueadmin();
use Apache::lonlocal;
use LONCAPA;
use IO::Socket;

&main();
exit 0;

#############################################
#############################################

=pod

=item main()

Inputs: None

Returns: Nothing

Description: Main program. Determines if requesting IP is the IP 
             of the validation server. Side effect is to
             print content (with text/plain HTTP header).
             Content is URL course requester should use
             to access the course, when course creation
             is successful.

=cut

#############################################
#############################################

sub main {
    my $query = CGI->new();

    my @okdoms = &Apache::lonnet::current_machine_domains();

    my $perlvar = &LONCAPA::Configuration::read_conf();
    my $lonidsdir;
    if (ref($perlvar) eq 'HASH') {
        $lonidsdir = $perlvar->{'lonIDsDir'};
    }
    undef($perlvar);

    my ($cdom,$cnum);
    if ($query->param('course')) {
        my $course = $query->param('course'); 
        $course =~ s/^\s+|\s+$//g;
        if ($course =~ /^($LONCAPA::match_domain)_($LONCAPA::match_courseid)$/) {
            my $possdom = $1;
            my $domdesc = &Apache::lonnet::domain($possdom);
            unless ($domdesc eq '') {
                $cdom = $possdom;
            }
        } else {
            print &LONCAPA::loncgi::cgi_header('text/plain',1);
            return;
        }
    }

    if ($cdom eq '') {
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
        return;
    }

    if (!grep(/^\Q$cdom\E$/,@okdoms)) {
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
        return;
    }

    my %domconfig = &Apache::lonnet::get_dom('configuration',['requestcourses'],$cdom);
    my $remote_ip = $ENV{'REMOTE_ADDR'};
    my $allowed;

    if (ref($domconfig{'requestcourses'}) eq 'HASH') {
        if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') {
            if ($domconfig{'requestcourses'}{'validation'}{'url'} =~ m{^https?://([^/]+)/}) {
                my $ip = gethostbyname($1);
                if ($ip ne '') {
                    my $validator_ip = inet_ntoa($ip);
                    if (($validator_ip ne '') && ($remote_ip eq $validator_ip)) {
                        $allowed = 1;
                    }
                }
            } elsif ($domconfig{'requestcourses'}{'validation'}{'url'} =~ m{^/}) {
                if ($remote_ip ne '') {
                    if (($remote_ip eq '127.0.0.1') || ($remote_ip eq $ENV{'SERVER_ADDR'})) {
                        $allowed = 1;
                    }
                }
            }
        }
    }

    my (%params,@fields,$numrequired);
    if ($allowed) {
        &Apache::lonlocal::get_language_handle();
        my ($validreq,@fields);
        if (ref($domconfig{'requestcourses'}) eq 'HASH') {
            if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') {
                if (ref($domconfig{'requestcourses'}{'validation'}{'fields'}) eq 'ARRAY') {
                    $numrequired = scalar(@{$domconfig{'requestcourses'}{'validation'}{'fields'}});
                    foreach my $field (@{$domconfig{'requestcourses'}{'validation'}{'fields'}}) {
                        $params{$field} = $query->param($field);
                        if ($field eq 'owner') {
                            if ($query->param($field) =~ /^(LONCAPA::match_username):($LONCAPA::match_domain)$$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'course') {
                            if ($query->param($field) =~ /^(?:LONCAPA::match_domain)_(?:LONCAPA::match_courseid)$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'coursetype') {
                            if ($query->param($field) =~ /^(unofficial|community|textbook)$/) {
                                $params{$field} = $query->param($field);
                            }
                        }
                        if ($field eq 'description') {
                            $params{$field} = $query->param($field);
                        }
                    }
                    if ($numrequired == scalar(keys(%params))) {
                        $validreq = 1;
                    }
                }
            }
        }
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
        if ($validreq) {
            $params{'token'} = $query->param('token');
            my ($url,$code) = &process_courserequest($cdom,$lonidsdir,\%params);
            if ($url) {
                print("$url\n$code");
            }
        }
    } else {
        print &LONCAPA::loncgi::cgi_header('text/plain',1);
    }
    return;
}

#############################################
#############################################

=pod

=item process_courserequest()

Inputs: $dom - domain of course to be created
        $lonidsdir - Path to directory containing session files for users.
                     Perl var lonIDsDir is read from loncapa_apache.conf
                     in &main() and passed as third arg to process_courserequest().
        $params - references to hash of key=value pairs from input
                  (either query string or POSTed). Keys which will be
                  used are fields specified in domain configuration
                  for validation of pending unofficial courses, textbook courses,
                  and communities.

Returns: $url,$code - If processing of the pending course request succeeds,
                      a URL is returned which may be used by the requester to access
                      the new course. If a six character code was also set, that is
                      returned as a second item.

Description: Processes a pending course creation request, given the username 
             and domain of the requester and the courseID of the new course. 

=cut

#############################################
#############################################

sub process_courserequest {
    my ($dom,$lonidsdir,$params) = @_;
    return () unless (ref($params) eq 'HASH');

    my $cid = $params->{'course'};
    my $owner = $params->{'owner'};
    my $token = $params->{'token'};
    my ($ownername,$ownerdom) = split(/:/,$owner);
    my $ownerhome = &Apache::lonnet::homeserver($ownername,$ownerdom);
    return () if ($ownerhome eq 'no_host');
    return () if ($cid eq '');
    my ($cdom,$cnum) = split(/_/,$cid); 
    my $chome = &Apache::lonnet::homeserver($cnum,$cdom);
    return () unless ($chome eq 'no_host');
    my ($url,$code);
    my $confname = &Apache::lonnet::get_domainconfiguser($cdom);
    my %queuehash = &Apache::lonnet::get('courserequestqueue',
                                         [$cnum.'_pending'],$cdom,$confname);
    return () unless (ref($queuehash{$cnum.'_pending'}) eq 'HASH');
    my ($crstype,$lonhost,$hostname,$handle);
    $crstype = $queuehash{$cnum.'_pending'}{'crstype'};
    $lonhost = $queuehash{$cnum.'_pending'}{'lonhost'};
    if ($lonhost ne '') {
        $hostname = &Apache::lonnet::hostname($lonhost);
    }
    my $savedtoken = $queuehash{$cnum.'_pending'}{'token'};
    my $process;
    if ($token ne '') {
        if ($token eq $savedtoken) {
            $process = 1;
        }
    }
    return () unless ($process);

    my %domdefs = &Apache::lonnet::get_domain_defaults($cdom);
    my ($logmsg,$newusermsg,$addresult,$enrollcount,$response,$keysmsg,%longroles,$code,
        $dcname,$dcdom);
    my $type = 'Course';
    my $now = time;
    if ($crstype eq 'community') {
        $type = 'Community';
    }
    my @roles = &Apache::lonuserutils::roles_by_context('course','',$type);
    foreach my $role (@roles) {
        $longroles{$role}=&Apache::lonnet::plaintext($role,$type);
    }
    my @permissions = ('mau','ccc','cin','cta','cep','ccr','cst');
    my %permissionflags = ();
    &set_permissions(\%permissionflags,\@permissions);
    my %domconfig = &Apache::lonnet::get_dom('configuration',['requestcourses'],$cdom);
    if (ref($domconfig{'requestcourses'}) eq 'HASH') {
        if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') { 
            if ($domconfig{'requestcourses'}{'validation'}{'dc'}) {
                ($dcname,$dcdom) = split(/:/,$domconfig{'requestcourses'}{'validation'}{'dc'});
            }
        }
    }
    my %history = &Apache::lonnet::restore($cid,'courserequests',$ownerdom,$ownername);
    if (ref($history{'details'}) eq 'HASH') {
        my %reqhash = (
                        reqtime   => $now,
                        crstype   => $crstype,
                        details   => $history{'details'},
                      );
        my %customitems;
        my $fullname = &Apache::loncommon::plainname($ownername,$ownerdom);
        my $inprocess = &Apache::lonnet::auto_crsreq_update($cdom,$cnum,$crstype,'process',
                                                            $ownername,$ownerdom,$fullname,
                                                            $history{'details'}{'cdescr'});
        if (ref($inprocess) eq 'HASH') {
            foreach my $key (keys(%{$inprocess})) {
                if (exists($history{'details'}{$key})) {
                    $customitems{$key} = $history{'details'}{$key};
                }
            }
        }
        &set_dc_env($dcname,$dcdom,$dcdom,$ownername,$ownerdom,$crstype);
        my ($result,$postprocess) = &Apache::loncoursequeueadmin::course_creation($cdom,$cnum,
                                        'domain',$history{'details'},\$logmsg,\$newusermsg,
                                        \$addresult,\$enrollcount,\$response,\$keysmsg,\%domdefs,
                                        \%longroles,\$code,\%customitems);
        &unset_dc_env($dcname,$dcdom,$ownername,$ownerdom,$crstype);
        if ($result eq 'created') {
            my $disposition = 'created';
            my $reqstatus = 'created';
            if (($code) || ((ref($postprocess) eq 'HASH') &&
                (($postprocess->{'createdweb'}) || ($postprocess->{'createdmsg'})))) {
                my $addmsg = [];
                my $recipient = $ownername.':'.$ownerdom;
                my $sender = $recipient;
                if ($code) {
                    push(@{$addmsg},{
                                      mt   => 'Students can automatically select your course: "[_1]" by entering this code: [_2]',
                                      args => [$history{'details'}{'cdescr'},$code],
                                    });
                }
                if (ref($postprocess) eq 'HASH') {
                    if (ref($postprocess->{'createdmsg'}) eq 'ARRAY') {
                        foreach my $item (@{$postprocess->{'createdmsg'}}) {
                            if (ref($item) eq 'HASH') {
                                if ($item->{'mt'} ne '') {
                                    push(@{$addmsg},$item);
                                }
                            }
                        }
                    }
                }
                if (scalar(@{$addmsg}) > 0) {
                    my $type = 'createdcrsreq';
                    if ($code) {
                        $type = 'uniquecode';
                    }
                    &Apache::loncoursequeueadmin::send_selfserve_notification($recipient,$addmsg,$cdom.'_'.$cnum,
                                                                              $history{'details'}{'cdescr'},
                                                                              $now,$type,$sender);
                }
            }
            if ($code) {
                $reqhash{'code'} = $code;
            }
            my $creationresult = 'created';
            my ($storeresult,$updateresult) =
                &Apache::loncoursequeueadmin::update_coursereq_status(\%reqhash,$cdom,
                                              $cnum,$reqstatus,'request',$ownerdom,$ownername);
#
# check for session for this user
# if session, construct URL point at check for new roles.
#
            if ($lonhost) {    
                my @hosts = &Apache::lonnet::current_machine_ids();
                if (grep(/^\Q$lonhost\E$/,@hosts) && ($handle) && ($hostname)) {
                    if ($lonidsdir ne '') {
                        if (-e "$lonidsdir/$handle.id") {
                            my $protocol = $Apache::lonnet::protocol{$lonhost};
                            $protocol = 'http' if ($protocol ne 'https');
                            $url = $protocol.'://'.$hostname.'/adm/roles?state=doupdate';
                        }
                    }
                }
#
# otherwise point at default portal, or if non specified, at /adm/login?querystring where 
# querystring contains role=st./$cdom/$cnum
#
                if ($url eq '') {
                    my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
                    if ($domdefaults{'portal_def'}) {
                        $url = $domdefaults{'portal_def'};
                    } else {
                        my $chome = &Apache::lonnet::homeserver($cnum,$cdom);
                        my $hostname = &Apache::lonnet::hostname($chome);
                        my $protocol = $Apache::lonnet::protocol{$chome};
                        $protocol = 'http' if ($protocol ne 'https');
                        my $role = 'cc';
                        if ($crstype eq 'community') {
                            $role = 'co';
                        }
                        $url = $protocol.'://'.$hostname.'/adm/login?role='.$role.'./'.$cdom.'/'.$cnum;
                    }
                }
            }
        }
    }
    &unset_permissions(\%permissionflags);
    return ($url,$code);
}

sub set_permissions {
    my ($permissionflags,$permissions) = @_;
    foreach my $allowtype (@{$permissions}) {
        unless($env{"allowed.$allowtype"}) {
            $env{"allowed.$allowtype"} = 'F';
            $permissionflags->{$allowtype} = 1;
        }
    }
}

sub unset_permissions {
    my ($permissionflags) = @_;
    foreach my $allowtype (keys(%{$permissionflags})) {
        delete($env{"allowed.$allowtype"});
    }
}

sub set_dc_env {
    my ($dcname,$dcdom,$defdom,$ownername,$ownerdom,$crstype) = @_;
    $env{'user.name'} = $dcname;
    $env{'user.domain'} = $dcdom;
    $env{'user.home'} = &Apache::lonnet::homeserver($dcname,$dcdom);
    if ($defdom ne '') {
        $env{'request.role.domain'} = $defdom;
    }
    if (($dcname eq $ownername) && ($dcdom eq $ownerdom)) {
        $env{'environment.canrequest.'.$crstype} = 1;
    }
    return;
}

sub unset_dc_env {
    my ($dcname,$dcdom,$ownername,$ownerdom,$crstype) = @_;
    delete($env{'user.name'});
    delete($env{'user.domain'});
    delete($env{'user.home'});
    if ($env{'request.role.domain'}) {
        delete($env{'request.role.domain'});
    }
    if (($dcname eq $ownername) && ($dcdom eq $ownerdom)) {
        delete($env{'environment.canrequest.'.$crstype});
    }
    return;
}

=pod

=back

=cut

