Skip to content

Commit 3dfb7cb

Browse files
author
Richard Jones
committed
. [SF#517734] web header customisation is obscure
1 parent a53bb07 commit 3dfb7cb

File tree

8 files changed

+301
-107
lines changed

8 files changed

+301
-107
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Feature:
2525
the id, name and description for the priority class. The description
2626
field won't exist in most installations, but it will be added to the
2727
default templates.
28+
. #517734 ] web header customisation is obscure
2829

2930
Fixed:
3031
. Clean up mail handling, multipart handling.

roundup/cgi_client.py

Lines changed: 125 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: cgi_client.py,v 1.112 2002-03-12 22:52:26 richard Exp $
18+
# $Id: cgi_client.py,v 1.113 2002-03-14 23:59:24 richard Exp $
1919

2020
__doc__ = """
2121
WWW request handler (also used in the stand-alone server).
2222
"""
2323

24-
import os, cgi, StringIO, urlparse, re, traceback, mimetypes
24+
import os, cgi, StringIO, urlparse, re, traceback, mimetypes, urllib
2525
import binascii, Cookie, time, random
2626

2727
import roundupdb, htmltemplate, date, hyperdb, password
@@ -58,7 +58,7 @@ def __init__(self, instance, request, env, form=None):
5858
port = self.env['SERVER_PORT']
5959
if port != '80': machine = machine + ':' + port
6060
self.base = urlparse.urlunparse(('http', env['HTTP_HOST'], url,
61-
None, None, None))
61+
None, None, None))
6262

