Skip to content

Commit 1919c82

Browse files
author
Richard Jones
committed
Fixes for SourceForge tracker bugs.
- remember the change note on bad submissions [SF#625989] - highlight required form fields [SF#625989] add a couple of examples to the customisation doc too. un-reversed the message list so first messages appear first. fixed the messages header (post introduction of "remove")
1 parent c923ee0 commit 1919c82

File tree

4 files changed

+200
-9
lines changed

4 files changed

+200
-9
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ are given with the most recent entry first.
88
- added CGI :remove:<propname> and :add:<propname> which specify item ids to
99
remove / add in <propname> multilink.
1010
- bugfix in boolean templating
11+
- remember the change note on bad submissions (sf bug 625989)
12+
- highlight required form fields (sf bug 625989)
1113

1214

1315
2002-10-16 0.5.1

doc/customizing.txt

Lines changed: 179 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Customising Roundup
33
===================
44

5-
:Version: $Revision: 1.58 $
5+
:Version: $Revision: 1.59 $
66

77
.. This document borrows from the ZopeBook section on ZPT. The original is at:
88
http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -2579,6 +2579,184 @@ able to give a summary of the total time spent on a particular issue.
25792579
example - then you'll need to restart that to pick up the code changes.
25802580
When that's done, you'll be able to use the new time logging interface.
25812581

2582+
Using a UN*X passwd file as the user database
2583+
---------------------------------------------
2584+
2585+
On some systems, the primary store of users is the UN*X passwd file. It holds
2586+
information on users such as their username, real name, password and primary
2587+
user group.
2588+
2589+
Roundup can use this store as its primary source of user information, but it
2590+
needs additional information too - email address(es), roundup Roles, vacation
2591+
flags, roundup hyperdb item ids, etc. Also, "retired" users must still exist
2592+
in the user database, unlike some passwd files in which the users are removed
2593+
when they no longer have access to a system.
2594+
2595+
To make use of the passwd file, we therefore synchronise between the two user
2596+
stores. We also use the passwd file to validate the user logins, as described
2597+
in the previous example, `using an external password validation source`_. We
2598+
keep the users lists in sync using a fairly simple script that runs once a
2599+
day, or several times an hour if more immediate access is needed. In short, it:
2600+
2601+
1. parses the passwd file, finding usernames, passwords and real names,
2602+
2. compares that list to the current roundup user list:
2603+
a. entries no longer in the passwd file are *retired*
2604+
b. entries with mismatching real names are *updated*
2605+
a. entries only exist in the passwd file are *created*
2606+
3. send an email to administrators to let them know what's been done.
2607+
2608+
The retiring and updating are simple operations, requiring only a call to
2609+
``retire()`` or ``set()``. The creation operation requires more information
2610+
though - the user's email address and their roundup Roles. We're going to
2611+
assume that the user's email address is the same as their login name, so we
2612+
just append the domain name to that. The Roles are determined using the
2613+
passwd group identifier - mapping their UN*X group to an appropriate set of
2614+
Roles.
2615+
2616+
The script to perform all this, broken up into its main components, is as
2617+
follows. Firstly, we import the necessary modules and open the tracker we're
2618+
to work on::
2619+
2620+
import sys, os, smtplib
2621+
from roundup import instance, date
2622+
2623+
# open the tracker
2624+
tracker_home = sys.argv[1]
2625+
tracker = instance.open(tracker_home)
2626+
2627+
Next we read in the *passwd* file from the tracker home::
2628+
2629+
# read in the users
2630+
file = os.path.join(tracker_home, 'users.passwd')
2631+
users = [x.strip().split(':') for x in open(file).readlines()]
2632+
2633+
Handle special users (those to ignore in the file, and those who don't appear
2634+
in the file)::
2635+
2636+
# users to not keep ever, pre-load with the users I know aren't
2637+
# "real" users
2638+
ignore = ['ekmmon', 'bfast', 'csrmail']
2639+
2640+
# users to keep - pre-load with the roundup-specific users
2641+
keep = ['comment_pool', 'network_pool', 'admin', 'dev-team', 'cs_pool',
2642+
'anonymous', 'system_pool', 'automated']
2643+
2644+
Now we map the UN*X group numbers to the Roles that users should have::
2645+
2646+
roles = {
2647+
'501': 'User,Tech', # tech
2648+
'502': 'User', # finance
2649+
'503': 'User,CSR', # customer service reps
2650+
'504': 'User', # sales
2651+
'505': 'User', # marketing
2652+
}
2653+
2654+
Now we do all the work. Note that the body of the script (where we have the
2655+
tracker database open) is wrapped in a ``try`` / ``finally`` clause, so that
2656+
we always close the database cleanly when we're finished. So, we now do all
2657+
the work::
2658+
2659+
# open the database
2660+
db = tracker.open('admin')
2661+
try:
2662+
# store away messages to send to the tracker admins
2663+
msg = []
2664+
2665+
# loop over the users list read in from the passwd file
2666+
for user,passw,uid,gid,real,home,shell in users:
2667+
if user in ignore:
2668+
# this user shouldn't appear in our tracker
2669+
continue
2670+
keep.append(user)
2671+
try:
2672+
# see if the user exists in the tracker
2673+
uid = db.user.lookup(user)
2674+
2675+
# yes, they do - now check the real name for correctness
2676+
if real != db.user.get(uid, 'realname'):
2677+
db.user.set(uid, realname=real)
2678+
msg.append('FIX %s - %s'%(user, real))
2679+
except KeyError:
2680+
# nope, the user doesn't exist
2681+
db.user.create(username=user, realname=real,
2682+
address='%[email protected]'%user, roles=roles[gid])
2683+
msg.append('ADD %s - %s (%s)'%(user, real, roles[gid]))
2684+
2685+
# now check that all the users in the tracker are also in our "keep"
2686+
# list - retire those who aren't
2687+
for uid in db.user.list():
2688+
user = db.user.get(uid, 'username')
2689+
if user not in keep:
2690+
db.user.retire(uid)
2691+
msg.append('RET %s'%user)
2692+
2693+
# if we did work, then send email to the tracker admins
2694+
if msg:
2695+
# create the email
2696+
msg = '''Subject: %s user database maintenance
2697+
2698+
%s
2699+
'''%(db.config.TRACKER_NAME, '\n'.join(msg))
2700+
2701+
# send the email
2702+
smtp = smtplib.SMTP(db.config.MAILHOST)
2703+
addr = db.config.ADMIN_EMAIL
2704+
smtp.sendmail(addr, addr, msg)
2705+
2706+
# now we're done - commit the changes
2707+
db.commit()
2708+
finally:
2709+
# always close the database cleanly
2710+
db.close()
2711+
2712+
And that's it!
2713+
2714+
2715+
Enabling display of either message summaries or the entire messages
2716+
-------------------------------------------------------------------
2717+
2718+
This is pretty simple - all we need to do is copy the code from the example
2719+
`displaying entire message contents in the issue display`_ into our template
2720+
alongside the summary display, and then introduce a switch that shows either
2721+
one or the other. We'll use a new form variable, ``:whole_messages`` to
2722+
achieve this::
2723+
2724+
<table class="messages" tal:condition="context/messages">
2725+
<tal:block tal:condition="not:request/form/:whole_messages/value | python:0">
2726+
<tr><th colspan=3 class="header">Messages</th>
2727+
<th colspan=2 class="header">
2728+
<a href="?:whole_messages=yes">show entire messages</a>
2729+
</th>
2730+
</tr>
2731+
<tr tal:repeat="msg context/messages">
2732+
<td><a tal:attributes="href string:msg${msg/id}"
2733+
tal:content="string:msg${msg/id}"></a></td>
2734+
<td tal:content="msg/author">author</td>
2735+
<td nowrap tal:content="msg/date/pretty">date</td>
2736+
<td tal:content="msg/summary">summary</td>
2737+
<td>
2738+
<a tal:attributes="href string:?:remove:messages=${msg/id}&:action=edit">remove</a>
2739+
</td>
2740+
</tr>
2741+
</tal:block>
2742+
2743+
<tal:block tal:condition="request/form/:whole_messages/value | python:0">
2744+
<tr><th colspan=2 class="header">Messages</th>
2745+
<th class="header"><a href="?:whole_messages=">show only summaries</a></th>
2746+
</tr>
2747+
<tal:block tal:repeat="msg context/messages">
2748+
<tr>
2749+
<th tal:content="msg/author">author</th>
2750+
<th nowrap tal:content="msg/date/pretty">date</th>
2751+
<th style="text-align: right">
2752+
(<a tal:attributes="href string:?:remove:messages=${msg/id}&:action=edit">remove</a>)
2753+
</th>
2754+
</tr>
2755+
<tr><td colspan=3 tal:content="msg/content"></td></tr>
2756+
</tal:block>
2757+
</tal:block>
2758+
</table>
2759+
25822760

25832761
-------------------
25842762

roundup/templates/classic/html/issue.item

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ You are not allowed to view this page.
1818

1919
<table class="form">
2020
<tr>
21-
<th nowrap>Title</th>
21+
<th class="required" nowrap>Title</th>
2222
<td colspan=3 tal:content="structure python:context.title.field(size=60)">title</td>
2323
</tr>
2424

2525
<tr>
26-
<th nowrap>Priority</th>
26+
<th class="required" nowrap>Priority</th>
2727
<td tal:content="structure context/priority/menu">priority</td>
2828
<th nowrap>Status</th>
2929
<td tal:content="structure context/status/menu">status</td>
@@ -60,7 +60,8 @@ python:db.user.classhelp('username,realname,address,phone')" /><br>
6060
<tr>
6161
<th nowrap>Change Note</th>
6262
<td colspan=3>
63-
<textarea name=":note" wrap="hard" rows="5" cols="60"></textarea>
63+
<textarea tal:content="request/form/:note/value | default"
64+
name=":note" wrap="hard" rows="5" cols="60"></textarea>
6465
</td>
6566
</tr>
6667

@@ -76,9 +77,16 @@ python:db.user.classhelp('username,realname,address,phone')" /><br>
7677
</td>
7778
</tr>
7879
</table>
79-
8080
</form>
8181

82+
<table class="form" tal:condition="not:context/id">
83+
<tr>
84+
<td>Note:&nbsp;</td>
85+
<th class="required">highlighted</th>
86+
<td>&nbsp;fields are required.</td>
87+
</tr>
88+
</table>
89+
8290
<table class="form" tal:condition="context/is_only_view_ok">
8391
<tr>
8492
<th nowrap>Title</th><td colspan=3 tal:content="context/title">title</td>
@@ -114,8 +122,8 @@ python:db.user.classhelp('username,realname,address,phone')" /><br>
114122
</p>
115123

116124
<table class="messages" tal:condition="context/messages">
117-
<tr><th colspan=4 class="header">Messages</th></tr>
118-
<tr tal:repeat="msg context/messages/reverse">
125+
<tr><th colspan=5 class="header">Messages</th></tr>
126+
<tr tal:repeat="msg context/messages">
119127
<td><a tal:attributes="href string:msg${msg/id}"
120128
tal:content="string:msg${msg/id}"></a></td>
121129
<td tal:content="msg/author">author</td>

roundup/templates/classic/html/style.css

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,22 @@ table.form {
7070
}
7171

7272
table.form th {
73-
font-weight: bold;
7473
color: #333388;
7574
text-align: right;
7675
vertical-align: top;
76+
font-weight: normal;
7777
}
7878

7979
table.form th.header {
8080
font-weight: bold;
81-
color: #333388;
8281
background-color: #eeeeff;
8382
text-align: left;
8483
}
8584

85+
table.form th.required {
86+
font-weight: bold;
87+
}
88+
8689
table.form td {
8790
color: #333333;
8891
empty-cells: show;

0 commit comments

Comments
 (0)