#!/usr/bin/env python # -*- coding: iso8859-1 -*- __version__ = '0.5' __author__ = 'Daniel Lundin , Jonas Borgström ' __copyright__ = 'Copyright (c) 2004 Edgewall Software' __license__ = """ Copyright (C) 2003, 2004 Edgewall Software Copyright (C) 2003, 2004 Jonas Borgström Copyright (C) 2003, 2004 Daniel Lundin Trac 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. Trac 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.""" import os import os.path import sys import time import cmd import shlex import sqlite import StringIO import trac.siteconfig def my_sum(list): """Python2.1 doesn't have sum()""" tot = 0 for item in list: tot += item return tot class TracAdmin(cmd.Cmd): intro = '' license = trac.__license_long__ credits = trac.__credits__ doc_header = 'Trac Admin Console %(ver)s\n' \ 'Available Commands:\n' \ % {'ver':__version__ } ruler = '' prompt = "Trac> " db_schema = """ CREATE TABLE revision ( rev integer PRIMARY KEY, time integer, author text, message text ); CREATE TABLE node_change ( rev integer, name text, change char(1), UNIQUE(rev, name, change) ); CREATE TABLE auth_cookie ( cookie text, name text, ipnr text, time integer, UNIQUE(cookie, name, ipnr) ); CREATE TABLE enum ( type text, name text, value text, UNIQUE(name,type) ); CREATE TABLE config ( section text, name text, value text, UNIQUE(section, name) ); CREATE TABLE ticket ( id integer PRIMARY KEY, time integer, -- the time it was created changetime integer, component text, severity text, priority text, owner text, -- who is this ticket assigned to reporter text, cc text, -- email addresses to notify url text, -- url related to this ticket version text, -- milestone text, -- status text, resolution text, summary text, -- one-line summary description text -- problem description (long) ); CREATE TABLE ticket_change ( ticket integer, time integer, author text, field text, oldvalue text, newvalue text ); CREATE TABLE report ( id integer PRIMARY KEY, author text, title text, sql text ); CREATE TABLE permission ( user text, -- action text -- allowable activity ); CREATE TABLE component ( name text PRIMARY KEY, owner text ); CREATE TABLE milestone ( name text PRIMARY KEY, time integer ); CREATE TABLE version ( name text PRIMARY KEY, time integer ); CREATE TABLE wiki ( name text, version integer, time integer, author text, ipnr text, locked integer, text text, UNIQUE(name,version) ); CREATE INDEX node_change_idx ON node_change(rev); CREATE INDEX ticket_change_idx ON ticket_change(ticket, time); CREATE INDEX wiki_idx ON wiki(name,version); """ # Default data for database # (table, (column1, column2), ((row1col1, row1col2), (row2col1, row2col2))) db_data = (('component', ('name', 'owner'), (('component1', 'somebody'), ('component2', 'somebody'))), ('milestone', ('name', 'time'), (('', 0), ('milestone1', 0), ('milestone2', 0), ('milestone3', 0), ('milestone4', 0))), ('version', ('name', 'time'), (('', 0), ('1.0', 0), ('2.0', 0))), ('enum', ('type', 'name', 'value'), (('status', 'new', 1), ('status', 'assigned', 2), ('status', 'reopened', 3), ('status', 'closed', 4), ('resolution', 'fixed', 1), ('resolution', 'invalid', 2), ('resolution', 'wontfix', 3), ('resolution', 'duplicate', 4), ('resolution', 'worksforme', 5), ('severity', 'blocker', 1), ('severity', 'critical', 2), ('severity', 'major', 3), ('severity', 'normal', 4), ('severity', 'minor', 5), ('severity', 'trivial', 6), ('severity', 'enhancement', 7), ('priority', 'highest', 1), ('priority', 'high', 2), ('priority', 'normal', 3), ('priority', 'low', 4), ('priority', 'lowest', 5))), ('permission', ('user', 'action'), (('anonymous', 'LOG_VIEW'), ('anonymous', 'FILE_VIEW'), ('anonymous', 'WIKI_VIEW'), ('anonymous', 'WIKI_CREATE'), ('anonymous', 'WIKI_MODIFY'), ('anonymous', 'SEARCH_VIEW'), ('anonymous', 'REPORT_VIEW'), ('anonymous', 'TICKET_VIEW'), ('anonymous', 'TICKET_CREATE'), ('anonymous', 'TICKET_MODIFY'), ('anonymous', 'BROWSER_VIEW'), ('anonymous', 'TIMELINE_VIEW'), ('anonymous', 'CHANGESET_VIEW'))), ('config', ('section', 'name', 'value'), (('trac', 'database_version', '1'), ('general', 'htdocs_location', '/trac/'), ('general', 'repository_dir', '/var/svn/myrep'), ('general', 'templates_dir', '/usr/lib/trac/templates'), ('project', 'name', 'My Project'), ('project', 'descr', 'My example project'), ('project', 'url', 'http://example.com/'), ('ticket', 'default_version', ''), ('ticket', 'default_severity', 'normal'), ('ticket', 'default_priority', 'normal'), ('ticket', 'default_milestone', ''), ('ticket', 'default_component', 'component1'), ('header_logo', 'link', 'http://trac.edgewall.com/'), ('header_logo', 'src', 'trac_banner.png'), ('header_logo', 'alt', 'Trac'), ('header_logo', 'width', '236'), ('header_logo', 'height', '73'))), ('report', ('id', 'author', 'title', 'sql'), (('1', None, 'Active Tickets', "SELECT id AS ticket, status, severity, priority, owner, " "time as created, summary FROM ticket " "WHERE status IN ('new', 'assigned', 'reopened')" "ORDER BY priority, time"),))) def __init__(self,dbfile=None): cmd.Cmd.__init__(self) self.interactive = 0 if dbfile: self.db_set(dbfile) def docmd(self, cmd='help'): self.onecmd(cmd) def run(self): self.interactive = 1 print 'Welcome to trac-admin %(ver)s\n' \ 'Interactive Trac adminstration console.\n' \ '%(copy)s\n\n' \ "Type: '?' or 'help' for help on commands.\n" % \ {'ver':__version__,'copy':__copyright__} while 1: try: self.cmdloop() break except KeyboardInterrupt: print "\n** Interrupt. Use 'quit' to exit **" ## ## Database methods ## def db_set(self, dbfile): self.dbname = dbfile self.prompt = "Trac [%s]> " % self.dbname def db_check(self): if os.path.isfile(self.dbname): f = open (self.dbname) data = f.read (50) if -1 == data.find("This file contains an SQLite"): return 0 f.close() return 1 return 0 def db_open(self): try: if not self.db_check(): raise Exception return sqlite.connect (self.dbname) except Exception, e: print 'Failed to open/create database.', e sys.exit(1) def db_execsql (self, sql, cursor=None): data = [] if not cursor: cnx=self.db_open() cursor = cnx.cursor() else: cnx = None cursor.execute(sql) while 1: row = cursor.fetchone() if row == None: break data.append(row) if cnx: cnx.commit() return data ## ## Utility methods ## def arg_tokenize (self, argstr): if hasattr(shlex, 'split'): toks = shlex.split(argstr) else: def my_strip(s, c): """string::strip in python2.1 doesn't support arguments""" i = j = 0 for i in range(len(s)): if not s[i] in c: break for j in range(len(s), 0, -1): if not s[j-1] in c: break return s[i:j] lexer = shlex.shlex(StringIO.StringIO(argstr)) lexer.wordchars = lexer.wordchars + ".,_/" toks = [] while 1: token = my_strip(lexer.get_token(), '"\'') if not token: break toks.append(token) return toks or [''] def word_complete (self, text, words): return [a for a in words if a.startswith (text)] def print_listing(self, headers, data, sep=' ',decor=1): ldata = data if decor: ldata.insert (0, headers) print colw=[] ncols = len(ldata[0]) # assumes all rows are of equal length for cnum in xrange(0, ncols): mw = 0 for cell in [str(d[cnum]) or '' for d in ldata]: if len(cell) > mw: mw = len(cell) colw.append(mw) for rnum in xrange(0, len(ldata)): for cnum in xrange(0, ncols): if decor and rnum == 0: sp = ('%%%ds' % len(sep)) % ' ' # No separator in header else: sp = sep if cnum+1 == ncols: sp = '' # No separator after last column print ("%%-%ds%s" % (colw[cnum], sp)) % (ldata[rnum][cnum] or ''), print if rnum == 0 and decor: print ''.join(['-' for x in xrange(0,(1+len(sep))*cnum+my_sum(colw))]) print def print_doc(self,doc,decor=0): if not doc: return self.print_listing (['Command','Description'], doc, ' --', decor) def get_component_list (self): data = self.db_execsql ("SELECT name FROM component") return [r[0] for r in data] def get_config_list (self): data = self.db_execsql ("SELECT section||'.'||name FROM config") return [r[0] for r in data] def get_user_list (self): data = self.db_execsql ("SELECT DISTINCT user FROM permission") return [r[0] for r in data] def get_wiki_list (self): data = self.db_execsql('SELECT DISTINCT name FROM wiki') return [r[0] for r in data] def get_dir_list (self, pathstr): dname = os.path.dirname(pathstr) d = os.path.join(os.getcwd(), dname) ls = os.listdir(d) return ls def get_enum_list (self, type): data = self.db_execsql("SELECT name FROM enum WHERE type='%s'" % type) return [r[0] for r in data] def get_milestone_list (self): data = self.db_execsql("SELECT name FROM milestone") return [r[0] for r in data] def get_version_list (self): data = self.db_execsql("SELECT name FROM version") return [r[0] for r in data] ## ## Available Commands ## ## Help _help_help = [('help', 'Show documentation')] def do_help(self, line=None): arg = self.arg_tokenize(line) if arg[0]: try: doc = getattr(self, "_help_" + arg[0]) self.print_doc (doc) except AttributeError: print "No documentation found for '%s'" % arg[0] else: docs = (self._help_about + self._help_help + self._help_initdb + self._help_config + self._help_wiki + self._help_permission + self._help_component + self._help_priority + self._help_severity + self._help_version + self._help_milestone) print 'trac-admin - The Trac Administration Console %s' % __version__ if not self.interactive: print print "Usage: trac-admin [command [subcommand] [option ...]]\n" print "Invoking trac-admin without command starts "\ "interactive mode." self.print_doc (docs) print self.credits ## About / Version _help_about = [('about', 'Shows information about trac-admin')] def do_about(self, line): print print 'Trac Admin Console %s' % __version__ print '=================================================================' print self.license print self.credits ## Quit / EOF _help_quit = [['quit', 'Exit the program']] _help_EOF = _help_quit def do_quit(self,line): print sys.exit() do_EOF = do_quit # Alias ## Config _help_config = [('config list', 'Show current configuration'), ('config set