6363
if form is None:
6464
self.form = cgi.FieldStorage(environ=env)
@@ -77,8 +77,8 @@ def getuid(self):
7777
def header(self, headers=None):
7878
'''Put up the appropriate header.
7979
'''
80-
if headers is None:
81-
headers = {'Content-Type':'text/html'}
80+
if headers is None:
81+
headers = {'Content-Type':'text/html'}
8282
if not headers.has_key('Content-Type'):
8383
headers['Content-Type'] = 'text/html'
8484
self.request.send_response(200)
@@ -107,36 +107,114 @@ def header(self, headers=None):
107107
108108
</script>
109109
'''
110+
def make_index_link(self, name):
111+
'''Turn a configuration entry into a hyperlink...
112+
'''
113+
# get the link label and spec
114+
spec = getattr(self.instance, name+'_INDEX')
115+
116+
d = {}
117+
d[':sort'] = ','.join(map(urllib.quote, spec['SORT']))
118+
d[':group'] = ','.join(map(urllib.quote, spec['GROUP']))
119+
d[':filter'] = ','.join(map(urllib.quote, spec['FILTER']))
120+
d[':columns'] = ','.join(map(urllib.quote, spec['COLUMNS']))
121+
122+
# snarf the filterspec
123+
filterspec = spec['FILTERSPEC'].copy()
124+
125+
# now format the filterspec
126+
for k, l in filterspec.items():
127+
# fix up the assignedto if needed
128+
if k == 'assignedto' and l is None:
129+
l = [self.db.user.lookup(self.user)]
130+
131+
# add
132+
d[urllib.quote(k)] = ','.join(map(urllib.quote, l))
133+
134+
# finally, format the URL
135+
return '<a href="%s?%s">%s</a>'%(spec['CLASS'],
136+
'&'.join([k+'='+v for k,v in d.items()]), spec['LABEL'])
137+
110138

111139
def pagehead(self, title, message=None):
140+
'''Display the page heading, with information about the tracker and
141+
links to more information
142+
'''
143+
144+
# include any important message
112145
if message is not None:
113146
message = _('<div class="system-msg">%(message)s</div>')%locals()
114147
else:
115148
message = ''
149+
150+
# style sheet (CSS)
116151
style = open(os.path.join(self.instance.TEMPLATES, 'style.css')).read()
152+
153+
# figure who the user is
117154
user_name = self.user or ''
118-
if self.user == 'admin':
119-
admin_links = _(' | <a href="list_classes">Class List</a>' \
120-
' | <a href="user">User List</a>' \
121-
' | <a href="newuser">Add User</a>')
122-
else:
123-
admin_links = ''
124-
if self.user not in (None, 'anonymous'):
155+
if user_name not in ('', 'anonymous'):
125156
userid = self.db.user.lookup(self.user)
157+
else:
158+
userid = None
159+
160+
# figure all the header links
161+
if hasattr(self.instance, 'HEADER_INDEX_LINKS'):
162+
links = []
163+
for name in self.instance.HEADER_INDEX_LINKS:
164+
spec = getattr(self.instance, name + '_INDEX')
165+
# skip if we need to fill in the logged-in user id there's
166+
# no user logged in
167+
if (spec['FILTERSPEC'].has_key('assignedto') and
168+
spec['FILTERSPEC']['assignedto'] is None and
169+
userid is None):
170+
continue
171+
links.append(self.make_index_link(name))
172+
else:
173+
# no config spec - hard-code
174+
links = [
175+
_('All <a href="issue?status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>'),
176+
_('Unassigned <a href="issue?assignedto=-1&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status,assignedto&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>')
177+
]
178+
179+
# if they're logged in, include links to their information, and the
180+
# ability to add an issue
181+
if user_name not in ('', 'anonymous'):
126182
user_info = _('''
127-
<a href="issue?assignedto=%(userid)s&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:filter=status,assignedto&:sort=-activity&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">My Issues</a> |
128183
<a href="user%(userid)s">My Details</a> | <a href="logout">Logout</a>
129184
''')%locals()
185+
186+
# figure the "add class" links
187+
if hasattr(self.instance, 'HEADER_ADD_LINKS'):
188+
classes = self.instance.HEADER_ADD_LINKS
189+
else:
190+
classes = ['issue']
191+
l = []
192+
for class_name in classes:
193+
cap_class = class_name.capitalize()
194+
links.append(_('Add <a href="new%(class_name)s">'
195+
'%(cap_class)s</a>')%locals())
196+
197+
# if there's no config header link spec, force a user link here
198+
if not hasattr(self.instance, 'HEADER_INDEX_LINKS'):
199+
links.append(_('<a href="issue?assignedto=%(userid)s&status=-1,unread,chatting,open,pending&:filter=status,resolution,assignedto&:sort=-activity&:columns=id,activity,status,resolution,title,creator&:group=type&show_customization=1">My Issues</a>')%locals())
130200
else:
131201
user_info = _('<a href="login">Login</a>')
132-
if self.user is not None:
133-
add_links = _('''
134-
| Add
135-
<a href="newissue">Issue</a>
136-
''')
137-
else:
138202
add_links = ''
203+
204+
# if the user is admin, include admin links
205+
admin_links = ''
206+
if user_name == 'admin':
207+
links.append(_('<a href="list_classes">Class List</a>'))
208+
links.append(_('<a href="user">User List</a>'))
209+
links.append(_('<a href="newuser">Add User</a>'))
210+
211+
# now we have all the links, join 'em
212+
links = '\n | '.join(links)
213+
214+
# include the javascript bit
139215
global_javascript = self.global_javascript%self.__dict__
216+
217+
# finally, format the header
140218
self.write(_('''<html><head>
141219
<title>%(title)s</title>
142220
<style type="text/css">%(style)s</style>
@@ -148,12 +226,7 @@ def pagehead(self, title, message=None):
148226
<tr class="location-bar"><td><big><strong>%(title)s</strong></big></td>
149227
<td align=right valign=bottom>%(user_name)s</td></tr>
150228
<tr class="location-bar">
151-
<td align=left>All
152-
<a href="issue?status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>
153-
| Unassigned
154-
<a href="issue?assignedto=-1&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status,assignedto&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>
155-
%(add_links)s
156-
%(admin_links)s</td>
229+
<td align=left>%(links)s</td>
157230
<td align=right>%(user_info)s</td>
158231
</table>
159232
''')%locals())
@@ -247,34 +320,44 @@ def customization_widget(self):
247320

248321
return visible
249322

323+
# TODO: make this go away some day...
250324
default_index_sort = ['-activity']
251325
default_index_group = ['priority']
252326
default_index_filter = ['status']
253327
default_index_columns = ['id','activity','title','status','assignedto']
254328
default_index_filterspec = {'status': ['1', '2', '3', '4', '5', '6', '7']}
329+
255330
def index(self):
256-
''' put up an index
331+
''' put up an index - no class specified
257332
'''
258-
self.classname = 'issue'
259333
# see if the web has supplied us with any customisation info
260334
defaults = 1
261335
for key in ':sort', ':group', ':filter', ':columns':
262336
if self.form.has_key(key):
263337
defaults = 0
264338
break
265339
if defaults:
266-
# no info supplied - use the defaults
267-
sort = self.default_index_sort
268-
group = self.default_index_group
269-
filter = self.default_index_filter
270-
columns = self.default_index_columns
271-
filterspec = self.default_index_filterspec
340+
# try the instance config first
341+
if hasattr(self.instance, 'DEFAULT_INDEX_CLASS'):
342+
self.classname = self.instance.DEFAULT_INDEX_CLASS
343+
sort = self.instance.DEFAULT_INDEX_SORT
344+
group = self.instance.DEFAULT_INDEX_GROUP
345+
filter = self.instance.DEFAULT_INDEX_FILTER
346+
columns = self.instance.DEFAULT_INDEX_COLUMNS
347+
filterspec = self.instance.DEFAULT_INDEX_FILTERSPEC
348+
349+
else:
350+
# nope - fall back on the old way of doing it
351+
self.classname = 'issue'
352+
sort = self.default_index_sort
353+
group = self.default_index_group
354+
filter = self.default_index_filter
355+
columns = self.default_index_columns
356+
filterspec = self.default_index_filterspec
272357
else:
273-
sort = self.index_arg(':sort')
274-
group = self.index_arg(':group')
275-
filter = self.index_arg(':filter')
276-
columns = self.index_arg(':columns')
277-
filterspec = self.index_filterspec(filter)
358+
# make list() extract the info from the CGI environ
359+
self.classname = 'issue'
360+
sort = group = filter = columns = filterspec = None
278361
return self.list(columns=columns, filter=filter, group=group,
279362
sort=sort, filterspec=filterspec)
280363

