#!/usr/local/bin/perl

## This file is profExportClasslistDatabase.pl
##

####################################################################
# Copyright @ 1995-2000 University of Rochester
# All Rights Reserved
####################################################################

use lib '/ww/webwork/gage_system/webwork/system/lib/'; # mainWeBWorKDirectory
use CGI qw(:standard);
use Global;
use Auth;
use strict;
use GDBM_File;

my $cgi = new CGI;
my %inputs = $cgi->Vars();

# get information from CGI inputs  (see also below for additional information)

my $Course 			= $inputs{'course'};
my $User 			= $inputs{'user'};
my $Session_key 	= $inputs{'key'};

# verify that information has been received
	unless($Course && $User && $Session_key) {
		&wwerror("$0","The script did not receive the proper input data.","","");
		}

# establish environment for this script

&Global::getCourseEnvironment($Course);


my $scriptsDirectory		= getWebworkScriptDirectory;
my $databaseDirectory		= getCourseDatabaseDirectory;
my $templateDirectory		= getCourseTemplateDirectory;
my $cgiURL					= getWebworkCgiURL;
my $CL_Database				= $Global::CL_Database;
my $path_to_CL_DB			= "${databaseDirectory}$CL_Database";
# File names

require "${scriptsDirectory}$Global::HTMLglue_pl";
require "${scriptsDirectory}$Global::DBglue_pl";
require "${scriptsDirectory}$Global::classlist_DBglue_pl";
require "${scriptsDirectory}$Global::FILE_pl";;
my $DELIM = $Global::delim;

# log access
	&Global::log_info('', query_string);

my $passwordFile = &Global::getCoursePasswordFile($Course);
my $permissionsFile = &Global::getCoursePermissionsFile($Course);
my $permissions = &get_permissions($inputs{'user'}, $permissionsFile);
my $keyFile = &Global::getCourseKeyFile($Course);

#verify session key
&verify_key($inputs{'user'}, $inputs{'key'}, $keyFile, $Course);

# verify permissions are correct
if ($permissions != $Global::instructor_permissions ) {
	print "permissions = $permissions instructor_permissions = $Global::instructor_permissions\n";
	print &html_NO_PERMISSION;
	exit(0);
}
# get the rest of the information from the submitted form

my $classlistFilename		= $inputs{'classList'};
my $update_firstName		= $inputs{'update_firstName'};
my $update_lastName			= $inputs{'update_lastName'};
my $update_status			= $inputs{'update_status'};
my $update_comment			= $inputs{'update_comment'};
my $update_section			= $inputs{'update_section'};
my $update_recitation		= $inputs{'update_recitation'};
my $update_email_address	= $inputs{'update_email_address'};
my $update_drop				= $inputs{'update_drop'};  ## either 'drop', 'leave', or 'remove'

$update_firstName		= 0 unless defined  $update_firstName;
$update_lastName		= 0 unless defined  $update_lastName;
$update_status			= 0 unless defined  $update_status;
$update_comment			= 0 unless defined  $update_comment;
$update_section			= 0 unless defined  $update_section;
$update_recitation		= 0 unless defined  $update_recitation;
$update_email_address	= 0 unless defined  $update_email_address;

