comparison roundup/cgi/form_parser.py @ 6062:ed19b123a7ac

flake8 whitespace/formatting changes.
author John Rouillard <rouilj@ieee.org>
date Fri, 17 Jan 2020 20:28:11 -0500
parents 936275dfe1fa
children 408fd477761f
comparison
equal deleted inserted replaced
6061:82816000aef3 6062:ed19b123a7ac
1 import re, mimetypes 1 import re, mimetypes
2 2
3 from roundup import hyperdb, date, password 3 from roundup import hyperdb, date, password
4 from roundup.cgi import templating 4 from roundup.cgi import templating
5 from roundup.cgi.exceptions import FormError 5 from roundup.cgi.exceptions import FormError
6
6 7
7 class FormParser: 8 class FormParser:
8 # edit form variable handling (see unit tests) 9 # edit form variable handling (see unit tests)
9 FV_LABELS = r''' 10 FV_LABELS = r'''
10 ^( 11 ^(
199 if not hasattr(self, 'FV_SPECIAL'): 200 if not hasattr(self, 'FV_SPECIAL'):
200 # generate the regexp for handling special form values 201 # generate the regexp for handling special form values
201 classes = '|'.join(db.classes.keys()) 202 classes = '|'.join(db.classes.keys())
202 # specials for parsePropsFromForm 203 # specials for parsePropsFromForm
203 # handle the various forms (see unit tests) 204 # handle the various forms (see unit tests)
204 self.FV_SPECIAL = re.compile(self.FV_LABELS%classes, re.VERBOSE) 205 self.FV_SPECIAL = re.compile(self.FV_LABELS % classes, re.VERBOSE)
205 self.FV_DESIGNATOR = re.compile(r'(%s)([-\d]+)'%classes) 206 self.FV_DESIGNATOR = re.compile(r'(%s)([-\d]+)' % classes)
206 207
207 # these indicate the default class / item 208 # these indicate the default class / item
208 default_cn = self.classname 209 default_cn = self.classname
209 default_cl = self.db.classes[default_cn] 210 default_cl = self.db.classes[default_cn]
210 default_nodeid = self.nodeid 211 default_nodeid = self.nodeid
245 cn = 'msg' 246 cn = 'msg'
246 cl = self.db.classes[cn] 247 cl = self.db.classes[cn]
247 nodeid = '-1' 248 nodeid = '-1'
248 propname = 'content' 249 propname = 'content'
249 all_links.append((default_cn, default_nodeid, 'messages', 250 all_links.append((default_cn, default_nodeid, 'messages',
250 [('msg', '-1')])) 251 [('msg', '-1')]))
251 have_note = 1 252 have_note = 1
252 elif d['file']: 253 elif d['file']:
253 # the special file field 254 # the special file field
254 cn = default_cn 255 cn = default_cn
255 cl = default_cl 256 cl = default_cl
284 if d['link']: 285 if d['link']:
285 value = [] 286 value = []
286 for entry in self.extractFormList(form[key]): 287 for entry in self.extractFormList(form[key]):
287 m = self.FV_DESIGNATOR.match(entry) 288 m = self.FV_DESIGNATOR.match(entry)
288 if not m: 289 if not m:
289 raise FormError (self._('link "%(key)s" ' 290 raise FormError(self._('link "%(key)s" '
290 'value "%(entry)s" not a designator') % locals()) 291 'value "%(entry)s" not a designator') % locals())
291 value.append((m.group(1), m.group(2))) 292 value.append((m.group(1), m.group(2)))
292 293
293 # get details of linked class 294 # get details of linked class
294 lcn = m.group(1) 295 lcn = m.group(1)
302 got_props[(lcn, lnodeid)] = {} 303 got_props[(lcn, lnodeid)] = {}
303 304
304 # make sure the link property is valid 305 # make sure the link property is valid
305 if (not isinstance(propdef[propname], hyperdb.Multilink) and 306 if (not isinstance(propdef[propname], hyperdb.Multilink) and
306 not isinstance(propdef[propname], hyperdb.Link)): 307 not isinstance(propdef[propname], hyperdb.Link)):
307 raise FormError (self._('%(class)s %(property)s ' 308 raise FormError(self._('%(class)s %(property)s '
308 'is not a link or multilink property') % { 309 'is not a link or multilink property') % {
309 'class':cn, 'property':propname}) 310 'class':cn, 'property':propname})
310 311
311 all_links.append((cn, nodeid, propname, value)) 312 all_links.append((cn, nodeid, propname, value))
312 continue 313 continue
313 314
314 # detect the special ":required" variable 315 # detect the special ":required" variable
315 if d['required']: 316 if d['required']:
316 for entry in self.extractFormList(form[key]): 317 for entry in self.extractFormList(form[key]):
317 m = self.FV_SPECIAL.match(entry) 318 m = self.FV_SPECIAL.match(entry)
318 if not m: 319 if not m:
319 raise FormError (self._('The form action claims to ' 320 raise FormError(self._('The form action claims to '
320 'require property "%(property)s" ' 321 'require property "%(property)s" '
321 'which doesn\'t exist') % { 322 'which doesn\'t exist') % {
322 'property':propname}) 323 'property':propname})
323 if m.group('classname'): 324 if m.group('classname'):
324 this = (m.group('classname'), m.group('id')) 325 this = (m.group('classname'), m.group('id'))
336 mlaction = 'add' 337 mlaction = 'add'
337 338
338 # does the property exist? 339 # does the property exist?
339 if propname not in propdef: 340 if propname not in propdef:
340 if mlaction != 'set': 341 if mlaction != 'set':
341 raise FormError (self._('You have submitted a %(action)s ' 342 raise FormError(self._('You have submitted a %(action)s '
342 'action for the property "%(property)s" ' 343 'action for the property "%(property)s" '
343 'which doesn\'t exist') % { 344 'which doesn\'t exist') % {
344 'action': mlaction, 'property':propname}) 345 'action': mlaction, 'property': propname})
345 # the form element is probably just something we don't care 346 # the form element is probably just something we don't care
346 # about - ignore it 347 # about - ignore it
347 continue 348 continue
348 proptype = propdef[propname] 349 proptype = propdef[propname]
349 350
361 elif isinstance(proptype, hyperdb.Multilink): 362 elif isinstance(proptype, hyperdb.Multilink):
362 value = self.extractFormList(value) 363 value = self.extractFormList(value)
363 else: 364 else:
364 # multiple values are not OK 365 # multiple values are not OK
365 if isinstance(value, type([])): 366 if isinstance(value, type([])):
366 raise FormError (self._('You have submitted more than one ' 367 raise FormError(self._('You have submitted more than one '
367 'value for the %s property') % propname) 368 'value for the %s property') % propname)
368 # value might be a single file upload 369 # value might be a single file upload
369 if not getattr(value, 'filename', None): 370 if not getattr(value, 'filename', None):
370 value = value.value.strip() 371 value = value.value.strip()
371 372
386 for key, d in matches: 387 for key, d in matches:
387 if d['confirm'] and d['propname'] == propname: 388 if d['confirm'] and d['propname'] == propname:
388 confirm = form[key] 389 confirm = form[key]
389 break 390 break
390 else: 391 else:
391 raise FormError (self._('Password and confirmation text ' 392 raise FormError(self._('Password and confirmation text '
392 'do not match')) 393 'do not match'))
393 if isinstance(confirm, type([])): 394 if isinstance(confirm, type([])):
394 raise FormError (self._('You have submitted more than one ' 395 raise FormError(self._('You have submitted more than one '
395 'value for the %s property') % propname) 396 'value for the %s property') % propname)
396 if value != confirm.value: 397 if value != confirm.value:
397 raise FormError (self._('Password and confirmation text ' 398 raise FormError(self._('Password and confirmation text '
398 'do not match')) 399 'do not match'))
399 try: 400 try:
400 value = password.Password(value, scheme = proptype.scheme, 401 value = password.Password(value, scheme=proptype.scheme,
401 config=self.db.config) 402 config=self.db.config)
402 except hyperdb.HyperdbValueError as msg: 403 except hyperdb.HyperdbValueError as msg:
403 raise FormError (msg) 404 raise FormError(msg)
404 elif d['file']: 405 elif d['file']:
405 # This needs to be a Multilink and is checked above 406 # This needs to be a Multilink and is checked above
406 fcn = 'file' 407 fcn = 'file'
407 fcl = self.db.classes[fcn] 408 fcl = self.db.classes[fcn]
408 fpropname = 'content' 409 fpropname = 'content'
410 all_propdef[fcn] = fcl.getprops() 411 all_propdef[fcn] = fcl.getprops()
411 fpropdef = all_propdef[fcn] 412 fpropdef = all_propdef[fcn]
412 have_file = [] 413 have_file = []
413 for n, v in enumerate(value): 414 for n, v in enumerate(value):
414 if not hasattr(v, 'filename'): 415 if not hasattr(v, 'filename'):
415 raise FormError (self._('Not a file attachment')) 416 raise FormError(self._('Not a file attachment'))
416 # skip if the upload is empty 417 # skip if the upload is empty
417 if not v.filename: 418 if not v.filename:
418 continue 419 continue
419 fnodeid = str (-(n+1)) 420 fnodeid = str(-(n+1))
420 have_file.append(fnodeid) 421 have_file.append(fnodeid)
421 fthis = (fcn, fnodeid) 422 fthis = (fcn, fnodeid)
422 if fthis not in all_props: 423 if fthis not in all_props:
423 all_props[fthis] = {} 424 all_props[fthis] = {}
424 fprops = all_props[fthis] 425 fprops = all_props[fthis]
425 all_links.append((cn, nodeid, 'files', [('file', fnodeid)])) 426 all_links.append((cn, nodeid, 'files',
427 [('file', fnodeid)]))
426 428
427 fprops['content'] = self.parse_file(fpropdef, fprops, v) 429 fprops['content'] = self.parse_file(fpropdef, fprops, v)
428 value = None 430 value = None
429 nodeid = None 431 nodeid = None
430 elif isinstance(proptype, hyperdb.Multilink): 432 elif isinstance(proptype, hyperdb.Multilink):
431 # convert input to list of ids 433 # convert input to list of ids
432 try: 434 try:
433 l = hyperdb.rawToHyperdb(self.db, cl, nodeid, 435 l = hyperdb.rawToHyperdb(self.db, cl, nodeid,
434 propname, value) 436 propname, value)
435 except hyperdb.HyperdbValueError as msg: 437 except hyperdb.HyperdbValueError as msg:
436 raise FormError (msg) 438 raise FormError(msg)
437 439
438 # now use that list of ids to modify the multilink 440 # now use that list of ids to modify the multilink
439 if mlaction == 'set': 441 if mlaction == 'set':
440 value = l 442 value = l
441 else: 443 else:
453 # the list 455 # the list
454 for entry in l: 456 for entry in l:
455 try: 457 try:
456 existing.remove(entry) 458 existing.remove(entry)
457 except ValueError: 459 except ValueError:
458 raise FormError (self._('property ' 460 raise FormError(self._('property '
459 '"%(propname)s": "%(value)s" ' 461 '"%(propname)s": "%(value)s" '
460 'not currently in list') % { 462 'not currently in list') % {
461 'propname': propname, 'value': entry}) 463 'propname': propname, 'value': entry})
462 else: 464 else:
463 # add - easy, just don't dupe 465 # add - easy, just don't dupe
480 hasattr(value, 'filename') and 482 hasattr(value, 'filename') and
481 value.filename is not None): 483 value.filename is not None):
482 value = self.parse_file(propdef, props, value) 484 value = self.parse_file(propdef, props, value)
483 else: 485 else:
484 value = hyperdb.rawToHyperdb(self.db, cl, nodeid, 486 value = hyperdb.rawToHyperdb(self.db, cl, nodeid,
485 propname, value) 487 propname, value)
486 except hyperdb.HyperdbValueError as msg: 488 except hyperdb.HyperdbValueError as msg:
487 raise FormError (msg) 489 raise FormError(msg)
488 490
489 # register that we got this property 491 # register that we got this property
490 if isinstance(proptype, hyperdb.Multilink): 492 if isinstance(proptype, hyperdb.Multilink):
491 if value != []: 493 if value != []:
492 got_props[this][propname] = 1 494 got_props[this][propname] = 1
514 existing.sort(key=int) 516 existing.sort(key=int)
515 517
516 # "missing" existing values may not be None 518 # "missing" existing values may not be None
517 if not existing: 519 if not existing:
518 if isinstance(proptype, hyperdb.String): 520 if isinstance(proptype, hyperdb.String):
519 # some backends store "missing" Strings as empty strings 521 # some backends store "missing" Strings as
522 # empty strings
520 if existing == self.db.BACKEND_MISSING_STRING: 523 if existing == self.db.BACKEND_MISSING_STRING:
521 existing = None 524 existing = None
522 elif isinstance(proptype, hyperdb.Number) or isinstance(proptype, hyperdb.Integer): 525 elif isinstance(proptype, hyperdb.Number) or \
526 isinstance(proptype, hyperdb.Integer):
523 # some backends store "missing" Numbers as 0 :( 527 # some backends store "missing" Numbers as 0 :(
524 if existing == self.db.BACKEND_MISSING_NUMBER: 528 if existing == self.db.BACKEND_MISSING_NUMBER:
525 existing = None 529 existing = None
526 elif isinstance(proptype, hyperdb.Boolean): 530 elif isinstance(proptype, hyperdb.Boolean):
527 # likewise Booleans 531 # likewise Booleans
581 ) % { 585 ) % {
582 'class': self._(thing[0]), 586 'class': self._(thing[0]),
583 'property': ', '.join(map(self.gettext, required)) 587 'property': ', '.join(map(self.gettext, required))
584 }) 588 })
585 if s: 589 if s:
586 raise FormError ('\n'.join(s)) 590 raise FormError('\n'.join(s))
587 591
588 # When creating a FileClass node, it should have a non-empty content 592 # When creating a FileClass node, it should have a non-empty content
589 # property to be created. When editing a FileClass node, it should 593 # property to be created. When editing a FileClass node, it should
590 # either have a non-empty content property or no property at all. In 594 # either have a non-empty content property or no property at all. In
591 # the latter case, nothing will change. 595 # the latter case, nothing will change.
608 if id is not None and \ 612 if id is not None and \
609 not id.startswith('-') and \ 613 not id.startswith('-') and \
610 not props['content']: 614 not props['content']:
611 # This is an existing file with emtpy content 615 # This is an existing file with emtpy content
612 # value in the form. 616 # value in the form.
613 del props ['content'] 617 del props['content']
614 else: 618 else:
615 # this is a new file without any content property. 619 # this is a new file without any content property.
616 if id is not None and id.startswith('-'): 620 if id is not None and id.startswith('-'):
617 del all_props[(cn, id)] 621 del all_props[(cn, id)]
618 # if this is a new file with content (even 0 length content) 622 # if this is a new file with content (even 0 length content)
637 641
638 def extractFormList(self, value): 642 def extractFormList(self, value):
639 ''' Extract a list of values from the form value. 643 ''' Extract a list of values from the form value.
640 644
641 It may be one of: 645 It may be one of:
642 [MiniFieldStorage('value'), MiniFieldStorage('value','value',...), ...] 646 [MiniFieldStorage('value'),
647 MiniFieldStorage('value','value',...), ...]
643 MiniFieldStorage('value,value,...') 648 MiniFieldStorage('value,value,...')
644 MiniFieldStorage('value') 649 MiniFieldStorage('value')
645 ''' 650 '''
646 # multiple values are OK 651 # multiple values are OK
647 if isinstance(value, type([])): 652 if isinstance(value, type([])):

Roundup Issue Tracker: http://roundup-tracker.org/