@@ -536,7 +619,7 @@ def _createnode(self):
536619

537620
# set status to 'unread' if not specified - a status of '- no
538621
# selection -' doesn't make sense
539-
if not props.has_key('status'):
622+
if not props.has_key('status') and cl.getprops().has_key('status'):
540623
try:
541624
unread_id = self.db.status.lookup('unread')
542625
except KeyError:
@@ -1192,60 +1275,6 @@ class ExtendedClient(Client):
11921275
default_index_columns = ['activity','status','title','assignedto']
11931276
default_index_filterspec = {'status': ['1', '2', '3', '4', '5', '6', '7']}
11941277

1195-
def pagehead(self, title, message=None):
1196-
if message is not None:
1197-
message = _('<div class="system-msg">%(message)s</div>')%locals()
1198-
else:
1199-
message = ''
1200-
style = open(os.path.join(self.instance.TEMPLATES, 'style.css')).read()
1201-
user_name = self.user or ''
1202-
if self.user == 'admin':
1203-
admin_links = _(' | <a href="list_classes">Class List</a>' \
1204-
' | <a href="user">User List</a>' \
1205-
' | <a href="newuser">Add User</a>')
1206-
else:
1207-
admin_links = ''
1208-
if self.user not in (None, 'anonymous'):
1209-
userid = self.db.user.lookup(self.user)
1210-
user_info = _('''
1211-
<a href="issue?assignedto=%(userid)s&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:filter=status,assignedto&:sort=-activity&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">My Issues</a> |
1212-
<a href="support?assignedto=%(userid)s&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:filter=status,assignedto&:sort=-activity&:columns=id,activity,status,title,assignedto&:group=customername&show_customization=1">My Support</a> |
1213-
<a href="user%(userid)s">My Details</a> | <a href="logout">Logout</a>
1214-
''')%locals()
1215-
else:
1216-
user_info = _('<a href="login">Login</a>')
1217-
if self.user is not None:
1218-
add_links = _('''
1219-
| Add
1220-
<a href="newissue">Issue</a>,
1221-
<a href="newsupport">Support</a>,
1222-
''')
1223-
else:
1224-
add_links = ''
1225-
global_javascript = self.global_javascript%self.__dict__
1226-
self.write(_('''<html><head>
1227-
<title>%(title)s</title>
1228-
<style type="text/css">%(style)s</style>
1229-
</head>
1230-
%(global_javascript)s
1231-
<body bgcolor=#ffffff>
1232-
%(message)s
1233-
<table width=100%% border=0 cellspacing=0 cellpadding=2>
1234-
<tr class="location-bar"><td><big><strong>%(title)s</strong></big></td>
1235-
<td align=right valign=bottom>%(user_name)s</td></tr>
1236-
<tr class="location-bar">
1237-
<td align=left>All
1238-
<a href="issue?status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=activity&:filter=status&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>,
1239-
<a href="support?status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=activity&:filter=status&:columns=id,activity,status,title,assignedto&:group=customername&show_customization=1">Support</a>
1240-
| Unassigned
1241-
<a href="issue?assignedto=-1&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status,assignedto&:columns=id,activity,status,title,assignedto&:group=priority&show_customization=1">Issues</a>,
1242-
<a href="support?assignedto=-1&status=-1,unread,deferred,chatting,need-eg,in-progress,testing,done-cbb&:sort=-activity&:filter=status,assignedto&:columns=id,activity,status,title,assignedto&:group=customername&show_customization=1">Support</a>
1243-
%(add_links)s
1244-
%(admin_links)s</td>
1245-
<td align=right>%(user_info)s</td>
1246-
</table>
1247-
''')%locals())
1248-
12491278
def parsePropsFromForm(db, cl, form, nodeid=0):
12501279
'''Pull properties for the given class out of the form.
12511280
'''
@@ -1327,6 +1356,9 @@ def parsePropsFromForm(db, cl, form, nodeid=0):
13271356