my $CL_status = get_CL_database_status();
wwerror('Classlist Database is unlocked', 'You must go back and lock the classlist database
  before you can export it to an ascii file.') unless $CL_status eq 'locked';

wwerror('No classlist file selected', 'You must go back and select a classlist file.')
  unless $classlistFilename =~ /\w/;

my $msg1 = updateClasslistDB($classlistFilename);

my $msg2 = initial_passwords();

my $msg3 = "$msg1" . "$msg2";

uploadSuccess("$msg3");

exit;  ## end of main script

sub updateClasslistDB { ## builds the classlist DB and returns a message

	my ($classlistFilename) = @_;

	#get data from class list.
	my $fileName="${templateDirectory}$classlistFilename";  ## e.g. fileName=m161.lst

	my $message = "\nGetting classlist file from: $fileName <BR>\n";
	checkClasslistFile($Global::noOfFieldsInClasslist,$fileName);
	open(FILE, "$fileName") || wwerror($0, "Can't open $fileName");
	my @classList=<FILE>;
	close(FILE);

	###################################
	#  Before updating the database we back it up
	###################################
	if ( -e "$path_to_CL_DB" ) {
		$message .= "Backing up current classlist database to: ${path_to_CL_DB}_bak1 <BR>\n";
		&backup($path_to_CL_DB);
	}

	my %loginName_StudentID_Hash = %{getLoginName_StudentID_Hash()};
	my %studentID_LoginName_Hash =%{getStudentID_LoginName_Hash()};

#	my $WW_DB_exists = 0;
#	$WW_DB_exists = 1 if ( -e "${databaseDirectory}$Global::database" );

#	my %loginName_StudentID_Hash_from_WW_DB =();
#	my %studentID_LoginName_Hash_from_WW_DB =();

#	if ($WW_DB_exists) {
#		%loginName_StudentID_Hash_from_WW_DB =%{getLoginName_StudentID_Hash_from_WW_DB()};
#		%studentID_LoginName_Hash_from_WW_DB = reverse %loginName_StudentID_Hash_from_WW_DB;
#	}

	my $errors ='';
	my %new_good_classlist_students =();  	## students in new classlist without conflicts
	my %new_bad_classlist_students =();		## students in new classlist with conflicts


	foreach (@classList)     {					## read through classlist and create
												## class list database
		unless ($_ =~ /\S/)  {next;}			## skip blank lines
		chomp;
		my @classListRecord=&getRecord($_);
		my ($studentID, $lastName, $firstName, $status, $comment,  $section, $recitation, $email_address, $login_name)
			  =  @classListRecord;

		## First we get a list of any conflicts with current students
		if ((defined $loginName_StudentID_Hash{$login_name})
		  and ($loginName_StudentID_Hash{$login_name} ne $studentID)) {
			$errors .= "$firstName $lastName, $login_name, $studentID <BR>\n ";
			$new_bad_classlist_students{$login_name} =1;
			next;
		}

		if ((defined $studentID_LoginName_Hash{$studentID})
		  and ($studentID_LoginName_Hash{$studentID} ne $login_name)) {
			$errors .= "$firstName $lastName, $login_name, $studentID <BR>\n ";
			$new_bad_classlist_students{$login_name} =1;
			next;
		}



		## OK, the student record has no conflicts

		$new_good_classlist_students{$login_name} =1;

		## Handle students already in classlist DB
		if (defined $loginName_StudentID_Hash{$login_name}) {
			&attachCLRecord($login_name);
			&CL_putStudentLastName     ($lastName, $login_name)       if $update_firstName;
			&CL_putStudentFirstName    ($firstName, $login_name)      if $update_lastName;
			&CL_putStudentStatus       ($status, $login_name)         if $update_status;
			&CL_putComment             ($comment, $login_name)        if $update_comment;
			&CL_putClassSection        ($section,$login_name)         if $update_section;
			&CL_putClassRecitation     ($recitation,$login_name)      if $update_recitation;
			&CL_putStudentEmailAddress ($email_address, $login_name)  if $update_email_address;
			$Global::over_ride_CLBD_lock = 1;
			&saveCLRecord($login_name);
			$Global::over_ride_CLBD_lock = 0;


		}
		else {  			## Handle new students
			&CL_putStudentID           ($studentID, $login_name);
			&CL_putStudentLastName     ($lastName, $login_name);
			&CL_putStudentFirstName    ($firstName, $login_name);
			&CL_putStudentStatus       ($status, $login_name);
			&CL_putComment             ($comment, $login_name);
			&CL_putClassSection        ($section,$login_name);
			&CL_putClassRecitation     ($recitation,$login_name);
			&CL_putStudentEmailAddress ($email_address, $login_name);
			$Global::over_ride_CLBD_lock = 1;
			&saveCLRecord($login_name);
			$Global::over_ride_CLBD_lock = 0;
		}
	}

	## Now we take care of students who are in the current classlist database but are not in
	## the classlist file.

	my %drop_list =();
	my $login_name;
	%loginName_StudentID_Hash = %{getLoginName_StudentID_Hash()};
	foreach $login_name (keys %loginName_StudentID_Hash) {
		$drop_list{$login_name} = 1 unless (
		  (defined ($new_good_classlist_students{$login_name})) or (defined ($new_bad_classlist_students{$login_name}))
		);
	}
	if  ($update_drop eq 'drop')	{
		my $status = 'D';
		$status = $Global::statusDrop[0] if defined $Global::statusDrop[0];
		foreach $login_name (keys %drop_list) {
			&attachCLRecord($login_name);
			&CL_putStudentStatus($status, $login_name);
			$Global::over_ride_CLBD_lock = 1;
			&saveCLRecord($login_name);
			$Global::over_ride_CLBD_lock = 0;
		}
	}
	elsif ($update_drop eq 'remove') {
		foreach $login_name (keys %drop_list) {
			$Global::over_ride_CLBD_lock = 1;
			deleteClassListRecord($login_name);
			$Global::over_ride_CLBD_lock = 0;
		}
	}
	else {  ## if this case $update_drop eq 'leave' and we do nothing
	}

	unlock_CL_database();
	if ($errors) {
		$message .= '<BR>The following students HAVE NOT BEEN ENTERED IN THE CLASSLIST DATABASE
		because of a conflict with entries in the WeBWorK problem set database or the classlist database.
		These students have a studentID or a loginName that conflicts with a current student.
		Enter this information again from the Add Student(s) Page to get a more detailed error message
		and instructions on how to correct the problem.<BR><BR>';
		$message .= "\n $errors<BR>";
	}
	$message;
}

sub initial_passwords {
	my %studentsinclass=();
	my @classListRecord=();
	my $msg ='';

	# Check that the files exist:
	# 	 The permissions file must exist and have both read and write privilages.
	# 	 The password file must exist and have both read and write privilages.


	unless ( -r $passwordFile and -w $passwordFile) {
		wwerror ($0, "Permissions set incorrectly on $passwordFile or its directory.
			 Cannot access file to both read and write.");
	}

	unless ( -r $permissionsFile and -w $permissionsFile) {
		wwerror ($0, "Permissions set incorrectly on $permissionsFile or its directory.
			 Cannot access file to both read and write.");
	}

	my $login_name;

	my @classList = @{getAllLoginNames()};

	$msg .= "\n<BR><BR> Modifying the password file :\n   $passwordFile <BR>\n ";

	foreach $login_name (@classList)   {			## read through classlist database and create
													## passwords for all active students
													## except if passwords already exist for student
		attachCLRecord($login_name);

		my $status		= CL_getStudentStatus($login_name);
		my $studentID	= CL_getStudentID($login_name);

		$studentsinclass{$login_name}++ unless(&dropStatus($status));

		if(&dropStatus($status)) {
			$msg .= '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'."$login_name not added because status is $status <BR>\n ";
		}
		elsif (&get_password($login_name, $passwordFile)) {
			$msg .= '&nbsp;&nbsp;&nbsp;'."$login_name not added because password already exists <BR>\n ";
		}
		else {
			&new_password($login_name, $studentID, $passwordFile);
			&put_permissions(0,$login_name,$permissionsFile);
			$msg .= "added: $login_name, $studentID <BR>\n ";
		}
	}

	my @pwStudents = &get_keys_from_db($passwordFile);
	my ($ans,$student);


	$msg .= "\n<BR<BR><BR> The following login's (if any) in the password and permissions databases are either\n ";
	$msg .= "(1) not listed  in the new class list database file \n";
	$msg .= "or (2) have DROP status in the new class list database file.\n";
	$msg .= "They will all be removed from the password and permissions databases.<BR><BR>\n ";

	foreach $student (@pwStudents) {
		next if defined($studentsinclass{$student});

		&delete_password($student,$passwordFile);
		&delete_permissions($student,$permissionsFile);
		$msg .= "$student<BR>\n ";
	}

#	## if the owner of the password file is running this script (e.g. when the password file is first created)
#	## set the permissions correctly
#
#	open (PASSWORDFILE, "$passwordFile") or wwerror($0, "Can't open $passwordFile");
#	my @stat = stat PASSWORDFILE;
#	close PASSWORDFILE;
#
#	if ($< == $stat[4]) {
#
#		chmod($Global::password_permission, $passwordFile) or
#								 wwerror($0, "Can't do chmod($Global::password_permission, $passwordFile)");
#		chown(-1,$Global::numericalGroupID,$passwordFile)  or
#								 wwerror($0, "Can't do chown(-1,$Global::numericalGroupID,$passwordFile)");
#	}
#
#	open (PERMISSIONSFILE, "$permissionsFile") or wwerror($0, "Can't open $permissionsFile");
#	@stat = stat PERMISSIONSFILE;
#	close PERMISSIONSFILE;
#
#	if ($< == $stat[4]) {
#
#		chmod($Global::permissions_permission, $permissionsFile) or
#								wwerror($0, "Can't do chmod($Global::permissions_permission, $permissionsFile)");
#		chown(-1,$Global::numericalGroupID,$permissionsFile)  or
#								wwerror($0, "Can't do chown(-1,$Global::numericalGroupID,$permissionsFile)");
#	}
$msg;
}


sub uploadSuccess {
	my ($msg) = @_;
	print"content-type: text/html\n\n<H2>Success, the classlist database has been updated. </H2>\n";
	print $msg;
	print &htmlBOTTOM("profImportClasslistDatabase.pl", \%inputs);
}

sub backup  {
	## takes as a parameter the full path name
	## makes upto two backups of the file with _bak1, or _bak2
	## appended to filename where _bak1 is the most recent backup
	use File::Copy;
	my $fileName =$_[0];

	if (-e "${fileName}_bak1") {
		rename("${fileName}_bak1","${fileName}_bak2") or
		  &wwerror("$0","can't rename ${fileName}_bak1");
	}

	if (-e "${fileName}") {
		copy("${fileName}","${fileName}_bak1") or
		  &wwerror("$0","can't copy ${fileName}");
	}
}

