#!/usr/bin/perl -w -I/opt/eprints3/perl_lib

######################################################################
#
#  This file is part of GNU EPrints 2.
#  
#  Copyright (c) 2000-2004 University of Southampton, UK. SO17 1BJ.
#  
#  EPrints 2 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.
#  
#  EPrints 2 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 EPrints 2; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
######################################################################

=pod

=head1 NAME

B<epadmin> - EPrints repository admin tool

=head1 SYNOPSIS

=over 8

=item B<epadmin> create

START HERE! This option will walk you through the tasks needed to create your repository.

=item B<epadmin> test I<repository_id>

A null operation which just checks your configuration files are OK and that you can connect to the database.

=item B<epadmin> config_core I<repository_id>

Set hostname, contact email and repository name. 

=item B<epadmin> config_db I<repository_id>

Set database connection properties and, optionally, to create database and database user.

=item B<epadmin> create_db I<repository_id>

Create a database and database user with the current settings.

=item B<epadmin> create_tables I<repository_id>

Create the database tables.

=item B<epadmin> create_user I<repository_id>

Create a new user. You need to do this to create your first admin account.

=item B<epadmin> erase_fulltext_index I<repository_id>

This erases all the .words and .indexcodes cache files from your repository, forcing the indexer to rerun the tools used to extract full text from your documents.

This is useful if you only setup the fulltext indexing after your repository is already live, or if you discover there has been a problem.

=item B<epadmin> recommit I<repository_id> I<dataset_id>

Recommit all the records in the given dataset. What this does is cause the automatic values to be re-calculated.

=item B<epadmin> reindex I<repository_id> I<dataset_id>

Schedule the dataset for reindexing. The indexer will do the actual indexing and it may take some time. This only schedules the reindexing.

=item B<epadmin> rehash I<repository_id> [I<document_id>]

Recalculate the hashes of the files in this document and write it to a probity log file. If a document id is given then just generate the hash for that document.

=item B<epadmin> reload I<repository_id>

Cause the web server to reload the repository configuration.

=item B<epadmin> redo_thumbnails I<repository_id> 

Regenerate all the thumbnail and image-preview files.

=item B<epadmin> erase_data I<repository_id>

Erases and recreates the database. Removes all documents and files. Does not touch the configuration files.

=item B<epadmin> erase_eprints I<repository_id>

Erases all the documents and eprints (including their files). Recreates the eprint and document tables. Leaves configuration files and the users and subjects tables alone.

=item B<epadmin> upgrade I<repository_id>

After upgrading EPrints, use this to update the database tables. It will advise any other tasks that are required.

=item B<epadmin> --help

=back

=head1 OPTIONS

=over 8

=item B<--help>

Print a brief help message and exit.

=item B<--man>

Print the full manual page and then exit.

=item B<--quiet>

This option does not do anything.

=item B<--verbose>

Explain in detail what is going on. May be repeated for greater effect.

=item B<--version>

Output version information and exit.

=back   

=head1 AUTHOR

This is part of this EPrints 3 system. EPrints 3 is developed by Christopher Gutteridge.

=head1 VERSION

EPrints Version: 3.0

=head1 CONTACT

For more information goto B<http://www.eprints.org/> which give information on mailing lists and the like.

Chris Gutteridge may be contacted at B<support@eprints.org>

Should you need a real world address for some reason, EPrints can be contacted in the real world at

 EPrints c/o Christopher Gutteridge
 Department of Electronics and Computer Science
 University of Southampton
 SO17 1BJ
 United Kingdom

=head1 COPYRIGHT

This file is part of GNU EPrints 2.

Copyright (c) 2000-2004 University of Southampton, UK. SO17 1BJ.

EPrints 2 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.

EPrints 2 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 EPrints 2; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


=cut

#cjg Does not use noise levels


use EPrints;

use Sys::Hostname;
use DBI;
use Unicode::String qw(utf8 latin1);
use Data::Dumper;

use strict;
use Getopt::Long;
use Pod::Usage;

my $verbose = 0;
my $quiet = 0;
my $help = 0;
my $man = 0;
my $version = 0;

GetOptions( 
	'help|?' => \$help,
	'man' => \$man,
	'version' => \$version,
	'verbose+' => \$verbose,
	'silent' => \$quiet,
	'quiet' => \$quiet
) || pod2usage( 2 );
EPrints::Utils::cmd_version( "epadmin" ) if $version;
pod2usage( 1 ) if $help;
pod2usage( -exitstatus => 0, -verbose => 2 ) if $man;
pod2usage( 2 ) if( scalar @ARGV == 0 ); 

# Set STDOUT to auto flush (without needing a \n)
$|=1;

my $noise = 1;
$noise = 0 if( $quiet );
$noise = 1+$verbose if( $verbose );

my $REGEXP_HOSTNAME_MIDDLE = '[a-z0-9-]+(\.[a-z0-9-]+)*';
my $REGEXP_HOSTNAME = '^'.$REGEXP_HOSTNAME_MIDDLE.'$';
my $REGEXP_EMAIL = '^[^@]+@'.$REGEXP_HOSTNAME_MIDDLE.'$';
my $REGEXP_HOSTNAME_FULL = '^[a-z0-9-]+(\.[a-z0-9-]+)+$';
my $REGEXP_VARNAME = '^[a-zA-Z][_A-Za-z0-9]*$';
my $REGEXP_NUMBER = '^[0-9]+$';
my $REGEXP_YESNO = '^(yes|no)$';
my $REGEXP_ANY = '^.*$';

