From f1ad79ad4370f171e88b1beafc20bea5c397916a Mon Sep 17 00:00:00 2001 From: richard Date: Thu, 14 Mar 2002 23:59:24 +0000 Subject: [PATCH] . #517734 ] web header customisation is obscure git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@673 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 1 + roundup/cgi_client.py | 218 ++++++++++-------- roundup/mailgw.py | 11 +- roundup/scripts/roundup_admin.py | 7 +- roundup/scripts/roundup_mailgw.py | 7 +- roundup/scripts/roundup_server.py | 7 +- roundup/templates/classic/instance_config.py | 60 ++++- roundup/templates/extended/instance_config.py | 97 +++++++- 8 files changed, 301 insertions(+), 107 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 7713218..0800add 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -25,6 +25,7 @@ Feature: the id, name and description for the priority class. The description field won't exist in most installations, but it will be added to the default templates. + . #517734 ] web header customisation is obscure Fixed: . Clean up mail handling, multipart handling. diff --git a/roundup/cgi_client.py b/roundup/cgi_client.py index a79f86a..e6a69c2 100644 --- a/roundup/cgi_client.py +++ b/roundup/cgi_client.py @@ -15,13 +15,13 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: cgi_client.py,v 1.112 2002-03-12 22:52:26 richard Exp $ +# $Id: cgi_client.py,v 1.113 2002-03-14 23:59:24 richard Exp $ __doc__ = """ WWW request handler (also used in the stand-alone server). """ -import os, cgi, StringIO, urlparse, re, traceback, mimetypes +import os, cgi, StringIO, urlparse, re, traceback, mimetypes, urllib import binascii, Cookie, time, random import roundupdb, htmltemplate, date, hyperdb, password @@ -58,7 +58,7 @@ class Client: port = self.env['SERVER_PORT'] if port != '80': machine = machine + ':' + port self.base = urlparse.urlunparse(('http', env['HTTP_HOST'], url, - None, None, None)) + None, None, None)) if form is None: self.form = cgi.FieldStorage(environ=env) @@ -77,8 +77,8 @@ class Client: def header(self, headers=None): '''Put up the appropriate header. ''' - if headers is None: - headers = {'Content-Type':'text/html'} + if headers is None: + headers = {'Content-Type':'text/html'} if not headers.has_key('Content-Type'): headers['Content-Type'] = 'text/html' self.request.send_response(200) @@ -107,36 +107,114 @@ function help_window(helpurl, width, height) { ''' + def make_index_link(self, name): + '''Turn a configuration entry into a hyperlink... + ''' + # get the link label and spec + spec = getattr(self.instance, name+'_INDEX') + + d = {} + d[':sort'] = ','.join(map(urllib.quote, spec['SORT'])) + d[':group'] = ','.join(map(urllib.quote, spec['GROUP'])) + d[':filter'] = ','.join(map(urllib.quote, spec['FILTER'])) + d[':columns'] = ','.join(map(urllib.quote, spec['COLUMNS'])) + + # snarf the filterspec + filterspec = spec['FILTERSPEC'].copy() + + # now format the filterspec + for k, l in filterspec.items(): + # fix up the assignedto if needed + if k == 'assignedto' and l is None: + l = [self.db.user.lookup(self.user)] + + # add + d[urllib.quote(k)] = ','.join(map(urllib.quote, l)) + + # finally, format the URL + return '%s'%(spec['CLASS'], + '&'.join([k+'='+v for k,v in d.items()]), spec['LABEL']) + def pagehead(self, title, message=None): + '''Display the page heading, with information about the tracker and + links to more information + ''' + + # include any important message if message is not None: message = _('
%(message)s
')%locals() else: message = '' + + # style sheet (CSS) style = open(os.path.join(self.instance.TEMPLATES, 'style.css')).read() + + # figure who the user is user_name = self.user or '' - if self.user == 'admin': - admin_links = _(' | Class List' \ - ' | User List' \ - ' | Add User') - else: - admin_links = '' - if self.user not in (None, 'anonymous'): + if user_name not in ('', 'anonymous'): userid = self.db.user.lookup(self.user) + else: + userid = None + + # figure all the header links + if hasattr(self.instance, 'HEADER_INDEX_LINKS'): + links = [] + for name in self.instance.HEADER_INDEX_LINKS: + spec = getattr(self.instance, name + '_INDEX') + # skip if we need to fill in the logged-in user id there's + # no user logged in + if (spec['FILTERSPEC'].has_key('assignedto') and + spec['FILTERSPEC']['assignedto'] is None and + userid is None): + continue + links.append(self.make_index_link(name)) + else: + # no config spec - hard-code + links = [ + _('All Issues'), + _('Unassigned Issues') + ] + + # if they're logged in, include links to their information, and the + # ability to add an issue + if user_name not in ('', 'anonymous'): user_info = _(''' -My Issues | My Details | Logout ''')%locals() + + # figure the "add class" links + if hasattr(self.instance, 'HEADER_ADD_LINKS'): + classes = self.instance.HEADER_ADD_LINKS + else: + classes = ['issue'] + l = [] + for class_name in classes: + cap_class = class_name.capitalize() + links.append(_('Add ' + '%(cap_class)s')%locals()) + + # if there's no config header link spec, force a user link here + if not hasattr(self.instance, 'HEADER_INDEX_LINKS'): + links.append(_('My Issues')%locals()) else: user_info = _('Login') - if self.user is not None: - add_links = _(''' -| Add -Issue -''') - else: add_links = '' + + # if the user is admin, include admin links + admin_links = '' + if user_name == 'admin': + links.append(_('Class List')) + links.append(_('User List')) + links.append(_('Add User')) + + # now we have all the links, join 'em + links = '\n | '.join(links) + + # include the javascript bit global_javascript = self.global_javascript%self.__dict__ + + # finally, format the header self.write(_(''' %(title)s @@ -148,12 +226,7 @@ function help_window(helpurl, width, height) { %(title)s %(user_name)s -All -Issues -| Unassigned -Issues -%(add_links)s -%(admin_links)s +%(links)s %(user_info)s ''')%locals()) @@ -247,15 +320,16 @@ function help_window(helpurl, width, height) { return visible + # TODO: make this go away some day... default_index_sort = ['-activity'] default_index_group = ['priority'] default_index_filter = ['status'] default_index_columns = ['id','activity','title','status','assignedto'] default_index_filterspec = {'status': ['1', '2', '3', '4', '5', '6', '7']} + def index(self): - ''' put up an index + ''' put up an index - no class specified ''' - self.classname = 'issue' # see if the web has supplied us with any customisation info defaults = 1 for key in ':sort', ':group', ':filter', ':columns': @@ -263,18 +337,27 @@ function help_window(helpurl, width, height) { defaults = 0 break if defaults: - # no info supplied - use the defaults - sort = self.default_index_sort - group = self.default_index_group - filter = self.default_index_filter - columns = self.default_index_columns - filterspec = self.default_index_filterspec + # try the instance config first + if hasattr(self.instance, 'DEFAULT_INDEX_CLASS'): + self.classname = self.instance.DEFAULT_INDEX_CLASS + sort = self.instance.DEFAULT_INDEX_SORT + group = self.instance.DEFAULT_INDEX_GROUP + filter = self.instance.DEFAULT_INDEX_FILTER + columns = self.instance.DEFAULT_INDEX_COLUMNS + filterspec = self.instance.DEFAULT_INDEX_FILTERSPEC + + else: + # nope - fall back on the old way of doing it + self.classname = 'issue' + sort = self.default_index_sort + group = self.default_index_group + filter = self.default_index_filter + columns = self.default_index_columns + filterspec = self.default_index_filterspec else: - sort = self.index_arg(':sort') - group = self.index_arg(':group') - filter = self.index_arg(':filter') - columns = self.index_arg(':columns') - filterspec = self.index_filterspec(filter) + # make list() extract the info from the CGI environ + self.classname = 'issue' + sort = group = filter = columns = filterspec = None return self.list(columns=columns, filter=filter, group=group, sort=sort, filterspec=filterspec) @@ -536,7 +619,7 @@ function help_window(helpurl, width, height) { # set status to 'unread' if not specified - a status of '- no # selection -' doesn't make sense - if not props.has_key('status'): + if not props.has_key('status') and cl.getprops().has_key('status'): try: unread_id = self.db.status.lookup('unread') except KeyError: @@ -1192,60 +1275,6 @@ class ExtendedClient(Client): default_index_columns = ['activity','status','title','assignedto'] default_index_filterspec = {'status': ['1', '2', '3', '4', '5', '6', '7']} - def pagehead(self, title, message=None): - if message is not None: - message = _('
%(message)s
')%locals() - else: - message = '' - style = open(os.path.join(self.instance.TEMPLATES, 'style.css')).read() - user_name = self.user or '' - if self.user == 'admin': - admin_links = _(' | Class List' \ - ' | User List' \ - ' | Add User') - else: - admin_links = '' - if self.user not in (None, 'anonymous'): - userid = self.db.user.lookup(self.user) - user_info = _(''' -My Issues | -My Support | -My Details | Logout -''')%locals() - else: - user_info = _('Login') - if self.user is not None: - add_links = _(''' -| Add -Issue, -Support, -''') - else: - add_links = '' - global_javascript = self.global_javascript%self.__dict__ - self.write(_(''' -%(title)s - - -%(global_javascript)s - -%(message)s - - - - - - -
%(title)s%(user_name)s
All -Issues, -Support -| Unassigned -Issues, -Support -%(add_links)s -%(admin_links)s%(user_info)s
-''')%locals()) - def parsePropsFromForm(db, cl, form, nodeid=0): '''Pull properties for the given class out of the form. ''' @@ -1327,6 +1356,9 @@ def parsePropsFromForm(db, cl, form, nodeid=0): # # $Log: not supported by cvs2svn $ +# Revision 1.112 2002/03/12 22:52:26 richard +# more pychecker warnings removed +# # Revision 1.111 2002/02/25 04:32:21 richard # ahem # diff --git a/roundup/mailgw.py b/roundup/mailgw.py index 3898051..3dbf229 100644 --- a/roundup/mailgw.py +++ b/roundup/mailgw.py @@ -73,7 +73,7 @@ are calling the create() method to create a new node). If an auditor raises an exception, the original message is bounced back to the sender with the explanatory message given in the exception. -$Id: mailgw.py,v 1.65 2002-02-15 00:13:38 richard Exp $ +$Id: mailgw.py,v 1.66 2002-03-14 23:59:24 richard Exp $ ''' @@ -523,8 +523,8 @@ Unknown address: %s # required body parts. # ACTION: Not handleable as the content is encrypted. # multipart/related (rfc 1872, 2112, 2387): - # The Multipart/Related content-type addresses the MIME representation - # of compound objects. + # The Multipart/Related content-type addresses the MIME + # representation of compound objects. # ACTION: Default. If we are lucky there is a text/plain. # TODO: One should use the start part and look for an Alternative # that is text/plain. @@ -803,6 +803,11 @@ def parseContent(content, blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'), # # $Log: not supported by cvs2svn $ +# Revision 1.65 2002/02/15 00:13:38 richard +# . #503204 ] mailgw needs a default class +# - partially done - the setting of additional properties can wait for a +# better configuration system. +# # Revision 1.64 2002/02/14 23:46:02 richard # . #516883 ] mail interface + ANONYMOUS_REGISTER # diff --git a/roundup/scripts/roundup_admin.py b/roundup/scripts/roundup_admin.py index 9596fe0..92d9795 100644 --- a/roundup/scripts/roundup_admin.py +++ b/roundup/scripts/roundup_admin.py @@ -1,5 +1,3 @@ -#! /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 # under the same terms as Python, so long as this copyright message and @@ -16,7 +14,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: roundup_admin.py,v 1.2 2002-01-29 20:07:15 jhermann Exp $ +# $Id: roundup_admin.py,v 1.3 2002-03-14 23:59:24 richard Exp $ # python version check from roundup import version_check @@ -36,6 +34,9 @@ if __name__ == '__main__': # # $Log: not supported by cvs2svn $ +# Revision 1.2 2002/01/29 20:07:15 jhermann +# Conversion to generated script stubs +# # Revision 1.1 2002/01/29 19:53:08 jhermann # Moved scripts from top-level dir to roundup.scripts subpackage # diff --git a/roundup/scripts/roundup_mailgw.py b/roundup/scripts/roundup_mailgw.py index faec3d9..65f8a00 100644 --- a/roundup/scripts/roundup_mailgw.py +++ b/roundup/scripts/roundup_mailgw.py @@ -1,5 +1,3 @@ -#! /usr/bin/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 # under the same terms as Python, so long as this copyright message and @@ -16,7 +14,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: roundup_mailgw.py,v 1.2 2002-01-29 20:07:15 jhermann Exp $ +# $Id: roundup_mailgw.py,v 1.3 2002-03-14 23:59:24 richard Exp $ # python version check from roundup import version_check @@ -181,6 +179,9 @@ if __name__ == '__main__': # # $Log: not supported by cvs2svn $ +# Revision 1.2 2002/01/29 20:07:15 jhermann +# Conversion to generated script stubs +# # Revision 1.1 2002/01/29 19:53:08 jhermann # Moved scripts from top-level dir to roundup.scripts subpackage # diff --git a/roundup/scripts/roundup_server.py b/roundup/scripts/roundup_server.py index 8cf2704..cd18a80 100644 --- a/roundup/scripts/roundup_server.py +++ b/roundup/scripts/roundup_server.py @@ -1,5 +1,3 @@ -#!/usr/bin/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 # under the same terms as Python, so long as this copyright message and @@ -18,7 +16,7 @@ # """ HTTP Server that serves roundup. -$Id: roundup_server.py,v 1.4 2002-02-21 07:02:54 richard Exp $ +$Id: roundup_server.py,v 1.5 2002-03-14 23:59:24 richard Exp $ """ # python version check @@ -249,6 +247,9 @@ if __name__ == '__main__': # # $Log: not supported by cvs2svn $ +# Revision 1.4 2002/02/21 07:02:54 richard +# The correct var is "HTTP_HOST" +# # Revision 1.3 2002/02/21 06:57:39 richard # . Added popup help for classes using the classhelp html template function. # - add diff --git a/roundup/templates/classic/instance_config.py b/roundup/templates/classic/instance_config.py index 0fe747d..d6e7622 100644 --- a/roundup/templates/classic/instance_config.py +++ b/roundup/templates/classic/instance_config.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: instance_config.py,v 1.12 2002-02-15 00:13:38 richard Exp $ +# $Id: instance_config.py,v 1.13 2002-03-14 23:59:24 richard Exp $ MAIL_DOMAIN=MAILHOST=HTTP_HOST=None HTTP_PORT=0 @@ -89,8 +89,66 @@ EMAIL_SIGNATURE_POSITION = 'bottom' MAIL_DEFAULT_CLASS = 'issue' # use "issue" class by default #MAIL_DEFAULT_CLASS = '' # disable (or just comment the var out) +# Define what index links are available in the header, and what their +# labels are. Each key is used to look up one of the index specifications +# below - so 'DEFAULT' will use 'DEFAULT_INDEX'. +# Where the FILTERSPEC has 'assignedto' with a value of None, it will be +# replaced by the id of the logged-in user. +HEADER_INDEX_LINKS = ['DEFAULT', 'UNASSIGNED', 'USER'] + +# list the classes that users are able to add nodes to +HEADER_ADD_LINKS = ['issue'] + +# Now the DEFAULT display specification. TODO: describe format +DEFAULT_INDEX = { + 'LABEL': 'All Issues', + 'CLASS': 'issue', + 'SORT': ['-activity'], + 'GROUP': ['priority'], + 'FILTER': ['status'], + 'COLUMNS': ['id','activity','title','creator','assignedto'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + }, +} + +# The "unsassigned issues" index +UNASSIGNED_INDEX = { + 'LABEL': 'Unassigned Issues', + 'CLASS': 'issue', + 'SORT': ['-activity'], + 'GROUP': ['priority'], + 'FILTER': ['status', 'assignedto'], + 'COLUMNS': ['id','activity','title','creator','status'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + 'assignedto': ['-1'], + }, +} + +# The "my issues" index -- note that the user's id will replace the None +# valud of the "assignedto" filterspec +USER_INDEX = { + 'LABEL': 'My Issues', + 'CLASS': 'issue', + 'SORT': ['-activity'], + 'GROUP': ['priority'], + 'FILTER': ['status', 'assignedto'], + 'COLUMNS': ['id','activity','title','creator','status'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + 'assignedto': None, + }, +} + + # # $Log: not supported by cvs2svn $ +# Revision 1.12 2002/02/15 00:13:38 richard +# . #503204 ] mailgw needs a default class +# - partially done - the setting of additional properties can wait for a +# better configuration system. +# # Revision 1.11 2002/02/14 23:46:02 richard # . #516883 ] mail interface + ANONYMOUS_REGISTER # diff --git a/roundup/templates/extended/instance_config.py b/roundup/templates/extended/instance_config.py index 89f3268..b737312 100644 --- a/roundup/templates/extended/instance_config.py +++ b/roundup/templates/extended/instance_config.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: instance_config.py,v 1.12 2002-02-15 00:13:38 richard Exp $ +# $Id: instance_config.py,v 1.13 2002-03-14 23:59:24 richard Exp $ MAIL_DOMAIN=MAILHOST=HTTP_HOST=None HTTP_PORT=0 @@ -89,8 +89,103 @@ EMAIL_SIGNATURE_POSITION = 'bottom' MAIL_DEFAULT_CLASS = 'issue' # use "issue" class by default #MAIL_DEFAULT_CLASS = '' # disable (or just comment the var out) +# Define what index links are available in the header, and what their +# labels are. Each key is used to look up one of the index specifications +# below - so 'DEFAULT' will use 'DEFAULT_INDEX'. +# Where the FILTERSPEC has 'assignedto' with a value of None, it will be +# replaced by the id of the logged-in user. +HEADER_INDEX_LINKS = ['DEFAULT', 'ALL_SUPPORT', 'UNASSIGNED_ISSUE', + 'UNASSIGNED_SUPPORT', 'MY_ISSUE', 'MY_SUPPORT'] + +# list the classes that users are able to add nodes to +HEADER_ADD_LINKS = ['issue', 'support'] + +# Now the DEFAULT display specifications. TODO: describe format +DEFAULT_INDEX = { + 'LABEL': 'All Issues', + 'CLASS': 'issue', + 'SORT': ['-activity'], + 'GROUP': ['priority'], + 'FILTER': ['status'], + 'COLUMNS': ['id','activity','title','creator','assignedto'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + }, +} + +ALL_SUPPORT_INDEX = { + 'LABEL': 'All Support', + 'CLASS': 'support', + 'SORT': ['-activity'], + 'GROUP': ['customername'], + 'FILTER': ['status'], + 'COLUMNS': ['id','activity','title','creator','assignedto'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + }, +} + +# The "unsassigned issues" indexes +UNASSIGNED_ISSUE_INDEX = { + 'LABEL': 'Unassigned Issues', + 'CLASS': 'issue', + 'SORT': ['-activity'], + 'GROUP': ['priority'], + 'FILTER': ['status', 'assignedto'], + 'COLUMNS': ['id','activity','title','creator','status'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + 'assignedto': ['-1'], + }, +} +UNASSIGNED_SUPPORT_INDEX = { + 'LABEL': 'Unassigned Support', + 'CLASS': 'support', + 'SORT': ['-activity'], + 'GROUP': ['customername'], + 'FILTER': ['status', 'assignedto'], + 'COLUMNS': ['id','activity','title','creator','status'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + 'assignedto': ['-1'], + }, +} + +# The "my issues" indexes -- note that the user's id will replace the None +# valud of the "assignedto" filterspec +MY_ISSUE_INDEX = { + 'LABEL': 'My Issues', + 'CLASS': 'issue', + 'SORT': ['-activity'], + 'GROUP': ['priority'], + 'FILTER': ['status', 'assignedto'], + 'COLUMNS': ['id','activity','title','creator','status'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + 'assignedto': None, + }, +} + +MY_SUPPORT_INDEX = { + 'LABEL': 'My Support', + 'CLASS': 'support', + 'SORT': ['-activity'], + 'GROUP': ['customername'], + 'FILTER': ['status', 'assignedto'], + 'COLUMNS': ['id','activity','title','creator','status'], + 'FILTERSPEC': { + 'status': ['-1', '1', '2', '3', '4', '5', '6', '7'], + 'assignedto': None, + }, +} + # # $Log: not supported by cvs2svn $ +# Revision 1.12 2002/02/15 00:13:38 richard +# . #503204 ] mailgw needs a default class +# - partially done - the setting of additional properties can wait for a +# better configuration system. +# # Revision 1.11 2002/02/14 23:46:02 richard # . #516883 ] mail interface + ANONYMOUS_REGISTER # -- 2.30.2