Skip to content

Commit 8363d82

Browse files
author
Richard Jones
committed
queries on a per-user basis, and public queries [SF#891798] :)
EditAction was confused about who "self" was Edit collision detection was broken for index-page edits
1 parent c2085f4 commit 8363d82

File tree

10 files changed

+244
-64
lines changed

10 files changed

+244
-64
lines changed

CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ Feature:
1010
- added Reject exception which may be raised by auditors. This is trapped
1111
by mailgw and may be used to veto creation of file attachments or
1212
messages. (sf bug 700265)
13+
- queries on a per-user basis, and public queries (sf "bug" 891798 :)
1314

1415
Fixed:
1516
- Boolean HTML templating was broken
1617
- Link HTML templating field() was broken
1718
- Fix reporting of test inclusion in postgresql test
19+
- EditAction was confused about who "self" was
20+
- Edit collision detection was broken for index-page edits
1821

1922

2023
2004-03-24 0.7.0b1

doc/upgrading.txt

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,76 @@ accordingly. Note that there is information about upgrade procedures in the
99
.. contents::
1010

1111

12-
13-
Migrating from 0.7 to 0.8
12+
Migrating from 0.6 to 0.7
1413
=========================
1514

16-
0.8.0 Added Dispatcher role
17-
---------------------------
15+
0.7.0 Saving and sharing of user queries
16+
----------------------------------------
1817

19-
A new config option has been added. There is a 'role' that can be filled,
20-
that of the 'dispatcher'. This person acts as a central sentinel for issues
21-
coming into the system. You can configure it so that all e-mail error messages
22-
get bounced to them, them and the user in question, or just the user (default).
18+
Due to popular demand, the user query saving mechanisms have been
19+
overhauled. This means that queries remember the user that created them
20+
and they may be marked as being private for a particular user.
2321

24-
To toggle these switches, look at the new classic and minimal config.py's,
25-
specifically:
22+
You *are not required* to make these changes. You only need to make them
23+
if you wish to use the new query editing features. It's highly
24+
recommended, as the effort is minimal.
2625

26+
1. You will need to edit your tracker's ``dbinit.py`` to change the way
27+
queries are stored. Change the lines::
2728

28-
# The 'dispatcher' is a role that can get notified of new items to the database.
29-
DISPATCHER_EMAIL = ADMIN_EMAIL
29+
query = Class(db, "query",
30+
klass=String(), name=String(),
31+
url=String())
32+
query.setkey("name")
3033

31-
...
34+
to::
3235

36+
query = Class(db, "query",
37+
klass=String(), name=String(),
38+
url=String(), private_for=Link('user'))
3339

34-
# Send error messages to the dispatcher, user, or both?
35-
# If 'dispatcher', error message notifications will only be sent to the dispatcher.
36-
# If 'user', error message notifications will only be sent to the user.
37-
# If 'both', error message notifications will be sent to both individuals.
38-
ERROR_MESSAGES_TO = 'user'
40+
That is, add the "private_for" property, and remove the line that says
41+
``query.setkey("name")``. The latter is the most important edit here.
42+
43+
2. You will also need to copy the ``query.edit.html`` template page from the
44+
``templates/classic/html/`` directory of the source to your tracker's
45+
``html`` directory.
46+
47+
3. Once you've done that, edit the tracker's ``page.html`` template to
48+
change::
49+
50+
<td rowspan="2" valign="top" class="sidebar">
51+
<p class="classblock" tal:condition="request/user/queries">
52+
<b>Your Queries</b><br>
53+
<tal:block tal:repeat="qs request/user/queries">
54+
55+
to::
56+
57+
<td rowspan="2" valign="top" class="sidebar">
58+
<p class="classblock">
59+
<b>Your Queries</b> (<a href="query?@template=edit">edit</a>)<br>
60+
<tal:block tal:repeat="qs request/user/queries">
61+
62+
That is, you're removing the ``tal:condition`` and adding a link to the
63+
new edit page.
64+
65+
4. You might also wish to remove the redundant query editing section from the
66+
``user.item.html`` page.
67+
68+
69+
0.7.0 Added Dispatcher role
70+
---------------------------
71+
72+
A new config option has been added that specifies the email address of
73+
a "dispatcher" role. This email address acts as a central sentinel for
74+
issues coming into the system. You can configure it so that all e-mail
75+
error messages get bounced to them, them and the user in question, or
76+
just the user (default).
77+
78+
To toggle these switches, add the "DISPATCHER_EMAIL" and
79+
"ERROR_MESSAGES_TO" configuration values to your tracker's ``config.py``.
80+
See the `customisation documentation`_ for how to use them.
3981

40-
Migrating from 0.6 to 0.7
41-
=========================
4282

4383
0.7.0 Added CSV export action
4484
-----------------------------

doc/user_guide.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
User Guide
33
==========
44

5-
:Version: $Revision: 1.25 $
5+
:Version: $Revision: 1.26 $
66

77
.. contents::
88

@@ -245,6 +245,24 @@ See `entering values in your tracker`_ for an explanation of what you
245245
may type into the search form.
246246

247247

248+
Saving queries
249+
~~~~~~~~~~~~~~
250+
251+
You may save queries in the tracker by giving the query a name. Each user
252+
may only have one query with a given name - if a subsequent search is
253+
performed with the same query name supplied, then it will edit the
254+
existing query of the same name.
255+
256+
Queries may be marked as "private". These queries are only visible to the
257+
user that created them. If they're not marked "private" then all other
258+
users may include the query in their list of "Your Queries". Marking it as
259+
private at a later date does not affect users already using the query, nor
260+
does deleting the query.
261+
262+
If a user subsequently creates or edits a public query, a new personal
263+
version of that query is made, with the same editing rules as described
264+
above.
265+
248266

249267
Under the covers
250268
~~~~~~~~~~~~~~~~

roundup/cgi/actions.py

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#$Id: actions.py,v 1.16 2004-03-26 00:46:33 richard Exp $
1+
#$Id: actions.py,v 1.17 2004-03-26 04:50:50 richard Exp $
22

33
import re, cgi, StringIO, urllib, Cookie, time, random
44

@@ -134,14 +134,36 @@ def handle(self, wcre=re.compile(r'[\s,]+')):
134134
# query string.
135135
url = req.indexargs_href('', {})[1:]
136136

137-
# handle editing an existing query
138-
try:
139-
qid = self.db.query.lookup(queryname)
140-
self.db.query.set(qid, klass=self.classname, url=url)
141-
except KeyError:
142-
# create a query
143-
qid = self.db.query.create(name=queryname,
144-
klass=self.classname, url=url)
137+
key = self.db.query.getkey()
138+
if key:
139+
# edit the old way, only one query per name
140+
try:
141+
qid = self.db.query.lookup(queryname)
142+
self.db.query.set(qid, klass=self.classname, url=url)
143+
except KeyError:
144+
# create a query
145+
qid = self.db.query.create(name=queryname,
146+
klass=self.classname, url=url)
147+
else:
148+
# edit the new way, query name not a key any more
149+
# see if we match an existing private query
150+
uid = self.db.getuid()
151+
qids = self.db.query.filter({}, {'name': queryname,
152+
'private_for': uid})
153+
if not qids:
154+
# ok, so there's not a private query for the current user
155+
# - see if there's a public one created by them
156+
qids = self.db.query.filter({}, {'name': queryname,
157+
'private_for': -1, 'creator': uid})
158+
159+
if qids:
160+
# edit query
161+
qid = qids[0]
162+
self.db.query.set(qid, klass=self.classname, url=url)
163+
else:
164+
# create a query
165+
qid = self.db.query.create(name=queryname,
166+
klass=self.classname, url=url, private_for=uid)
145167

146168
# and add it to the user's query multilink
147169
queries = self.db.user.get(self.userid, 'queries')
@@ -435,23 +457,21 @@ def _createnode(self, cn, props):
435457
return cl.create(**props)
436458

437459
class EditItemAction(_EditAction):
438-
def lastUserActivity(self):
460+
def lastUserActvity(self):
439461
if self.form.has_key(':lastactivity'):
440-
return date.Date(self.form[':lastactivity'].value)
462+
user_actvity = date.Date(self.form[':lastactivity'].value)
441463
elif self.form.has_key('@lastactivity'):
442-
return date.Date(self.form['@lastactivity'].value)
464+
user_actvity = date.Date(self.form['@lastactivity'].value)
443465
else:
444466
return None
445467

446468
def lastNodeActivity(self):
447469
cl = getattr(self.client.db, self.classname)
448-
return cl.get(self.nodeid, 'activity')
470+
node_activity = cl.get(self.nodeid, 'activity')
449471

450-
def detectCollision(self, userActivity, nodeActivity):
451-
# Result from lastUserActivity may be None. If it is, assume there's no
452-
# conflict, or at least not one we can detect.
453-
if userActivity:
454-
return userActivity < nodeActivity
472+
def detectCollision(self, user_actvity, node_activity):
473+
if user_activity:
474+
return user_activity < node_activity
455475

456476
def handleCollision(self):
457477
self.client.template = 'collision'
@@ -462,7 +482,9 @@ def handle(self):
462482
See parsePropsFromForm and _editnodes for special variables.
463483
464484
"""
465-
if self.detectCollision(self.lastUserActivity(), self.lastNodeActivity()):
485+
user_activity = self.lastUserActvity()
486+
if user_activity and self.detectCollision(user_activity,
487+
self.lastNodeActivity()):
466488
self.handleCollision()
467489
return
468490

@@ -488,7 +510,7 @@ def handle(self):
488510
url += '?@ok_message=%s&@template=%s'%(urllib.quote(message),
489511
urllib.quote(self.template))
490512
if self.nodeid is None:
491-
req = templating.HTMLRequest(self)
513+
req = templating.HTMLRequest(self.client)
492514
url += '&' + req.indexargs_href('', {})[1:]
493515
raise Redirect, url
494516

roundup/cgi/templating.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def get(self, name, extension=None):
124124
raise
125125

126126
if self.templates.has_key(src) and \
127-
stime < self.templates[src].mtime:
127+
stime <= self.templates[src].mtime:
128128
# compiled template is up to date
129129
return self.templates[src]
130130

@@ -134,7 +134,7 @@ def get(self, name, extension=None):
134134
content_type = mimetypes.guess_type(filename)[0] or 'text/html'
135135
pt.pt_edit(open(src).read(), content_type)
136136
pt.id = filename
137-
pt.mtime = time.time()
137+
pt.mtime = stime
138138
return pt
139139

140140
def __getitem__(self, name):
@@ -195,6 +195,7 @@ class utils(client.instance.interfaces.TemplatingUtils, utils):
195195
'tracker': client.instance,
196196
'utils': utils(client),
197197
'templates': Templates(client.instance.config.TEMPLATES),
198+
'template': self,
198199
}
199200
# add in the item if there is one
200201
if client.nodeid:
@@ -644,6 +645,10 @@ def __getattr__(self, attr):
644645
def designator(self):
645646
"""Return this item's designator (classname + id)."""
646647
return '%s%s'%(self._classname, self._nodeid)
648+
649+
def is_retired(self):
650+
"""Is this item retired?"""
651+
return self._klass.is_retired(self._nodeid)
647652

648653
def submit(self, label="Submit Changes"):
649654
"""Generate a submit button.
@@ -1505,6 +1510,7 @@ def __contains__(self, value):
15051510
''' Support the "in" operator. We have to make sure the passed-in
15061511
value is a string first, not a HTMLProperty.
15071512
'''
1513+
print (self, value, self._value)
15081514
return str(value) in self._value
15091515

15101516
def reverse(self):

templates/classic/dbinit.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: dbinit.py,v 1.6 2004-03-15 05:51:23 richard Exp $
18+
# $Id: dbinit.py,v 1.7 2004-03-26 04:50:50 richard Exp $
1919

2020
import os
2121

@@ -54,8 +54,7 @@ def open(name=None):
5454

5555
query = Class(db, "query",
5656
klass=String(), name=String(),
57-
url=String())
58-
query.setkey("name")
57+
url=String(), private_for=Link('user'))
5958

6059
# add any additional database schema configuration here
6160

templates/classic/html/_generic.collision.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
editing. Please <a tal:attributes="href context/designator">reload</a>
99
the node and review your edits.
1010
</td>
11-
</tal:block>
11+
</tal:block>
12+

templates/classic/html/page.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ <h2><span metal:define-slot="body_title">body title</span></h2>
2525

2626
<tr>
2727
<td rowspan="2" valign="top" class="sidebar">
28-
<p class="classblock" tal:condition="request/user/queries">
29-
<b>Your Queries</b><br>
28+
<p class="classblock">
29+
<b>Your Queries</b> (<a href="query?@template=edit">edit</a>)<br>
3030
<tal:block tal:repeat="qs request/user/queries">
3131
<a tal:attributes="href string:${qs/klass}?${qs/url}"
3232
tal:content="qs/name">link</a><br>
@@ -149,3 +149,4 @@ <h2><span metal:define-slot="body_title">body title</span></h2>
149149
tal:attributes="value name;
150150
checked python:name == group_on">
151151
</td>
152+
<!-- SHA: 9defd15b86478f539e44f06b9548340e239d7320 -->

0 commit comments

Comments
 (0)