13281357
#
13291358
# $Log: not supported by cvs2svn $
1359+
# Revision 1.112 2002/03/12 22:52:26 richard
1360+
# more pychecker warnings removed
1361+
#
13301362
# Revision 1.111 2002/02/25 04:32:21 richard
13311363
# ahem
13321364
#

roundup/mailgw.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class node. Any parts of other types are each stored in separate files
7373
an exception, the original message is bounced back to the sender with the
7474
explanatory message given in the exception.
7575
76-
$Id: mailgw.py,v 1.65 2002-02-15 00:13:38 richard Exp $
76+
$Id: mailgw.py,v 1.66 2002-03-14 23:59:24 richard Exp $
7777
'''
7878

7979

@@ -523,8 +523,8 @@ def handle_message(self, message):
523523
# required body parts.
524524
# ACTION: Not handleable as the content is encrypted.
525525
# multipart/related (rfc 1872, 2112, 2387):
526-
# The Multipart/Related content-type addresses the MIME representation
527-
# of compound objects.
526+
# The Multipart/Related content-type addresses the MIME
527+
# representation of compound objects.
528528
# ACTION: Default. If we are lucky there is a text/plain.
529529
# TODO: One should use the start part and look for an Alternative
530530
# that is text/plain.
@@ -803,6 +803,11 @@ def parseContent(content, blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
803803

804804
#
805805
# $Log: not supported by cvs2svn $
806+
# Revision 1.65 2002/02/15 00:13:38 richard
807+
# . #503204 ] mailgw needs a default class
808+
# - partially done - the setting of additional properties can wait for a
809+
# better configuration system.
810+
#
806811
# Revision 1.64 2002/02/14 23:46:02 richard
807812
# . #516883 ] mail interface + ANONYMOUS_REGISTER
808813
#

roundup/scripts/roundup_admin.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#! /usr/bin/env python
2-
#
31
# Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
42
# This module is free software, and you may redistribute it and/or modify
53
# under the same terms as Python, so long as this copyright message and
@@ -16,7 +14,7 @@
1614
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1715
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1816
#
19-
# $Id: roundup_admin.py,v 1.2 2002-01-29 20:07:15 jhermann Exp $
17+
# $Id: roundup_admin.py,v 1.3 2002-03-14 23:59:24 richard Exp $
2018

2119
# python version check
2220
from roundup import version_check
@@ -36,6 +34,9 @@ def run():
3634

3735
#
3836
# $Log: not supported by cvs2svn $
37+
# Revision 1.2 2002/01/29 20:07:15 jhermann
38+
# Conversion to generated script stubs
39+
#
3940
# Revision 1.1 2002/01/29 19:53:08 jhermann
4041
# Moved scripts from top-level dir to roundup.scripts subpackage
4142
#

0 commit comments

Comments
 (0)