# Copyright David Abrahams 2007. Distributed under the Boost
# Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

import sys
import os
from trac.config import Configuration

parent_dir = '/usr/local/share/tracs'
old_trac_dir = parent_dir
new_trac_dir = '/usr/local/share/trac'
repospath = '/usr/local/svnroot/main'

dbname = 'trac'
master_user = 'trac'

def system(cmd):
    sys.stderr.flush()
    if noact:
        print '--------------------------not executing:---------------------------'
        print cmd
        return
    elif verbose:
        print '--------------------------executing:---------------------------'
        print cmd
        sys.stdout.flush()
        
    status = os.system(cmd)
    if status:
        raise SystemError, "Exit status %s\n%r" % (status, cmd)
    return

### Read arguments and options ###
class Args(set):
    def __contains__(self, x):
        super_in = super(Args,self).__contains__
        if not x.startswith('-') and super_in('ALL'):
            return True
        else:
            return super_in(x)
    
args = Args(sys.argv[1:])
verbose = '-v' in args
noact = '-n' in args
passwd = [ x for x in args if x.startswith('-p') ][0][2:]
if not passwd:
    raise TypeError, 'password required in the form -pPASSWORD'

### Find Tracs to Operate On ###
children = set(os.listdir(old_trac_dir))
tracspec = set([ o[:-1] for o in args if o.endswith(':') ])
if tracspec:
    children &= tracspec
tracs = [ d for d in [ os.path.join(parent_dir,f) for f in children ]
          if os.path.isdir(d) ]

### Create Directory Structure ###
backup_dir = os.path.join(new_trac_dir, 'projects')
try:
    os.makedirs(backup_dir)
except OSError: pass

for d in 'conf', 'templates', 'plugins':
    try:
        os.makedirs(os.path.join(new_trac_dir,'global',d))
    except OSError: pass
    
### Upgrade and Backup ###
newtracs = [ os.path.join(backup_dir, os.path.split(t)[1]) for t in tracs ]
for t,n in zip(tracs, newtracs):
    if 'upgrade' in args:
        system('trac-admin "%s" upgrade' % t)
        system('trac-admin "%s" wiki upgrade' % t)

    if 'backup' in args:
        system('trac-admin "%s" hotcopy "%s"' % (t, n))
        
### Adjust Paths to Point at Backups ###
tracs = newtracs
parent_dir = backup_dir

### Create PostGreSQL Database ###
if 'createdb' in args:
    
    # could be smarter about quoting passwd
    system(
        '''psql -d postgres -v "tracpass='%s'" -f create_multitrac_db.sql''' 
        % passwd
        );

global_conf = os.path.join(new_trac_dir,'global','conf')
project_trac_ini = os.path.join(global_conf, 'project-trac.ini')

config_specifics = {
    'project': ['descr', 'name', 'url'], 
    'trac': ['base_url', 'database'],
    }

### Create Master Trac Instance ###
if 'createmaster' in args:
    master_env = os.path.join(parent_dir, 'master')
    system('''trac-admin %(master_env)s initenv master'''
           ''' "postgres://trac_instance_master:%(passwd)s'''
           '''@localhost/%(dbname)s'''
           '''?schema=trac_instance_master"'''
           ''' svn "%(repospath)s"'''
               % locals())

    base_trac_ini = os.path.join(global_conf, 'base-trac.ini')
    master_trac_ini = os.path.join(master_env,'conf','trac.ini')

    # Move the master project's trac.ini into the base file
    if not noact:
        if verbose:
            print 'renaming %s => %s' % (master_trac_ini, base_trac_ini)
        os.rename(master_trac_ini, base_trac_ini)

    # Make a new master config file, and move any project specifics from the
    # base file back into it.
    base_config = Configuration(base_trac_ini)
    master_config = Configuration(master_trac_ini)
    for section,names in config_specifics.items():
        for name in names:
            master_config.set(section,name, base_config.get(section,name))
            base_config.remove(section, name)
            
    # inherit everything else from the base config file
    master_config.set('inherit', 'file', base_trac_ini)

    # set up tracforge and account manager components
    master_components = master_config['components']
    master_components.set('acct_mgr.*', 'enabled')
    master_components.set('multiple_project_query_filter', 'enabled')
    master_components.set('tracforge.*', 'enabled')
    master_components.set('tracforge.linker.*', 'disabled')
    master_components.set('tracforge.linker.auth.tracforgecookiemunger', 'enabled')
    master_components.set('tracforge.subscriptions', 'disabled')
    if not noact:
        master_config.save()

    # We're not using tracforge subscriptions
    base_config.set('components', 'tracforge.subscriptions', 'disabled')

    # Prepare global plugins and templates directories
    base_config.set('inherit', 'templates_dir', os.path.join(new_trac_dir,'global','templates'))
    base_config.set('inherit', 'plugins_dir', os.path.join(new_trac_dir,'global','plugins'))
    if not noact:
        base_config.save()

    # set up a config file for individual projects
    project_config = Configuration(project_trac_ini)
    project_config.set('inherit', 'file', base_trac_ini)
    project_components = project_config['components']
    project_components.set('acct_mgr.*', 'disabled')
    project_components.set('web.auth.loginmodule', 'disabled')
    project_components.set('tracforge.admin.perm.*', 'enabled')
    project_components.set('tracforge.linker.*', 'enabled')
    project_components.set('tracforge.perms.*', 'enabled')
    if not noact:
        project_config.save()
        
### Create PostGreSQL users and translate the databases ###
for t in tracs:
    project = os.path.split(t)[1]

    username = 'trac_instance_' + project

    if 'createuser' in args:
        system('''psql -d %(dbname)s -U %(master_user)s<<EOF
CREATE ROLE %(username)s PASSWORD '%(passwd)s'
  IN ROLE trac_instance LOGIN;
  
-- Give the master trac instance all the same 
-- permissions that this one has.
GRANT %(username)s TO trac_instance_master;
EOF
''' % locals()
               )

    dbstring = ('postgres://'
               '%(username)s:%(passwd)s'
               '@localhost/%(dbname)s'
               '?schema=%(username)s'
                % locals())

    if 'translate' in args:
        system('python ./sqlite2pg -t "%(parent_dir)s"'
               ' -p "%(dbstring)s"'
               ' %(project)s'
               % locals()
           );

    if 'newini' in args:
        conf = os.path.join(parent_dir, project, 'conf')
        new_ini = os.path.join(conf, 'trac.ini')
        old_ini = os.path.join(conf, 'trac.bak.ini')
        if not noact:
            os.rename(new_ini, old_ini)
        
        old_config = Configuration(old_ini)
        new_config = Configuration(new_ini)
        
        new_config.set('inherit','file',project_trac_ini)
        for section,names in config_specifics.items():
            for name in names:
                new_config.set(section, name, old_config.get(section,name))
                
        if not noact:
            new_config.save()
            
    if 'upgrade' in args:
        system('trac-admin "%s" upgrade --no-backup' % t)
        
print 'done.'
        