my $action = $ARGV[0];
if( $action eq "create" ) { create(); }
else
{
	my $repoid = $ARGV[1];
	pod2usage(1) unless defined $repoid;
	if( $action eq "config_core" ) { config_core( $repoid ); }
	elsif( $action eq "config_db" ) { config_db( $repoid ); }
	elsif( $action eq "create_db" ) { create_db( $repoid ); }
	elsif( $action eq "create_user" ) { create_user( $repoid ); }
	elsif( $action eq "create_tables" ) { create_tables( $repoid ); }
	elsif( $action eq "erase_data" ) { erase_data( $repoid ); }
	elsif( $action eq "erase_eprints" ) { erase_eprints( $repoid ); }
	elsif( $action eq "erase_fulltext_index" ) { erase_fulltext_index( $repoid ); }
	elsif( $action eq "reload" ) { reload( $repoid ); }
	elsif( $action eq "redo_thumbnails" ) { redo_thumbnails( $repoid ); }
	elsif( $action eq "upgrade" ) { upgrade( $repoid ); }
	elsif( $action eq "test" ) { test( $repoid ); }
	elsif( $action eq "recommit" ) 
	{
		my $datasetid = $ARGV[2];
		pod2usage(1) unless defined $datasetid;
		recommit( $repoid, $datasetid ); 
	}
	elsif( $action eq "reindex" ) 
	{
		my $datasetid = $ARGV[2];
		pod2usage(1) unless defined $datasetid;
		reindex( $repoid, $datasetid ); 
	}
	elsif( $action eq "rehash" ) { rehash( $repoid ); }
	else { pod2usage( 1 ); }
}


exit;



sub create
{
	pod2usage( 2 ) if( scalar @ARGV != 1 ); 

	print <<END;

Create an EPrint Repository

Please select an ID for the repository, which will be used to create a directory
and identify the repository. Lower case letters and numbers, may not start with
a number. examples: "lemurprints" or "test3"

END
	if( scalar EPrints::Config::get_repository_ids() )
	{
		print "Existing repositories:\n";
		print join( ", ", EPrints::Config::get_repository_ids() )."\n\n";
	}

	my $repoid = EPrints::Utils::get_input( $REGEXP_VARNAME, 'Archive ID' );

	my $repodir = EPrints::Config::get( "base_path" )."/archives/".$repoid;

	my $loaded_config = EPrints::Config::get_repository_config( $repoid );		
	my $exists = ( defined $loaded_config );

	if( $exists )
	{
		print "A repository with that ID already exist.\n";
		exit;	
	}

	unless( -e $repodir )
	{
		print "We need to create $repodir, doing it now...\n";
		unless( EPrints::Platform::mkdir( $repodir ) )
		{
			print "Problem creating directory\n\n";
			exit;
		}
	}
	unless( -d $repodir )
	{	
		print "$repodir MUST be a directory.\n\n";
	}

	my $username = EPrints::Config::get( "user" );
	print "Getting uid and gid information for $username\n";
	my(undef,undef,$uid,$gid) = EPrints::Platform::getpwnam( $username );
	print "UID: $uid\n";
	print "GID: $gid\n";
	print "\nCreating initial files:\n";
	&install( 
		EPrints::Config::get( "base_path" )."/lib/defaultcfg",
		$EPrints::SystemSettings::conf->{"file_perms"},
		$uid,
		$gid,
		$repodir."/cfg",
		$repoid );

	foreach( "var", "html", "documents", "documents/disk0" )
	{
		my $dir = $repodir."/".$_;
		EPrints::Platform::mkdir( $dir );
		EPrints::Platform::chown( $uid, $gid, $dir );
	}
	print <<END;

Ok. I've created the initial config files and directory structure. 
I've also created a "disk0" directory under documents/ if you want
your full texts to be stored on a different partition then remove 
the disk0, and create a symbolic link to the directory you wish to
store the full texts in. Additional links may be placed here to be
used when the first is full.

END
	print "\n";
	my $config_core = EPrints::Utils::get_input( $REGEXP_YESNO, "Configure vital settings?", "yes" );
	if( $config_core eq "yes" )
	{
		config_core( $repoid );	
	}
	else
	{
		print "OK, but you'll need to edit 10_core.pl by hand in that case.\n";
	}

	print "\n";
	my $config_db = EPrints::Utils::get_input( $REGEXP_YESNO, "Configure database?", "yes" );
	if( $config_db eq "yes" )
	{
		config_db( $repoid );	
	}
	else
	{
		print "OK, but you'll need to edit database.pl by hand in that case, and make sure the database exists.\n";
	}


	print "\n";
	my $create_user = EPrints::Utils::get_input( $REGEXP_YESNO, "Create an initial user?", "yes" );
	if( $create_user eq "yes" )
	{
		create_user( $repoid );	
	}
	else
	{
		print "OK, but you will not be able to log into the website. You can always run 'epadmin create_user $repoid' later.\n"
	}
# cjg: Register with website!

	my $ok;
	$ok = EPrints::Utils::get_input( $REGEXP_YESNO, "Do you want to build the static web pages?", "yes" );
	if( $ok eq "yes" )
	{
		run_script( $repoid, "generate_static", "--verbose", $repoid );	
	}

	$ok = EPrints::Utils::get_input( $REGEXP_YESNO, "Do you want to import the LOC subjects?", "yes" );
	if( $ok eq "yes" )
	{
		run_script( $repoid, "import_subjects", "--verbose", "--force", $repoid );	
	}

	$ok = EPrints::Utils::get_input( $REGEXP_YESNO, "Do you want to update the apache config files? (you still need to add the 'Include' line)", "yes" );
	if( $ok eq "yes" )
	{
		run_script( $repoid, "generate_apacheconf", "--verbose" );
	}

	my $base_path = EPrints::Config::get( "base_path" );
print <<END;

--------------------------------------------------------------------------
That seemed to more or less work...
--------------------------------------------------------------------------

Now make any required changes to the cfg files. 

Note that changing the metadata configuration may require the database
tables to be regenerated. epadmin erase_data will regenerate the 
eprints and documents tables only. erase_data will regenerate everything.
(nb. these also do erase the contents of the tables, and any uploaded 
files).

Make sure that your main apache config file contains the line:

 Include $base_path/cfg/apache.conf

Then stop and start your webserver:
Often:
 /etc/rc.d/init.d/httpd stop
 /etc/rc.d/init.d/httpd start
(or maybe /usr/local/apache/bin/apachectl stop & start)

And then try connecting to your repository.
--------------------------------------------------------------------------

Don't forget to register your repository at http://roar.eprints.org/

END

exit;

}

	
# don't be fooled. This isn't the same as the install() in
# eprints-install
sub install
{
	my($dir, $perms, $user, $group, $dest, $repoid) = @_;

	print "Installing: $dest\n";
	opendir(INDIR, $dir) or die("Unable to install directory: $dir");
	my @dirs = ();
	my @files = ();
	EPrints::Platform::mkdir( $dest );
	EPrints::Platform::chown($user, $group, $dest);
	while(my $item = readdir(INDIR))
	{
		next if $item=~m/^\./;
		if (-d "$dir/$item") { push(@dirs, $item); }
		else { push(@files, $item); }
	}
	closedir(INDIR);
	foreach my $filename (@files)
	{
		if( $filename eq "Config.pm" )
		{
			open(my $i_fh, "<", "$dir/$filename");
			open(my $o_fh, ">", "$dest/$filename")
				or die "Can't write to $dest/$filename";
			while(defined($_ = <$i_fh>))
			{
				if( /^package/ )
				{
					# Change the package name to the local repository ($/ = line seperator)
					$_ = "package EPrints::Config::$repoid;$/";
				}
				print $o_fh $_;
			}
			close($i_fh);
			close($o_fh);
		}
		else
		{
			EPrints::Utils::copy("$dir/$filename", "$dest/$filename");
		}
		EPrints::Platform::chmod($perms, "$dest/$filename");
		EPrints::Platform::chown($user, $group, "$dest/$filename");
	}
	foreach(@dirs)
	{
		install("$dir/".$_, $perms, $user, $group, "$dest/$_", $repoid );
	}
}

