X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=roundup-admin;h=9a1f74ce823545ebaf47545e139e563ffa0365ed;hb=00d1b4eae89cee6d7c37c094cc23e899351a5a52;hp=4267f433df594320e926a15eb10337b53f12c2fa;hpb=bcf81f8b9eca040a13c44f53f51bd90fe033b9b4;p=roundup.git diff --git a/roundup-admin b/roundup-admin index 4267f43..9a1f74c 100755 --- a/roundup-admin +++ b/roundup-admin @@ -1,4 +1,4 @@ -#! /usr/bin/python +#! /usr/bin/env python # # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/) # This module is free software, and you may redistribute it and/or modify @@ -16,434 +16,197 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: roundup-admin,v 1.23 2001-10-09 23:36:25 richard Exp $ +# $Id: roundup-admin,v 1.61 2002-01-05 02:21:21 richard Exp $ -import sys -if int(sys.version[0]) < 2: - print 'Roundup requires python 2.0 or later.' - sys.exit(1) - -import string, os, getpass, getopt, re -from roundup import date, roundupdb, init, password -import roundup.instance - -def usage(message=''): - if message: message = 'Problem: '+message+'\n' - commands = [] - for command in figureCommands().values(): - h = command.__doc__.split('\n')[0] - commands.append(h[7:]) - commands.sort() - print '''%sUsage: roundup-admin [-i instance home] [-u login] [-c] - -Commands: - %s -Help: - roundup-admin -h - roundup-admin help -- this help - roundup-admin help -- command-specific help - roundup-admin morehelp -- even more detailed help -Options: - -i instance home -- specify the issue tracker "home directory" to administer - -u -- the user[:password] to use for commands - -c -- when outputting lists of data, just comma-separate them'''%( -message, '\n '.join(commands)) - -def moreusage(message=''): - usage(message) - print ''' -All commands (except help) require an instance specifier. This is just the path -to the roundup instance you're working with. A roundup instance is where -roundup keeps the database and configuration file that defines an issue -tracker. It may be thought of as the issue tracker's "home directory". It may -be specified in the environment variable ROUNDUP_INSTANCE or on the command -line as "-i instance". - -A designator is a classname and a nodeid concatenated, eg. bug1, user10, ... - -Property values are represented as strings in command arguments and in the -printed results: - . Strings are, well, strings. - . Date values are printed in the full date format in the local time zone, and - accepted in the full format or any of the partial formats explained below. - . Link values are printed as node designators. When given as an argument, - node designators and key strings are both accepted. - . Multilink values are printed as lists of node designators joined by commas. - When given as an argument, node designators and key strings are both - accepted; an empty string, a single node, or a list of nodes joined by - commas is accepted. - -When multiple nodes are specified to the roundup get or roundup set -commands, the specified properties are retrieved or set on all the listed -nodes. - -When multiple results are returned by the roundup get or roundup find -commands, they are printed one per line (default) or joined by commas (with -the -c) option. - -Where the command changes data, a login name/password is required. The -login may be specified as either "name" or "name:password". - . ROUNDUP_LOGIN environment variable - . the -u command-line option -If either the name or password is not supplied, they are obtained from the -command-line. - -Date format examples: - "2000-04-17.03:45" means - "2000-04-17" means - "01-25" means - "08-13.22:13" means - "11-07.09:32:43" means - "14:25" means - "8:47:11" means - "." means "right now" - -Command help: -''' - for name, command in figureCommands().items(): - print '%s:'%name - print ' ',command.__doc__ - -def do_init(instance_home, args): - '''Usage: init [template [backend [admin password]]] - Initialise a new Roundup instance. - - The command will prompt for the instance home directory (if not supplied - through INSTANCE_HOME or the -i option. The template, backend and admin - password may be specified on the command-line as arguments, in that order. - ''' - # select template - import roundup.templates - templates = roundup.templates.listTemplates() - template = len(args) > 1 and args[1] or '' - if template not in templates: - print 'Templates:', ', '.join(templates) - while template not in templates: - template = raw_input('Select template [extended]: ').strip() - if not template: - template = 'extended' - - import roundup.backends - backends = roundup.backends.__all__ - backend = len(args) > 2 and args[2] or '' - if backend not in backends: - print 'Back ends:', ', '.join(backends) - while backend not in backends: - backend = raw_input('Select backend [anydbm]: ').strip() - if not backend: - backend = 'anydbm' - if len(args) > 3: - adminpw = confirm = args[3] - else: - adminpw = '' - confirm = 'x' - while adminpw != confirm: - adminpw = getpass.getpass('Admin Password: ') - confirm = getpass.getpass(' Confirm: ') - init.init(instance_home, template, backend, adminpw) - return 0 - - -def do_get(db, args): - '''Usage: get property designator[,designator]* - Get the given property of one or more designator(s). - - Retrieves the property value of the nodes specified by the designators. - ''' - propname = args[0] - designators = string.split(args[1], ',') - # TODO: handle the -c option - for designator in designators: - classname, nodeid = roundupdb.splitDesignator(designator) - print db.getclass(classname).get(nodeid, propname) - return 0 - - -def do_set(db, args): - '''Usage: set designator[,designator]* propname=value ... - Set the given property of one or more designator(s). - - Sets the property to the value for all designators given. - ''' - from roundup import hyperdb - - designators = string.split(args[0], ',') - props = {} - for prop in args[1:]: - key, value = prop.split('=') - props[key] = value - for designator in designators: - classname, nodeid = roundupdb.splitDesignator(designator) - cl = db.getclass(classname) - properties = cl.getprops() - for key, value in props.items(): - type = properties[key] - if isinstance(type, hyperdb.String): - continue - elif isinstance(type, hyperdb.Password): - props[key] = password.Password(value) - elif isinstance(type, hyperdb.Date): - props[key] = date.Date(value) - elif isinstance(type, hyperdb.Interval): - props[key] = date.Interval(value) - elif isinstance(type, hyperdb.Link): - props[key] = value - elif isinstance(type, hyperdb.Multilink): - props[key] = value.split(',') - apply(cl.set, (nodeid, ), props) - return 0 - -def do_find(db, args): - '''Usage: find classname propname=value ... - Find the nodes of the given class with a given property value. - - Find the nodes of the given class with a given property value. The - value may be either the nodeid of the linked node, or its key value. - ''' - classname = args[0] - cl = db.getclass(classname) - - # look up the linked-to class and get the nodeid that has the value - propname, value = args[1].split('=') - num_re = re.compile('^\d+$') - if num_re.match(value): - nodeid = value - else: - propcl = cl.properties[propname].classname - propcl = db.getclass(propcl) - nodeid = propcl.lookup(value) - - # now do the find - # TODO: handle the -c option - print cl.find(**{propname: nodeid}) - return 0 - -def do_spec(db, args): - '''Usage: spec classname - Show the properties for a classname. - - This lists the properties for a given class. - ''' - classname = args[0] - cl = db.getclass(classname) - keyprop = cl.getkey() - for key, value in cl.properties.items(): - if keyprop == key: - print '%s: %s (key property)'%(key, value) - else: - print '%s: %s'%(key, value) - -def do_create(db, args, pretty_re=re.compile(r'')): - '''Usage: create classname property=value ... - Create a new entry of a given class. - - This creates a new entry of the given class using the property - name=value arguments provided on the command line after the "create" - command. - ''' - from roundup import hyperdb - - classname = args[0] - cl = db.getclass(classname) - props = {} - properties = cl.getprops(protected = 0) - if len(args) == 1: - # ask for the properties - for key, value in properties.items(): - if key == 'id': continue - m = pretty_re.match(str(value)) - if m: - value = m.group(1) - value = raw_input('%s (%s): '%(key.capitalize(), value)) - if value: - props[key] = value - else: - # use the args - for prop in args[1:]: - key, value = prop.split('=') - props[key] = value - - # convert types - for key in props.keys(): - type = properties[key] - if isinstance(type, hyperdb.Date): - props[key] = date.Date(value) - elif isinstance(type, hyperdb.Interval): - props[key] = date.Interval(value) - elif isinstance(type, hyperdb.Multilink): - props[key] = value.split(',') - - if cl.getkey() and not props.has_key(cl.getkey()): - print "You must provide the '%s' property."%cl.getkey() - else: - print apply(cl.create, (), props) - - return 0 - -def do_list(db, args): - '''Usage: list classname [property] - List the instances of a class. - - Lists all instances of the given class along. If the property is not - specified, the "label" property is used. The label property is tried - in order: the key, "name", "title" and then the first property, - alphabetically. - ''' - classname = args[0] - cl = db.getclass(classname) - if len(args) > 1: - key = args[1] - else: - key = cl.labelprop() - # TODO: handle the -c option - for nodeid in cl.list(): - value = cl.get(nodeid, key) - print "%4s: %s"%(nodeid, value) - return 0 - -def do_history(db, args): - '''Usage: history designator - Show the history entries of a designator. - - Lists the journal entries for the node identified by the designator. - ''' - classname, nodeid = roundupdb.splitDesignator(args[0]) - # TODO: handle the -c option - print db.getclass(classname).history(nodeid) - return 0 - -def do_retire(db, args): - '''Usage: retire designator[,designator]* - Retire the node specified by designator. - - This action indicates that a particular node is not to be retrieved by - the list or find commands, and its key value may be re-used. - ''' - designators = string.split(args[0], ',') - for designator in designators: - classname, nodeid = roundupdb.splitDesignator(designator) - db.getclass(classname).retire(nodeid) - return 0 - -def do_freshen(db, args): - '''Usage: freshen - Freshen an existing instance. **DO NOT USE** - - This currently kills databases!!!! - - This action should generally not be used. It reads in an instance - database and writes it again. In the future, is may also update - instance code to account for changes in templates. It's probably wise - not to use it anyway. Until we're sure it won't break things... - ''' -# for classname, cl in db.classes.items(): -# properties = cl.properties.items() -# for nodeid in cl.list(): -# node = {} -# for name, type in properties: -# isinstance( if type, hyperdb.Multilink): -# node[name] = cl.get(nodeid, name, []) -# else: -# node[name] = cl.get(nodeid, name, None) -# db.setnode(classname, nodeid, node) - return 1 - -def figureCommands(): - d = {} - for k, v in globals().items(): - if k[:3] == 'do_': - d[k[3:]] = v - return d - -def printInitOptions(): - import roundup.templates - templates = roundup.templates.listTemplates() - print 'Templates:', ', '.join(templates) - import roundup.backends - backends = roundup.backends.__all__ - print 'Back ends:', ', '.join(backends) - -def main(): - opts, args = getopt.getopt(sys.argv[1:], 'i:u:hc') - - # handle command-line args - instance_home = os.environ.get('ROUNDUP_INSTANCE', '') - name = password = '' - if os.environ.has_key('ROUNDUP_LOGIN'): - l = os.environ['ROUNDUP_LOGIN'].split(':') - name = l[0] - if len(l) > 1: - password = l[1] - comma_sep = 0 - for opt, arg in opts: - if opt == '-h': - usage() - return 0 - if opt == '-i': - instance_home = arg - if opt == '-c': - comma_sep = 1 - - # figure the command - if not args: - usage('No command specified') - return 1 - command = args[0] - - # handle help now - if command == 'help': - if len(args)>1: - command = figureCommands().get(args[1], None) - if not command: - usage('no such command "%s"'%args[1]) - return 1 - print command.__doc__ - if args[1] == 'init': - printInitOptions() - return 0 - usage() - return 0 - if command == 'morehelp': - moreusage() - return 0 - - # make sure we have an instance_home - while not instance_home: - instance_home = raw_input('Enter instance home: ').strip() +# python version check +from roundup import version_check - # before we open the db, we may be doing an init - if command == 'init': - return do_init(instance_home, args) +# import the admin tool guts and make it go +from roundup.admin import AdminTool +from roundup.i18n import _ - function = figureCommands().get(command, None) - - # not a valid command - if function is None: - usage('Unknown command "%s"'%command) - return 1 - - # get the instance - instance = roundup.instance.open(instance_home) - db = instance.open('admin') - - if len(args) < 2: - print function.__doc__ - return 1 - - # do the command - try: - return function(db, args[1:]) - finally: - db.close() - - return 1 - - -if __name__ == '__main__': - sys.exit(main()) +import sys +tool = AdminTool() +sys.exit(tool.main()) # # $Log: not supported by cvs2svn $ +# Revision 1.60 2002/01/05 02:11:22 richard +# I18N'ed roundup admin - and split the code off into a module so it can be used +# elsewhere. +# Big issue with this is the doc strings - that's the help. We're probably going to +# have to switch to not use docstrings, which will suck a little :( +# +# Revision 1.59 2001/12/31 05:20:34 richard +# . #496360 ] table width does not work +# +# Revision 1.58 2001/12/31 05:12:52 richard +# actually handle the advertised response to "commit y/N?" +# +# Revision 1.57 2001/12/31 05:12:01 richard +# added some quoting instructions to roundup-admin +# +# Revision 1.56 2001/12/31 05:09:20 richard +# Added better tokenising to roundup-admin - handles spaces and stuff. Can +# use quoting or backslashes. See the roundup.token pydoc. +# +# Revision 1.55 2001/12/17 03:52:47 richard +# Implemented file store rollback. As a bonus, the hyperdb is now capable of +# storing more than one file per node - if a property name is supplied, +# the file is called designator.property. +# I decided not to migrate the existing files stored over to the new naming +# scheme - the FileClass just doesn't specify the property name. +# +# Revision 1.54 2001/12/15 23:09:23 richard +# Some cleanups in roundup-admin, also made it work again... +# +# Revision 1.53 2001/12/13 00:20:00 richard +# . Centralised the python version check code, bumped version to 2.1.1 (really +# needs to be 2.1.2, but that isn't released yet :) +# +# Revision 1.52 2001/12/12 21:47:45 richard +# . Message author's name appears in From: instead of roundup instance name +# (which still appears in the Reply-To:) +# . envelope-from is now set to the roundup-admin and not roundup itself so +# delivery reports aren't sent to roundup (thanks Patrick Ohly) +# +# Revision 1.51 2001/12/10 00:57:38 richard +# From CHANGES: +# . Added the "display" command to the admin tool - displays a node's values +# . #489760 ] [issue] only subject +# . fixed the doc/index.html to include the quoting in the mail alias. +# +# Also: +# . fixed roundup-admin so it works with transactions +# . disabled the back_anydbm module if anydbm tries to use dumbdbm +# +# Revision 1.50 2001/12/02 05:06:16 richard +# . We now use weakrefs in the Classes to keep the database reference, so +# the close() method on the database is no longer needed. +# I bumped the minimum python requirement up to 2.1 accordingly. +# . #487480 ] roundup-server +# . #487476 ] INSTALL.txt +# +# I also cleaned up the change message / post-edit stuff in the cgi client. +# There's now a clearly marked "TODO: append the change note" where I believe +# the change note should be added there. The "changes" list will obviously +# have to be modified to be a dict of the changes, or somesuch. +# +# More testing needed. +# +# Revision 1.49 2001/12/01 07:17:50 richard +# . We now have basic transaction support! Information is only written to +# the database when the commit() method is called. Only the anydbm +# backend is modified in this way - neither of the bsddb backends have been. +# The mail, admin and cgi interfaces all use commit (except the admin tool +# doesn't have a commit command, so interactive users can't commit...) +# . Fixed login/registration forwarding the user to the right page (or not, +# on a failure) +# +# Revision 1.48 2001/11/27 22:32:03 richard +# typo +# +# Revision 1.47 2001/11/26 22:55:56 richard +# Feature: +# . Added INSTANCE_NAME to configuration - used in web and email to identify +# the instance. +# . Added EMAIL_SIGNATURE_POSITION to indicate where to place the roundup +# signature info in e-mails. +# . Some more flexibility in the mail gateway and more error handling. +# . Login now takes you to the page you back to the were denied access to. +# +# Fixed: +# . Lots of bugs, thanks Roché and others on the devel mailing list! +# +# Revision 1.46 2001/11/21 03:40:54 richard +# more new property handling +# +# Revision 1.45 2001/11/12 22:51:59 jhermann +# Fixed option & associated error handling +# +# Revision 1.44 2001/11/12 22:01:06 richard +# Fixed issues with nosy reaction and author copies. +# +# Revision 1.43 2001/11/09 22:33:28 richard +# More error handling fixes. +# +# Revision 1.42 2001/11/09 10:11:08 richard +# . roundup-admin now handles all hyperdb exceptions +# +# Revision 1.41 2001/11/09 01:25:40 richard +# Should parse with python 1.5.2 now. +# +# Revision 1.40 2001/11/08 04:42:00 richard +# Expanded the already-abbreviated "initialise" and "specification" commands, +# and added a comment to the command help about the abbreviation. +# +# Revision 1.39 2001/11/08 04:29:59 richard +# roundup-admin now accepts abbreviated commands (eg. l = li = lis = list) +# [thanks Engelbert Gruber for the inspiration] +# +# Revision 1.38 2001/11/05 23:45:40 richard +# Fixed newuser_action so it sets the cookie with the unencrypted password. +# Also made it present nicer error messages (not tracebacks). +# +# Revision 1.37 2001/10/23 01:00:18 richard +# Re-enabled login and registration access after lopping them off via +# disabling access for anonymous users. +# Major re-org of the htmltemplate code, cleaning it up significantly. Fixed +# a couple of bugs while I was there. Probably introduced a couple, but +# things seem to work OK at the moment. +# +# Revision 1.36 2001/10/21 00:45:15 richard +# Added author identification to e-mail messages from roundup. +# +# Revision 1.35 2001/10/20 11:58:48 richard +# Catch errors in login - no username or password supplied. +# Fixed editing of password (Password property type) thanks Roch'e Compaan. +# +# Revision 1.34 2001/10/18 02:16:42 richard +# Oops, committed the admin script with the wierd #! line. +# Also, made the thing into a class to reduce parameter passing. +# Nuked the leading whitespace from the help __doc__ displays too. +# +# Revision 1.33 2001/10/17 23:13:19 richard +# Did a fair bit of work on the admin tool. Now has an extra command "table" +# which displays node information in a tabular format. Also fixed import and +# export so they work. Removed freshen. +# Fixed quopri usage in mailgw from bug reports. +# +# Revision 1.32 2001/10/17 06:57:29 richard +# Interactive startup blurb - need to figure how to get the version in there. +# +# Revision 1.31 2001/10/17 06:17:26 richard +# Now with readline support :) +# +# Revision 1.30 2001/10/17 06:04:00 richard +# Beginnings of an interactive mode for roundup-admin +# +# Revision 1.29 2001/10/16 03:48:01 richard +# admin tool now complains if a "find" is attempted with a non-link property. +# +# Revision 1.28 2001/10/13 00:07:39 richard +# More help in admin tool. +# +# Revision 1.27 2001/10/11 23:43:04 richard +# Implemented the comma-separated printing option in the admin tool. +# Fixed a typo (more of a vim-o actually :) in mailgw. +# +# Revision 1.26 2001/10/11 05:03:51 richard +# Marked the roundup-admin import/export as experimental since they're not fully +# operational. +# +# Revision 1.25 2001/10/10 04:12:32 richard +# The setup.cfg file is just causing pain. Away it goes. +# +# Revision 1.24 2001/10/10 03:54:57 richard +# Added database importing and exporting through CSV files. +# Uses the csv module from object-craft for exporting if it's available. +# Requires the csv module for importing. +# +# Revision 1.23 2001/10/09 23:36:25 richard +# Spit out command help if roundup-admin command doesn't get an argument. +# # Revision 1.22 2001/10/09 07:25:59 richard # Added the Password property type. See "pydoc roundup.password" for # implementation details. Have updated some of the documentation too.