Skip to content

Commit d464221

Browse files
author
Richard Jones
committed
Better handling of the form variable labels.
It's done in one step rather than the split step of last week. This means that "msg-1@link@files" will work now. I had to change the password confirmation special var from "name:confirm" to ":confirm:name" so it conformed with the pattern set by ":add:" etc.
1 parent 9ca74dc commit d464221

File tree

3 files changed

+165
-85
lines changed

3 files changed

+165
-85
lines changed

roundup/cgi/client.py

Lines changed: 98 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: client.py,v 1.88 2003-02-17 01:04:31 richard Exp $
1+
# $Id: client.py,v 1.89 2003-02-17 06:44:00 richard Exp $
22

33
__doc__ = """
44
WWW request handler (also used in the stand-alone server).
@@ -93,16 +93,27 @@ class Client:
9393
FV_OK_MESSAGE = re.compile(r'[@:]ok_message')
9494
FV_ERROR_MESSAGE = re.compile(r'[@:]error_message')
9595

96-
# specials for parsePropsFromForm
97-
FV_REQUIRED = re.compile(r'[@:]required')
98-
FV_ADD = re.compile(r'([@:])add\1')
99-
FV_REMOVE = re.compile(r'([@:])remove\1')
100-
FV_CONFIRM = re.compile(r'.+[@:]confirm')
101-
FV_LINK = re.compile(r'([@:])link\1(.+)')
102-
103-
# deprecated
104-
FV_NOTE = re.compile(r'[@:]note')
105-
FV_FILE = re.compile(r'[@:]file')
96+
# edit form variable handling (see unit tests)
97+
FV_LABELS = r'''
98+
^(
99+
(?P<note>[@:]note)|
100+
(?P<file>[@:]file)|
101+
(
102+
((?P<classname>%s)(?P<id>[-\d]+))? # optional leading designator
103+
((?P<required>[@:]required$)| # :required
104+
(
105+
(
106+
(?P<add>[@:]add[@:])| # :add:<prop>
107+
(?P<remove>[@:]remove[@:])| # :remove:<prop>
108+
(?P<confirm>[@:]confirm[@:])| # :confirm:<prop>
109+
(?P<link>[@:]link[@:])| # :link:<prop>
110+
([@:]) # just a separator
111+
)?
112+
(?P<propname>[^@:]+) # <prop>
113+
)
114+
)
115+
)
116+
)$'''
106117

107118
# Note: index page stuff doesn't appear here:
108119
# columns, sort, sortdir, filter, group, groupdir, search_text,
@@ -1097,39 +1108,44 @@ def _createnode(self, cn, props):
10971108
return cl.create(**props)
10981109

10991110
def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
1100-
''' Pull properties for the given class out of the form.
1111+
''' Pull properties out of the form.
11011112
11021113
In the following, <bracketed> values are variable, ":" may be
11031114
one of ":" or "@", and other text "required" is fixed.
11041115
1105-
Properties are specified as form variables
1116+
Properties are specified as form variables:
1117+
1118+
<propname>
1119+
- property on the current context item
1120+
11061121
<designator>:<propname>
1122+
- property on the indicated item
1123+
1124+
<classname>-<N>:<propname>
1125+
- property on the Nth new item of classname
11071126
1108-
where the propery belongs to the context class or item if the
1109-
designator is not specified. The designator may specify a
1110-
negative item id value (ie. "issue-1") and a new item of the
1111-
specified class will be created for each negative id found.
1127+
Once we have determined the "propname", we check to see if it
1128+
is one of the special form values:
11121129
1113-
If a "<designator>:required" parameter is supplied,
1114-
then the named property values must be supplied or a
1115-
ValueError will be raised.
1130+
:required
1131+
The named property values must be supplied or a ValueError
1132+
will be raised.
11161133
1117-
Other special form values:
1118-
[classname|designator]:remove:<propname>=id(s)
1134+
:remove:<propname>=id(s)
11191135
The ids will be removed from the multilink property.
1120-
[classname|designator]:add:<propname>=id(s)
1136+
1137+
:add:<propname>=id(s)
11211138
The ids will be added to the multilink property.
11221139
1123-
[classname|designator]:link:<propname>=<classname>
1140+
:link:<propname>=<designator>
11241141
Used to add a link to new items created during edit.
11251142
These are collected up and returned in all_links. This will
11261143
result in an additional linking operation (either Link set or
11271144
Multilink append) after the edit/create is done using
1128-
all_props in _editnodes. The <propname> on
1129-
[classname|designator] will be set/appended the id of the
1130-
newly created item of class <classname>.
1131-
1132-
Note: the colon may be either ":" or "@".
1145+
all_props in _editnodes. The <propname> on the current item
1146+
will be set/appended the id of the newly created item of
1147+
class <designator> (where <designator> must be
1148+
<classname>-<N>).
11331149
11341150
Any of the form variables may be prefixed with a classname or
11351151
designator.
@@ -1149,19 +1165,21 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
11491165
Two special form values are supported for backwards
11501166
compatibility:
11511167
:note - create a message (with content, author and date), link
1152-
to the context item
1168+
to the context item. This is ALWAYS desginated "msg-1".
11531169
:file - create a file, attach to the current item and any
1154-
message created by :note
1170+
message created by :note. This is ALWAYS designated
1171+
"file-1".
11551172
'''
11561173
# some very useful variables
11571174
db = self.db
11581175
form = self.form
11591176

1160-
if not hasattr(self, 'FV_ITEMSPEC'):
1161-
# generate the regexp for detecting
1162-
# <classname|designator>[@:+]property
1177+
if not hasattr(self, 'FV_SPECIAL'):
1178+
# generate the regexp for handling special form values
11631179
classes = '|'.join(db.classes.keys())
1164-
self.FV_ITEMSPEC = re.compile(r'(%s)([-\d]+)[@:](.+)$'%classes)
1180+
# specials for parsePropsFromForm
1181+
# handle the various forms (see unit tests)
1182+
self.FV_SPECIAL = re.compile(self.FV_LABELS%classes, re.VERBOSE)
11651183
self.FV_DESIGNATOR = re.compile(r'(%s)([-\d]+)'%classes)
11661184

11671185
# these indicate the default class / item
@@ -1181,54 +1199,52 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
11811199
keys = form.keys()
11821200
timezone = db.getUserTimezone()
11831201

1202+
# sentinels for the :note and :file props
1203+
have_note = have_file = 0
1204+
1205+
# extract the usable form labels from the form
1206+
matches = []
11841207
for key in keys:
1185-
# see if this value modifies a different item to the default
1186-
m = self.FV_ITEMSPEC.match(key)
1208+
m = self.FV_SPECIAL.match(key)
11871209
if m:
1210+
matches.append((key, m.groupdict()))
1211+
1212+
# now handle the matches
1213+
for key, d in matches:
1214+
if d['classname']:
11881215
# we got a designator
1189-
cn = m.group(1)
1216+
cn = d['classname']
11901217
cl = self.db.classes[cn]
1191-
nodeid = m.group(2)
1192-
propname = m.group(3)
1193-
elif key == ':note':
1194-
# backwards compatibility: the special note field
1218+
nodeid = d['id']
1219+
propname = d['propname']
1220+
elif d['note']:
1221+
# the special note field
11951222
cn = 'msg'
11961223
cl = self.db.classes[cn]
11971224
nodeid = '-1'
11981225
propname = 'content'
11991226
all_links.append((default_cn, default_nodeid, 'messages',
12001227
[('msg', '-1')]))
1201-
elif key == ':file':
1202-
# backwards compatibility: the special file field
1228+
have_note = 1
1229+
elif d['file']:
1230+
# the special file field
12031231
cn = 'file'
12041232
cl = self.db.classes[cn]
12051233
nodeid = '-1'
12061234
propname = 'content'
12071235
all_links.append((default_cn, default_nodeid, 'files',
12081236
[('file', '-1')]))
1209-
if self.form.has_key(':note'):
1210-
all_links.append(('msg', '-1', 'files', [('file', '-1')]))
1237+
have_file = 1
12111238
else:
12121239
# default
12131240
cn = default_cn
12141241
cl = default_cl
12151242
nodeid = default_nodeid
1216-
propname = key
1243+
propname = d['propname']
12171244

12181245
# the thing this value relates to is...
12191246
this = (cn, nodeid)
12201247

1221-
# is this a link command?
1222-
if self.FV_LINK.match(propname):
1223-
value = []
1224-
for entry in extractFormList(form[key]):
1225-
m = self.FV_DESIGNATOR.match(entry)
1226-
if not m:
1227-
raise ValueError, \
1228-
'link "%s" value "%s" not a designator'%(key, entry)
1229-
value.append((m.groups(1), m.groups(2)))
1230-
all_links.append((cn, nodeid, propname[6:], value))
1231-
12321248
# get more info about the class, and the current set of
12331249
# form props for it
12341250
if not all_propdef.has_key(cn):
@@ -1238,8 +1254,20 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
12381254
all_props[this] = {}
12391255
props = all_props[this]
12401256

1257+
# is this a link command?
1258+
if d['link']:
1259+
value = []
1260+
for entry in extractFormList(form[key]):
1261+
m = self.FV_DESIGNATOR.match(entry)
1262+
if not m:
1263+
raise ValueError, \
1264+
'link "%s" value "%s" not a designator'%(key, entry)
1265+
value.append((m.group(1), m.group(2)))
1266+
all_links.append((cn, nodeid, propname, value))
1267+
continue
1268+
12411269
# detect the special ":required" variable
1242-
if self.FV_REQUIRED.match(key):
1270+
if d['required']:
12431271
all_required[this] = extractFormList(form[key])
12441272
continue
12451273

@@ -1250,11 +1278,9 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
12501278

12511279
# see if we're performing a special multilink action
12521280
mlaction = 'set'
1253-
if self.FV_REMOVE.match(propname):
1254-
propname = propname[8:]
1281+
if d['remove']:
12551282
mlaction = 'remove'
1256-
elif self.FV_ADD.match(propname):
1257-
propname = propname[5:]
1283+
elif d['add']:
12581284
mlaction = 'add'
12591285

12601286
# does the property exist?
@@ -1263,6 +1289,8 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
12631289
raise ValueError, 'You have submitted a %s action for'\
12641290
' the property "%s" which doesn\'t exist'%(mlaction,
12651291
propname)
1292+
# the form element is probably just something we don't care
1293+
# about - ignore it
12661294
continue
12671295
proptype = propdef[propname]
12681296

@@ -1285,7 +1313,7 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
12851313

12861314
# now that we have the props field, we need a teensy little
12871315
# extra bit of help for the old :note field...
1288-
if key == ':note' and value:
1316+
if d['note'] and value:
12891317
props['author'] = self.db.getuid()
12901318
props['date'] = date.Date()
12911319

@@ -1294,8 +1322,8 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
12941322
if not value:
12951323
# ignore empty password values
12961324
continue
1297-
for key in keys:
1298-
if self.FV_CONFIRM.match(key):
1325+
for key, d in matches:
1326+
if d['confirm'] and d['propname'] == propname:
12991327
confirm = form[key]
13001328
break
13011329
else:
@@ -1466,6 +1494,10 @@ def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
14661494
if propname in required and value is not None:
14671495
required.remove(propname)
14681496

1497+
# check to see if we need to specially link a file to the note
1498+
if have_note and have_file:
1499+
all_links.append(('msg', '-1', 'files', [('file', '-1')]))
1500+
14691501
# see if all the required properties have been supplied
14701502
s = []
14711503
for thing, required in all_required.items():

roundup/cgi/templating.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -876,9 +876,9 @@ def field(self, size = 30):
876876
def confirm(self, size = 30):
877877
''' Render a second form edit field for the property, used for
878878
confirmation that the user typed the password correctly. Generates
879-
a field with name "name:confirm".
879+
a field with name ":confirm:name".
880880
'''
881-
return '<input type="password" name="%s:confirm" size="%s">'%(
881+
return '<input type="password" name=":confirm:name" size="%s">'%(
882882
self._name, size)
883883

884884
class NumberHTMLProperty(HTMLProperty):

0 commit comments

Comments
 (0)