sub run_script
{
	my( $repoid, $script, @opts ) = @_;

	my $repo = EPrints::Repository->new( $repoid );
	my $bin = $repo->get_conf( "bin_path" );

	system( "$bin/$script", @opts );
}

sub config_core
{
	my( $repoid ) = @_;

	print "Core configuration for $repoid\n\n";

	my %config = ();

	$config{port} = 80;
	$config{host} = undef;
	$config{urlpath} = '/';
	$config{archiveroot} = "archives/".$repoid;
	$config{configmodule} = "cfg/Config.pm";
	$config{aliases} = [];
	$config{securehost} = undef;
	$config{securepath} = undef;

	$config{adminemail} = undef;

	$config{archive_name} = "Test Repository";

print <<END;

Please enter the fully qualified hostname of the repository. 

For a production system we recommend against using the real hostname of the 
machine. 

Example: $repoid.footle.ac.uk

END

	$config{host} = EPrints::Utils::get_input( $REGEXP_HOSTNAME_FULL, 'Hostname', $config{host} );

print <<END;

Please enter the port of the webserver. This is probably 80, but you may wish 
to run apache on a different port if you are experimenting.

END

	$config{port} = EPrints::Utils::get_input( $REGEXP_NUMBER, 'Webserver Port', $config{port} );


	# calculate example aliases
	my $realhostname = hostname();
	if( $realhostname !~ m/\./ )
	{
		# No dots in the actual hostname! Lets try and got the
		# domain from resolv.conf
		my $domain = "";
		if( open( RESOLV, "/etc/resolv.conf" ) && 0)
		{
			while( <RESOLV> )
			{
				if( m/^search\s+([^\s]+)/ )
				{
					$domain = $1;	
					last;
				}
			}
			close RESOLV;
		}
		$domain = "mydomain.com" if( $domain eq "" );
		$realhostname.=".".$domain;
	}
	my @example_aliases = ();
	push @example_aliases,$realhostname;
	
	$realhostname=~m/^(([^\.]*)\.[^\.]*)(\.|$)?/;
	push @example_aliases,$1 if( $3 eq ".");
	push @example_aliases,$2;
	
	$config{host}=~m/^(([^\.]*)\.[^\.]*)(\.|$)?/;
	push @example_aliases,$1 if( $3 eq "." );
	push @example_aliases,$2;
	print <<END;
	
Please enter all the aliases which could reach the repository, and indicate if 
you would like EPrints to write a Redirect Rule to redirect requests to this
alias to the correct URL.
END
	if( scalar @{$config{aliases}}==0 )
	{
		print "Some suggestions:\n";
		foreach( @example_aliases )
		{
			print $_."\n";
		}
	}
	print <<END;
	
Enter a single hash (#) when you're done.

END

	my @aliases = @{$config{aliases}};
	$config{aliases} = [];

	for(;;)
	{
		my $default = shift @aliases;
		my $alias = EPrints::Utils::get_input( '^('.$REGEXP_HOSTNAME_MIDDLE.'|#)$', 'Alias (enter # when done)',
			(defined $default ? $default->{name} : '#' ) );
		last if( $alias eq "#" );
		my $aliasrecord = {};
		$aliasrecord->{name} = $alias;
		$aliasrecord->{redirect} = 
			EPrints::Utils::get_input( 
				$REGEXP_YESNO,
				"Redirect $alias to $config{host}",
				(defined $default && !$default->{redirect} ? 'no' : 'yes' ) );
		push @{$config{aliases}},$aliasrecord;
		print "\n";
	}

#print <<END;
#
#Language Configuration
#
#Please enter the primary language and other supported languages for the 
#repository. Supporting other languages represents a serious commitment to 
#translate all the phrases and templates etc. into each of these other 
#languages.
#
#Available languages: (please use the ID to refer to them)
#END
#my @langs = EPrints::Config::get_supported_languages();
#foreach( @langs )
#{
#	my $title = utf8("");
#	$title->utf8( "".EPrints::Config::lang_title( $_ ) );
#	print $_." - ".($title->latin1)."\n";
#}
#print <<END;
#
#If you plan to add support for another language, you will need to edit the 
#languages.xml file to indicate that this language is supported, and create 
#the relevant phrase and template files. 
#
#END

	print "\n";
	print "\n";

	$config{adminemail} = EPrints::Utils::get_input( $REGEXP_EMAIL, 'Administrator Email',  $config{adminemail} );
	print <<END;

Enter the name of the repository in the default language. If you wish to enter 
other titles for other languages or enter non ascii characters then you may
enter something as a placeholder and edit the XML config file which this
script generates.

END
	$config{archive_name} = EPrints::Utils::get_input( '^.+$', 'Archive Name',  $config{archive_name} );

	# Write files?

	print "\n";
	my $config_core = EPrints::Utils::get_input( $REGEXP_YESNO, "Write these core settings?", "yes" );
	if( $config_core eq "no" )
	{
		print "\nOK. Not writing after all.\n";
		return;
	}

	# Write files!

	my $repodir = EPrints::Config::get( "base_path" )."/archives/".$repoid;

	my $aemailfile = "$repodir/cfg/cfg.d/adminemail.pl";
	open( AEMAIL, ">$aemailfile" ) || die "Could not write to $aemailfile: $!";
	print AEMAIL Data::Dumper->Dump(
		[
			$config{adminemail}, 
		],
		[qw/
			$c->{adminemail} 
		/]
	);
	close AEMAIL;
	print "Wrote $aemailfile\n";

	my $corefile = "$repodir/cfg/cfg.d/10_core.pl";
	open( CORE, ">$corefile" ) || die "Could not write to $corefile: $!";
	print CORE Data::Dumper->Dump(
		[
			$config{host}, 
			$config{port},
			$config{aliases},
			$config{securehost},
			$config{secureport},
		],
		[qw/
			$c->{host} 
			$c->{port} 
			$c->{aliases} 
			$c->{securehost} 
			$c->{securepath}
		/]
	);
	close CORE;
	print "Wrote $corefile\n";

	my $anamefile = "$repodir/cfg/lang/en/phrases/archive_name.xml";
	open( ANAME, ">$anamefile" ) || die "Could not write to $anamefile: $!";
	print ANAME <<END;
<?xml version="1.0" encoding="iso-8859-1" standalone="no" ?>
<!DOCTYPE phrases SYSTEM "entities.dtd">

<epp:phrases xmlns="http://www.w3.org/1999/xhtml"
            xmlns:epp="http://eprints.org/ep3/phrase">

    <epp:phrase id="archive_name">$config{archive_name}</epp:phrase>

</epp:phrases>
END
	close( ANAME );
	print "Wrote $anamefile\n";
}

sub config_db
{
	my( $repoid ) = @_;

	my %config = ();
	$config{dbname} = $repoid;
	$config{dbhost} = "localhost";
	$config{dbport} = undef;
	$config{dbsock} = undef;
	$config{dbuser} = $repoid;
	$config{dbpass} = undef;

	print "\nConfiguring Database for: $repoid\n";

	$config{dbname} = EPrints::Utils::get_input( $REGEXP_VARNAME, 'Database Name',  $config{dbname} );
	$config{dbhost} = EPrints::Utils::get_input( $REGEXP_HOSTNAME, 'MySQL Host',  $config{dbhost} );

	print "\nYou probably don't need to set socket and port (unless you do!?).\n";
	$config{dbport} = "#" if( !defined $config{dbport} );
	$config{dbport} = EPrints::Utils::get_input( '^[0-9]+|#$', 'MySQL Port (# for no setting)',  $config{dbport} );
	$config{dbport} = undef if( $config{dbport} eq "#" );

	$config{dbsock} = "#" if( !defined $config{dbsock} );
	# can't remember what is a legal mysql socket... cjg
	$config{dbsock} = EPrints::Utils::get_input( '^.*$', 'MySQL Socket (# for no setting)',  $config{dbsock} );
	$config{dbsock} = undef if( $config{dbsock} eq "#" );

	$config{dbuser} = EPrints::Utils::get_input( $REGEXP_VARNAME, 'Database User',  $config{dbuser} );
	$config{dbpass} = EPrints::Utils::get_input_hidden( $REGEXP_VARNAME, 'Database Password',  $config{dbpass} );

	print "\n";
	my $config_db = EPrints::Utils::get_input( $REGEXP_YESNO, "Write these database settings?", "yes" );
	if( $config_db eq "no" )
	{
		print "\nOK. Not writing after all.\n";
		return;
	}

	my $repodir = EPrints::Config::get( "base_path" )."/archives/".$repoid;
	my $dbfile = "$repodir/cfg/cfg.d/database.pl";
	open( DBCONF, ">$dbfile" ) || die "Could not write to $dbfile: $!";
	print DBCONF Data::Dumper->Dump(
		[
			$config{dbname}, 
			$config{dbhost},
			$config{dbport},
			$config{dbsock},
			$config{dbuser},
			$config{dbpass},
		],
		[qw/
			$c->{dbname} 
			$c->{dbhost}
			$c->{dbport}
			$c->{dbsock}
			$c->{dbuser}
			$c->{dbpass}
		/]
	);
	close DBCONF;
	print "Wrote $dbfile\n";

	print <<END;

EPrints can create the database, and grant the correct permissions.
	
END
	my $makedb = EPrints::Utils::get_input( $REGEXP_YESNO, "Create database \"$config{dbname}\"", "yes" );
	if( $makedb eq "yes" )
	{
		create_db( $repoid );
	}
	else
	{
		print "\nWell, OK. But you'll need to do it yourself then.\n";
	}

}

my $mysql_root_password;
# subroutine so that it can cache if we do several operations
sub get_mysql_root_password
{
	return $mysql_root_password if( defined $mysql_root_password );

	#cjg hide password from display?
	print <<END;

Ok, I'll need to connect to the mysql database as root. What is the root 
password? 
	
END
	$mysql_root_password = EPrints::Utils::get_input_hidden( '^.*$', "MySQL Root Password" );

	return $mysql_root_password;
}

sub root_dbh
{
	my( $repoid, $dbname ) = @_;

	my $config = EPrints::Config::load_repository_config_module( $repoid );

	$dbname = $config->{dbname} unless defined $dbname;

	my $mysqlrootpassword = get_mysql_root_password();
	print "Connecting to the database...\n";
	my $dbh = DBI->connect(
		EPrints::Database::build_connection_string(
			dbname=>$dbname,
			dbsock=>$config->{dbsock},
			dbport=>$config->{dbport},
			dbhost=>$config->{dbhost} ),
		"root",
		$mysqlrootpassword );
	return $dbh;
}

sub create_db
{
	my( $repoid ) = @_;

	my $dbh = root_dbh( $repoid, "mysql" );

	if( !defined $dbh )
	{
		print <<END;

Hmmm. Problem connecting to database as root. We'll skip this but
you should create it by hand.

END
		exit 1;
	}

	my $mversion = EPrints::Database::mysql_version_from_dbh( $dbh );
	my $oldpasswords = 0;
	if( $mversion >= 40100 ) 
	{
		print "MySQL version id $mversion >= 40100\n";
		print "MYSQL OLD PASSWORDS will be used for compatibility with DBI::mysql\n";
		$oldpasswords = 1;
	} 

	my $config = EPrints::Config::load_repository_config_module( $repoid );

	my $sth = $dbh->prepare( "show databases" );
	$sth->execute;
	my $dbexists = 0;
	my @row;
	while( @row = $sth->fetchrow_array )
	{
		$dbexists = 1 if $row[0] eq $config->{dbname};
	}
	if( $dbexists )
	{
		print "Hmm. A database called ".$config->{dbname}." already exists, oh well.\n\n";
		return;
	}
	else
	{
		my $SQL = "CREATE DATABASE ".$config->{dbname};
		#print "DOING: $SQL\n";
		$dbh->do( $SQL );
	}
	print "Setting MySQL privs\n";
	#cjg @localhost ??? what about remote mysql's?

	my $SQL;
	$SQL = 'GRANT ALL ON '.$config->{dbname}.'.* TO '.$config->{dbuser}.'@localhost';
	#print "DOING: $SQL\n";
	$dbh->do( $SQL );
	my $pass = '"'.$config->{dbpass}.'"';
	if( $oldpasswords ) 
	{
		$pass = "OLD_PASSWORD($pass)";
	} else {
		$pass = "PASSWORD($pass)";
	}
	$SQL = 'SET PASSWORD FOR '.$config->{dbuser}.'@localhost = '.$pass;
	#print "DOING: $SQL\n";
	$dbh->do( $SQL );

	print "Disconnecting from database.\n\n";
	$dbh->disconnect;

	my $mktables = EPrints::Utils::get_input( $REGEXP_YESNO, "Create database tables?", "yes" );
	if( $mktables eq "yes" )
	{
		create_tables( $repoid );
	}

}
 

sub create_user
{
	my( $repoid ) = @_;

	my $session = EPrints::Session->new( 1 , $repoid, $noise );
	exit unless( defined $session );

	my $user_info = {};
	print "Creating a new user in $repoid\n\n";
	$user_info->{username} = EPrints::Utils::get_input( $REGEXP_VARNAME, 'Enter a username', 'admin' );
	while( defined EPrints::DataObj::User::user_with_username( $session, $user_info->{username} ) )
	{
		print STDERR "User with username '".$user_info->{username}."' already exists.\n";
		$user_info->{username} = EPrints::Utils::get_input( $REGEXP_VARNAME, 'Enter a username', 'admin' );
	}
	my @utypes = $session->get_repository->get_types( "user" );
	$user_info->{usertype} = EPrints::Utils::get_input( '^('.join( '|', @utypes ).')$', 'Select a user type ('.join( "|",@utypes).')', 'admin' );
	my $rawpassword = EPrints::Utils::get_input_hidden( $REGEXP_VARNAME, 'Enter Password' );
	$user_info->{password} = EPrints::Utils::crypt_password( $rawpassword, $session );
	$user_info->{email} = EPrints::Utils::get_input( $REGEXP_EMAIL, 'Email' );
	
	my $user_ds = $session->get_repository->get_dataset( "user" );
	my $new_user = $user_ds->create_object( $session, $user_info );

	print "\n";

	if( defined $new_user )
	{
		if( $noise >= 1 )
		{
			print "Successfully created new user:\n";
			print "       ID: ".$new_user->get_value( "userid" )."\n";
		}
		if( $noise >= 2 )
		{
			print " Username: ".$new_user->get_value( "username" )."\n";
			print "     Type: ".$new_user->get_value( "usertype" )."\n";
		}
	}
	else
	{
		my $db_error = $session->get_database->error;
		print STDERR "Error creating user: $db_error\n";
	}
	$session->terminate;
}


sub redo_thumbnails
{
	my( $repoid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise );
	exit( 1 ) unless( defined $session );
	
	my $doc_ds = $session->get_repository->get_dataset( "document" );
	$doc_ds->map( 
		$session,
		sub 
		{
			my( $session , $dataset , $doc ) = @_;

			if( $noise >= 2 )
			{
				print "Attempting to build thumbnails for document #".$doc->get_id."\n";
			}

			$doc->remove_thumbnails; #ouch!
			$doc->make_thumbnails;
		} );

	$session->terminate;
}	



sub reload
{
	my( $repoid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise );
	exit( 1 ) unless( defined $session );
	
	my $file = $session->get_repository->get_conf( "variables_path" )."/last_changed.timestamp";
	unless( open( CHANGEDFILE, ">$file" ) )
	{
		EPrints::abort( "Cannot write to file $file" );
	}
	print CHANGEDFILE "This file last poked at: ".EPrints::Time::human_time()."\n";
	close CHANGEDFILE;
	if( $noise > 0 )
	{
		print <<END;
The repository config will be reloaded, but you should still restart apache as
soon as possible.

END
	}
	$session->terminate;
}	


sub create_tables
{
	my( $repoid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise, 1 );
	exit( 1 ) unless( defined $session );

	if( $session->get_database->has_table( "eprint" ) )
	{
		print "WARNING: Database is NOT empty. Contains an \"eprint\" table.\n";
		print "You might consider running 'epadmin erase_data $repoid' instead.\n";
		$session->terminate;
		exit 1;
	}

	foreach my $dsid ( &EPrints::DataSet::get_sql_dataset_ids )
	{
		my $ds = $session->get_repository->get_dataset( $dsid );
		my $indexes = $ds->count_indexes;
		if( $indexes > 32 )
		{
			print STDERR "WARNING: Main table of dataset \"$dsid\" requires $indexes indexes.\n";
			$session->terminate;
			EPrints::abort( "MySQL has a maximum of 32 indexes per table and the '$dsid'\ntable requires $indexes. Add an sql_index=>0 parameter to some of the\nfields in this dataset. See the Documentation for more information\non the 'sql_index' metadata parameter. --force will override this\ncheck but the SQL will probably fail anyway." );
		}
	}
			
	if( $noise>=1 ) { print "Creating database tables...\n"; }
	if( $session->get_database->create_archive_tables )
	{
		if( $noise>=1 ) { print "Done creating database tables.\n\n"; }
	}
	else
	{
		my $error = $session->get_database->error;
		print STDERR "DB Error: $error\n";
		$session->terminate;
		exit 1;
	}
}

sub erase_data
{
	my( $repoid ) = @_;

	print <<END;
You are about to erase from $repoid:
  - all database tables
  - all eprint files
  - the generated html pages
but NOT the configuration files.

END
	
	my $sure = EPrints::Utils::get_input_confirm( "Are you sure you want this to happen" );
	unless( $sure )
	{
		print "Aborting then.\n";
		exit( 1 );
	}

	erase_eprint_files( $repoid );
	drop_and_recreate_db( $repoid );
}

sub erase_eprints
{
	my( $repoid ) = @_;

	print <<END;
You are about to erase from $repoid:
  - all eprints and documents data
  - all eprint files
  - all change history
  - the document requests
  - the access logs
  - the generated html pages
but NOT the configuration files, user data or subject data.

END
	
	my $sure = EPrints::Utils::get_input_confirm( "Are you sure you want this to happen" );
	unless( $sure )
	{
		print "Aborting then.\n";
		exit( 1 );
	}

	erase_eprint_files( $repoid );
	foreach( "eprint", "history","access","request","document" )
	{
		reset_dataset( $repoid, $_ );
	}		
	
	my $ok;
	$ok = EPrints::Utils::get_input( $REGEXP_YESNO, "Do you want to build the static web pages?", "yes" );
	if( $ok eq "yes" )
	{
		run_script( $repoid, "generate_static", "--verbose", $repoid );	
	}
}

sub reset_dataset
{
	my( $repoid, $datasetid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise, 1 );
	exit( 1 ) unless( defined $session );

	my $db = $session->get_database;
	my @tables = $db->get_tables;
	print "Erasing dataset $datasetid\n" if( $noise >= 1 );
	foreach my $table ( @tables )
	{
		next unless( $table =~ m/^$datasetid/ );
		print "Erasing table $table\n" if( $noise >= 2 );
		my $sql = "DROP TABLE ".$table;
		$db->do( $sql );
	}

	print "Creating dataset $datasetid\n";
	$db->create_dataset_tables( 
			$session->get_repository->get_dataset( $datasetid ) );

	if( $datasetid ne "subject" )
	{
		print "Resetting counter ${datasetid}id\n";
		$db->counter_reset( $datasetid."id" );
	}

	$session->terminate;
}

# not an option directly!
sub drop_and_recreate_db
{
	my( $repoid ) = @_;

	my $repo = EPrints::Repository->new( $repoid );
	my $database = $repo->get_conf( "dbname" );
	
	if( $noise>=1 ) { print "Connecting to mysql...\n"; }
	
	my $dbh = DBI->connect(
		EPrints::Database::build_connection_string(
			dbname=>"mysql",
			dbsock=>$repo->get_conf( "dbsock" ),
			dbport=>$repo->get_conf( "dbport" ),
			dbhost=>$repo->get_conf( "dbhost" ) ),
		"root",
		get_mysql_root_password() );

	if( !defined $dbh )
	{
		print STDERR "\n\nFailed to connect to database. Aborting.\n\n";
		exit( 1 );
	}

	if( $noise>=1 ) { print "Dropping database \"$database\"\n"; }
	$dbh->do( "drop database $database" );
	if( $noise>=1 ) { print "Re-creating database \"$database\"\n"; }
	$dbh->do( "create database $database" );
	$dbh->disconnect;

	if( $noise>=1 ) { print "Done recreating database\n\n"; }

	my $mktables = EPrints::Utils::get_input( $REGEXP_YESNO, "Create database tables?", "yes" );
	if( $mktables eq "yes" )
	{
		create_tables( $repoid );
	}
}

# not an option directly!
sub erase_eprint_files
{
	my( $repoid ) = @_;

	if( $noise>=1 ) { print "Erasing eprint files...\n"; }

	my $repo = EPrints::Repository->new( $repoid );
	my $documents_path = $repo->get_conf( "documents_path" );
	my $htdocs_path = $repo->get_conf( "htdocs_path" );
	
	# Get available directories
	opendir DOCSTORE, $documents_path
		or print STDERR "Can't open DOCSTORE\n";
	
	my @doomeddirs;
	foreach( readdir DOCSTORE )
	{
		next if m/^\.\.?$/; # skip . and ..
		push @doomeddirs,
			$documents_path."/".$_;
	}
	closedir DOCSTORE;
	
	# Remove the contents of each of the directories.

	push @doomeddirs, $htdocs_path;
	foreach my $dir (@doomeddirs)
	{
		if( $noise>=2 ) { print "Removing stuff in: $dir\n"; }
		my $rc = $repo->exec( "rmall", TARGET=>$dir );
		print STDERR "Warning: Cleaning $dir didn't go smoothly\n" unless( $rc==0 );
	}
	if( $noise>=1 ) { print "...done erasing eprint files.\n"; }
}

sub erase_fulltext_index
{
	my( $repoid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise );
	exit( 1 ) unless( defined $session );

	my $ds = $session->get_repository->get_dataset( "document" );
	print "Stating to erase caches\n" if( $noise >= 1 );

	$ds->map( 
		$session,
		sub 
		{
			my( $session , $dataset , $doc ) = @_;

			if( $noise >= 2 )
			{
				print "Removing fulltext index for: ".$doc->get_id."\n";
			}
			
			my @files = ( $doc->words_file, $doc->indexcodes_file );
			foreach my $file ( @files )
			{
				next unless( -e $file );
				if( $noise >= 2 )
				{
					print "Erasing: $file\n";
				}
				unlink( $file );
			}
		} );

	print "Done erasing\n" if( $noise >= 1 );
	print "Queuing records for re-indexing\n" if( $noise >= 1 );

	my $fn = sub {
		my( $session, $dataset, $item ) = @_;
	
		$session->get_database->index_queue( 
			'eprint',
			$item->get_id,
			$EPrints::Utils::FULLTEXT );
	
		if( $session->get_noise() >= 2 )
		{
			print STDERR "Queued item: ".$dataset->id()."/".$item->get_id()."\n";
		}
	};

	my $ep_ds = $session->get_repository->get_dataset( "eprint" );
	$ep_ds->map( $session, $fn );

	print "Done queuing\n" if( $noise >= 1 );

	$session->terminate;
}

sub test
{
	my( $repoid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise );
	exit( 1 ) unless( defined $session );
	$session->terminate;

	print "Everything seems OK.\n";
}

sub rehash
{
	my( $repoid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise );
	exit( 1 ) unless( defined $session );

	my $docid = $ARGV[2];
	if( defined $docid )
	{
		my $doc = EPrints::DataObj::Document->new( $session, $docid );
		if( !defined $doc )
		{
			$session->get_repository->log( 
	"Document #$docid not found. Can't rehash." );
		}
		else
		{
			$doc->rehash;
			print "Rehashed document #$docid\n" if( $noise > 0);
		}
	}
	else
	{
		print "Rehashing documents\n" if( $noise > 0);
		my $dataset = $session->get_repository->get_dataset( "document" );
		my $info = { count=>0 };
		$dataset->map( 
			$session , 
			sub 
			{
				my( $session, $dataset, $doc, $info ) = @_;
				$doc->rehash;
				if( $noise > 1 )
				{
					print "Rehashed ".$doc->get_value( "docid" )."\n";
				}
				$info->{count}++;
			},
			$info );

		if( $noise > 0)
		{
			print "Done rehashing ".$info->{count}." documents\n";
		}
	}

	$session->terminate;
}

###################################
#
# DATASET related utilities
#
###################################


sub recommit
{
	my( $repoid, $datasetid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise );
	exit( 1 ) unless( defined $session );

	my $dataset = $session->get_repository->get_dataset( $datasetid );
	if( !defined $dataset )
	{
		print "Exiting due to unknown dataset.\n" if( $noise >= 1 );
		$session->terminate();
		exit( 1 );
	}

        if( $noise > 0 )
        {
                print "\n";
                print "You are about to recommit \"$datasetid\" in the $repoid repository.\n";
                print "This can take some time.\n\n";
                print "Number of records in set: ".$dataset->count( $session )."\n";
        }

        my $sure = EPrints::Utils::get_input_confirm( "Continue" );
        unless( $sure )
        {
                print "Aborting then.\n\n";
                $session->terminate();
                exit( 1 );
        }


	my $fn = sub {
        	my( $session, $dataset, $item ) = @_;
	
        	if( $session->get_noise() >= 2 )
        	{
                	print STDERR "Committing item: ".$dataset->id()."/".$item->get_id()."\n";
        	}
        	$item->commit();
	};

	$dataset->map( $session, $fn );

	print "All items in \"$datasetid\" have been re-commited.\n" if( $noise >= 1 );

	$session->terminate;
}

sub reindex
{
	my( $repoid, $datasetid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise );
	exit( 1 ) unless( defined $session );

	my $dataset = $session->get_repository->get_dataset( $datasetid );
	if( !defined $dataset )
	{
		print "Exiting due to unknown dataset.\n" if( $noise >= 1 );
		$session->terminate();
		exit( 1 );
	}

	my $fn = sub {
		my( $session, $dataset, $item ) = @_;
	
		foreach my $field ( $dataset->get_fields() )
		{
			next unless( $field->get_property( "text_index" ) );
	
			$session->get_database->index_queue( 
				$dataset->id,
				$item->get_id,
				$field->get_name );
		}	
		if( $dataset->confid eq "eprint" )
		{
			$session->get_database->index_queue( 
				$dataset->id,
				$item->get_id,
				$EPrints::Utils::FULLTEXT );
		}
	
		if( $session->get_noise() >= 2 )
		{
			print STDERR "Queued item: ".$dataset->id()."/".$item->get_id()."\n";
		}
	};

	$dataset->map( $session, $fn );

	$session->terminate;
}








####################################################################
#
#  UPGRADE CODE	
#
####################################################################

sub upgrade
{
	my( $repoid ) = @_;

	my $session = new EPrints::Session( 1 , $repoid , $noise, 1 );
	exit( 1 ) unless( defined $session );
	my $db = $session->get_db();
	my $dbversion = $db->get_version();
	
	if( $dbversion eq "3.0" )
	{
		upgrade_3_0_to_3_0_1( $repoid, $db );
		$dbversion="3.0.1";
		$db->set_version( $dbversion );
	}
	if( $dbversion eq "3.0.1" )
	{
		upgrade_3_0_1_to_3_0_2( $repoid, $db );
		$dbversion="3.0.2";
		$db->set_version( $dbversion );
	}
	if( $dbversion eq "3.0.2" )
	{
		upgrade_3_0_2_to_3_0_3( $repoid, $db );
		$dbversion="3.0.3";
		$db->set_version( $dbversion );
	}
	if( $dbversion eq "3.0.3" )
	{
		upgrade_3_0_3_to_3_0_4( $repoid, $db );
		$dbversion="3.0.4";
		$db->set_version( $dbversion );
	}
	if( $dbversion eq "3.0.4" )
	{
		upgrade_3_0_4_to_3_0_5( $repoid, $db );
		$dbversion="3.0.5";
		$db->set_version( $dbversion );
	}
	if( $dbversion eq "3.0.5" )
	{
		upgrade_3_0_5_to_3_0_6( $repoid, $db );
		$dbversion="3.0.6";
		$db->set_version( $dbversion );
	}

	print $dbversion."\n";
	$session->terminate;
}

sub upgrade_3_0_to_3_0_1
{
	my( $repoid, $db ) = @_;

	my $rootdbh = root_dbh( $repoid );
	if( !defined $rootdbh ) { die "Could not open database as root"; }
	my @tables = $db->get_tables;
	my $sql;

	$sql = "ALTER TABLE saved_search ADD public set('TRUE','FALSE') default 'FALSE' AFTER mailempty";
	$rootdbh->do( $sql );

	$sql = "CREATE TABLE user_items_fields ( userid INT NOT NULL, pos INT, items_fields VARCHAR(255) default NULL, KEY userid (userid), KEY pos (pos) )  ";
	$rootdbh->do( $sql );

	$sql = "CREATE TABLE user_review_fields ( userid INT NOT NULL, pos INT, review_fields VARCHAR(255) default NULL, KEY userid (userid), KEY pos (pos) )  ";
	$rootdbh->do( $sql );

	foreach my $table ( @tables )
	{
		if( $table =~ m/^saved_search__ordervalues_.*$/ )
		{
			$sql = "ALTER TABLE $table ADD public TEXT AFTER mailempty",
			$rootdbh->do( $sql );
		}
		if( $table =~ m/^user__ordervalues_.*$/ )
		{
			$sql = "ALTER TABLE $table ADD items_fields TEXT AFTER mailempty",
			$rootdbh->do( $sql );
			$sql = "ALTER TABLE $table ADD review_fields TEXT AFTER items_fields",
			$rootdbh->do( $sql );
		}
	}

	$rootdbh->disconnect;
}



sub upgrade_3_0_1_to_3_0_2
{
	my( $repoid, $db ) = @_;

	my $rootdbh = root_dbh( $repoid );
	if( !defined $rootdbh ) { die "Could not open database as root"; }
	my @tables = $db->get_tables;
	my $sql;

	$sql = "ALTER TABLE saved_search ADD name VARCHAR(255) NOT NULL AFTER pos";
	$rootdbh->do( $sql );

	$sql = "ALTER TABLE cachemap ADD userid INTEGER AFTER lastused";
	$rootdbh->do( $sql );

	foreach my $table ( @tables )
	{
		if( $table =~ m/^saved_search__ordervalues_.*$/ )
		{
			$sql = "ALTER TABLE $table ADD name TEXT AFTER pos",
			$rootdbh->do( $sql );
		}
	}

	$rootdbh->disconnect;
}

sub upgrade_3_0_2_to_3_0_3
{
	my( $repoid, $db ) = @_;

	my $rootdbh = root_dbh( $repoid );
	if( !defined $rootdbh ) { die "Could not open database as root"; }
	my @tables = $db->get_tables;
	my $sql;

	$sql = "ALTER TABLE eprint DROP fileinfo";
	$rootdbh->do( $sql );

	$sql = "ALTER TABLE eprint ADD fileinfo TEXT AFTER contact_email";
	$rootdbh->do( $sql );

	foreach my $table ( @tables )
	{
		if( $table =~ m/^eprint__ordervalues_.*$/ )
		{
			$sql = "ALTER TABLE $table DROP fileinfo";
			$rootdbh->do( $sql );
			$sql = "ALTER TABLE $table ADD fileinfo TEXT AFTER contact_email",
			$rootdbh->do( $sql );
		}
	}

	$rootdbh->disconnect;
}



sub upgrade_3_0_3_to_3_0_4
{
	my( $repoid, $db ) = @_;

	my $rootdbh = root_dbh( $repoid );
	if( !defined $rootdbh ) { die "Could not open database as root"; }
	my @tables = $db->get_tables;
	my $sql;

	$sql = "ALTER TABLE eprint ADD latitude FLOAT AFTER fileinfo";
	$rootdbh->do( $sql );
	$sql = "ALTER TABLE eprint ADD longitude FLOAT AFTER latitude";
	$rootdbh->do( $sql );
	$sql = "ALTER TABLE user ADD latitude FLOAT AFTER mailempty";
	$rootdbh->do( $sql );
	$sql = "ALTER TABLE user ADD longitude FLOAT AFTER latitude";
	$rootdbh->do( $sql );

	foreach my $table ( @tables )
	{
		if( $table =~ m/^eprint__ordervalues_.*$/ )
		{
			$sql = "ALTER TABLE $table ADD latitude TEXT AFTER fileinfo";
			$rootdbh->do( $sql );
			$sql = "ALTER TABLE $table ADD longitude TEXT AFTER latitude";
			$rootdbh->do( $sql );
		}
		if( $table =~ m/^user__ordervalues_.*$/ )
		{
			$sql = "ALTER TABLE $table ADD latitude TEXT AFTER review_fields";
			$rootdbh->do( $sql );
			$sql = "ALTER TABLE $table ADD longitude TEXT AFTER latitude";
			$rootdbh->do( $sql );
		}
	}

	$rootdbh->disconnect;
}


sub upgrade_3_0_4_to_3_0_5
{
	my( $repoid, $db ) = @_;

	$db->_create_messages_table;
}



sub upgrade_3_0_5_to_3_0_6
{
	my( $repoid, $db ) = @_;

	reset_dataset( $repoid, 'subject' );
}




















