Skip to content

Commit 9394815

Browse files
author
Richard Jones
committed
A couple of form value handling changes:
- multilink properties may hhave multiple form values "1", "2,4", "5", ... - string search properties are split on whitespace and match any of the values
1 parent 3be1aa1 commit 9394815

File tree

9 files changed

+117
-44
lines changed

9 files changed

+117
-44
lines changed

CHANGES.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Feature:
6565
3rd-party templates.
6666
- extended date syntax to make range searches even more useful
6767
- SMTP login and TLS support added (sf bug 710853 with extras ;)
68+
Note: requires python 2.2+
6869

6970
Fixed:
7071
- applied unicode patch. All data is stored in utf-8. Incoming messages
@@ -92,7 +93,7 @@ Fixed:
9293
(sf "bug" 621226 for the users of the "standards compliant" browser IE)
9394

9495

95-
2003-??-?? 0.5.7
96+
2003-05-08 0.5.7
9697
- fixed Interval maths (sf bug 665357)
9798
- fixed sqlite rollback/caching bug (sf bug 689383)
9899
- fixed rdbms table update detection logic (sf bug 703297)

roundup/backends/back_anydbm.py

Lines changed: 17 additions & 8 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: back_anydbm.py,v 1.120 2003-04-22 20:53:54 kedder Exp $
18+
#$Id: back_anydbm.py,v 1.121 2003-05-09 01:47:50 richard Exp $
1919
'''
2020
This module defines a backend that saves the hyperdatabase in a database
2121
chosen by anydbm. It is guaranteed to always be available in python
@@ -1685,11 +1685,17 @@ def filter(self, search_matches, filterspec, sort=(None,None),
16851685
u.sort()
16861686
l.append((MULTILINK, k, u))
16871687
elif isinstance(propclass, String) and k != 'id':
1688-
# simple glob searching
1689-
v = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', v)
1690-
v = v.replace('?', '.')
1691-
v = v.replace('*', '.*?')
1692-
l.append((STRING, k, re.compile(v, re.I)))
1688+
if type(v) is not type([]):
1689+
v = [v]
1690+
m = []
1691+
for v in v:
1692+
# simple glob searching
1693+
v = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', v)
1694+
v = v.replace('?', '.')
1695+
v = v.replace('*', '.*?')
1696+
m.append(v)
1697+
m = re.compile('(%s)'%('|'.join(m)), re.I)
1698+
l.append((STRING, k, m))
16931699
elif isinstance(propclass, Date):
16941700
try:
16951701
date_rng = Range(v, date.Date, offset=timezone)
@@ -1761,11 +1767,14 @@ def filter(self, search_matches, filterspec, sort=(None,None),
17611767
continue
17621768
break
17631769
elif t == STRING:
1770+
if node[k] is None:
1771+
break
17641772
# RE search
1765-
if node[k] is None or not v.search(node[k]):
1773+
if not v.search(node[k]):
17661774
break
17671775
elif t == DATE or t == INTERVAL:
1768-
if node[k] is None: break
1776+
if node[k] is None:
1777+
break
17691778
if v.to_value:
17701779
if not (v.from_value <= node[k] and v.to_value >= node[k]):
17711780
break

roundup/backends/back_bsddb.py

Lines changed: 6 additions & 5 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: back_bsddb.py,v 1.25 2003-03-26 11:19:28 richard Exp $
18+
#$Id: back_bsddb.py,v 1.26 2003-05-09 01:47:50 richard Exp $
1919
'''
2020
This module defines a backend that saves the hyperdatabase in BSDDB.
2121
'''
@@ -74,6 +74,9 @@ def opendb(self, name, mode):
7474
#
7575
def getjournal(self, classname, nodeid):
7676
''' get the journal for id
77+
78+
Raise IndexError if the node doesn't exist (as per history()'s
79+
API)
7780
'''
7881
if __debug__:
7982
print >>hyperdb.DEBUG, 'getjournal', (self, classname, nodeid)
@@ -114,10 +117,8 @@ def getjournal(self, classname, nodeid):
114117
db.close()
115118

116119
# add all the saved journal entries for this node
117-
for entry in journal:
118-
(nodeid, date_stamp, user, action, params) = entry
119-
date_obj = date.Date(date_stamp)
120-
res.append((nodeid, date_obj, user, action, params))
120+
for nodeid, date_stamp, user, action, params in journal:
121+
res.append((nodeid, date.Date(date_stamp), user, action, params))
121122
return res
122123

123124
def getCachedJournalDB(self, classname):

roundup/backends/back_bsddb3.py

Lines changed: 33 additions & 6 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: back_bsddb3.py,v 1.18 2002-10-03 06:56:29 richard Exp $
18+
#$Id: back_bsddb3.py,v 1.19 2003-05-09 01:47:50 richard Exp $
1919
'''
2020
This module defines a backend that saves the hyperdatabase in BSDDB3.
2121
'''
@@ -74,7 +74,31 @@ def opendb(self, name, mode):
7474
#
7575
def getjournal(self, classname, nodeid):
7676
''' get the journal for id
77+
78+
Raise IndexError if the node doesn't exist (as per history()'s
79+
API)
7780
'''
81+
if __debug__:
82+
print >>hyperdb.DEBUG, 'getjournal', (self, classname, nodeid)
83+
84+
# our journal result
85+
res = []
86+
87+
# add any journal entries for transactions not committed to the
88+
# database
89+
for method, args in self.transactions:
90+
if method != self.doSaveJournal:
91+
continue
92+
(cache_classname, cache_nodeid, cache_action, cache_params,
93+
cache_creator, cache_creation) = args
94+
if cache_classname == classname and cache_nodeid == nodeid:
95+
if not cache_creator:
96+
cache_creator = self.curuserid
97+
if not cache_creation:
98+
cache_creation = date.Date()
99+
res.append((cache_nodeid, cache_creation, cache_creator,
100+
cache_action, cache_params))
101+
78102
# attempt to open the journal - in some rare cases, the journal may
79103
# not exist
80104
try:
@@ -84,14 +108,17 @@ def getjournal(self, classname, nodeid):
84108
raise IndexError, 'no such %s %s'%(classname, nodeid)
85109
# more handling of bad journals
86110
if not db.has_key(nodeid):
111+
db.close()
112+
if res:
113+
# we have some unsaved journal entries, be happy!
114+
return res
87115
raise IndexError, 'no such %s %s'%(classname, nodeid)
88116
journal = marshal.loads(db[nodeid])
89-
res = []
90-
for entry in journal:
91-
(nodeid, date_stamp, user, action, params) = entry
92-
date_obj = date.Date(date_stamp)
93-
res.append((nodeid, date_obj, user, action, params))
94117
db.close()
118+
119+
# add all the saved journal entries for this node
120+
for nodeid, date_stamp, user, action, params in journal:
121+
res.append((nodeid, date.Date(date_stamp), user, action, params))
95122
return res
96123

97124
def getCachedJournalDB(self, classname):

roundup/backends/back_metakit.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_metakit.py,v 1.46 2003-04-20 11:58:45 kedder Exp $
1+
# $Id: back_metakit.py,v 1.47 2003-05-09 01:47:50 richard Exp $
22
'''
33
Metakit backend for Roundup, originally by Gordon McMillan.
44
@@ -944,11 +944,16 @@ def filter(self, search_matches, filterspec, sort=(None,None),
944944
else:
945945
orcriteria[propname] = u
946946
elif isinstance(prop, hyperdb.String):
947-
# simple glob searching
948-
v = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', value)
949-
v = v.replace('?', '.')
950-
v = v.replace('*', '.*?')
951-
regexes[propname] = re.compile(v, re.I)
947+
if type(value) is not type([]):
948+
value = [value]
949+
m = []
950+
for v in value:
951+
# simple glob searching
952+
v = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', v)
953+
v = v.replace('?', '.')
954+
v = v.replace('*', '.*?')
955+
m.append(v)
956+
regexes[propname] = re.compile('(%s)'%('|'.join(m)), re.I)
952957
elif propname == 'id':
953958
where[propname] = int(value)
954959
elif isinstance(prop, hyperdb.Boolean):

roundup/cgi/client.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: client.py,v 1.114 2003-04-24 07:19:59 richard Exp $
1+
# $Id: client.py,v 1.115 2003-05-09 01:47:50 richard Exp $
22

33
__doc__ = """
44
WWW request handler (also used in the stand-alone server).
@@ -1283,15 +1283,17 @@ def editCSVPermission(self):
12831283
return 0
12841284
return 1
12851285

1286-
def searchAction(self):
1286+
def searchAction(self, wcre=re.compile(r'[\s,]+')):
12871287
''' Mangle some of the form variables.
12881288
12891289
Set the form ":filter" variable based on the values of the
12901290
filter variables - if they're set to anything other than
12911291
"dontcare" then add them to :filter.
12921292
1293-
Also handle the ":queryname" variable and save off the query to
1293+
Handle the ":queryname" variable and save off the query to
12941294
the user's query list.
1295+
1296+
Split any String query values on whitespace and comma.
12951297
'''
12961298
# generic edit is per-class only
12971299
if not self.searchPermission():
@@ -1319,6 +1321,15 @@ def searchAction(self):
13191321
else:
13201322
if not self.form[key].value:
13211323
continue
1324+
if isinstance(props[key], hyperdb.String):
1325+
v = self.form[key].value
1326+
l = wcre.split(v)
1327+
if len(l) > 1:
1328+
self.form.value.remove(self.form[key])
1329+
# replace the single value with the split list
1330+
for v in l:
1331+
self.form.value.append(cgi.MiniFieldStorage(key, v))
1332+
13221333
self.form.value.append(cgi.MiniFieldStorage('@filter', key))
13231334

13241335
# handle saving the query params
@@ -1857,18 +1868,20 @@ def extractFormList(value):
18571868
''' Extract a list of values from the form value.
18581869
18591870
It may be one of:
1860-
[MiniFieldStorage, MiniFieldStorage, ...]
1871+
[MiniFieldStorage('value'), MiniFieldStorage('value','value',...), ...]
18611872
MiniFieldStorage('value,value,...')
18621873
MiniFieldStorage('value')
18631874
'''
18641875
# multiple values are OK
18651876
if isinstance(value, type([])):
1866-
# it's a list of MiniFieldStorages
1867-
value = [i.value.strip() for i in value]
1877+
# it's a list of MiniFieldStorages - join then into
1878+
values = ','.join([i.value.strip() for i in value])
18681879
else:
18691880
# it's a MiniFieldStorage, but may be a comma-separated list
18701881
# of values
1871-
value = [i.strip() for i in value.value.split(',')]
1882+
values = value.value
1883+
1884+
value = [i.strip() for i in values.split(',')]
18721885

18731886
# filter out the empty bits
18741887
return filter(None, value)

roundup/cgi/templating.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,13 +1457,17 @@ def _post_init(self):
14571457
if self.classname is not None:
14581458
props = db.getclass(self.classname).getprops()
14591459
for name in self.filter:
1460-
if self.form.has_key(name):
1461-
prop = props[name]
1462-
fv = self.form[name]
1463-
if (isinstance(prop, hyperdb.Link) or
1464-
isinstance(prop, hyperdb.Multilink)):
1465-
self.filterspec[name] = lookupIds(db, prop,
1466-
handleListCGIValue(fv))
1460+
if not self.form.has_key(name):
1461+
continue
1462+
prop = props[name]
1463+
fv = self.form[name]
1464+
if (isinstance(prop, hyperdb.Link) or
1465+
isinstance(prop, hyperdb.Multilink)):
1466+
self.filterspec[name] = lookupIds(db, prop,
1467+
handleListCGIValue(fv))
1468+
else:
1469+
if isinstance(fv, type([])):
1470+
self.filterspec[name] = [v.value for v in fv]
14671471
else:
14681472
self.filterspec[name] = fv.value
14691473

test/test_cgi.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# but WITHOUT ANY WARRANTY; without even the implied warranty of
99
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1010
#
11-
# $Id: test_cgi.py,v 1.15 2003-04-17 06:51:44 richard Exp $
11+
# $Id: test_cgi.py,v 1.16 2003-05-09 01:47:50 richard Exp $
1212

1313
import unittest, os, shutil, errno, sys, difflib, cgi, re
1414

@@ -233,6 +233,17 @@ def testSetMultilink(self):
233233
self.assertEqual(self.parseForm({'nosy': 'admin,2'}, 'issue'),
234234
({('issue', None): {'nosy': ['1','2']}}, []))
235235

236+
def testMixedMultilink(self):
237+
form = cgi.FieldStorage()
238+
form.list.append(cgi.MiniFieldStorage('nosy', '1,2'))
239+
form.list.append(cgi.MiniFieldStorage('nosy', '3'))
240+
cl = client.Client(self.instance, None, {'PATH_INFO':'/'}, form)
241+
cl.classname = 'issue'
242+
cl.nodeid = None
243+
cl.db = self.db
244+
self.assertEqual(cl.parsePropsFromForm(),
245+
({('issue', None): {'nosy': ['1','2', '3']}}, []))
246+
236247
def testEmptyMultilinkSet(self):
237248
nodeid = self.db.issue.create(nosy=['1','2'])
238249
self.assertEqual(self.parseForm({'nosy': ''}, 'issue', nodeid),

test/test_db.py

Lines changed: 5 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: test_db.py,v 1.88 2003-04-22 20:53:55 kedder Exp $
18+
# $Id: test_db.py,v 1.89 2003-05-09 01:47:51 richard Exp $
1919

2020
import unittest, os, shutil, time
2121

@@ -674,9 +674,11 @@ def testFilteringID(self):
674674

675675
def testFilteringString(self):
676676
ae, filt = self.filteringSetup()
677-
ae(filt(None, {'title': 'issue one'}, ('+','id'), (None,None)), ['1'])
678-
ae(filt(None, {'title': 'issue'}, ('+','id'), (None,None)),
677+
ae(filt(None, {'title': ['one']}, ('+','id'), (None,None)), ['1'])
678+
ae(filt(None, {'title': ['issue']}, ('+','id'), (None,None)),
679679
['1','2','3'])
680+
ae(filt(None, {'title': ['one', 'two']}, ('+','id'), (None,None)),
681+
['1', '2'])
680682

681683
def testFilteringLink(self):
682684
ae, filt = self.filteringSetup()

0 commit comments

Comments
 (0)