Mercurial > p > roundup > code
comparison roundup/cgi/client.py @ 1055:cf72eae57a2c
Fixed instance installation
... moved the htmlbase module into templates and call it
<template>_htmlbase.py ... no more try/except in instance __init__!
Added :required to form handling.
Handle multiple values for single form items with decent error report.
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Thu, 05 Sep 2002 23:39:14 +0000 |
| parents | 3d8ea16347aa |
| children | 1c2a2ecc1b03 |
comparison
equal
deleted
inserted
replaced
| 1054:3d8ea16347aa | 1055:cf72eae57a2c |
|---|---|
| 1 # $Id: client.py,v 1.14 2002-09-05 05:25:23 richard Exp $ | 1 # $Id: client.py,v 1.15 2002-09-05 23:39:12 richard Exp $ |
| 2 | 2 |
| 3 __doc__ = """ | 3 __doc__ = """ |
| 4 WWW request handler (also used in the stand-alone server). | 4 WWW request handler (also used in the stand-alone server). |
| 5 """ | 5 """ |
| 6 | 6 |
| 112 4. render a template, resulting in HTML output | 112 4. render a template, resulting in HTML output |
| 113 | 113 |
| 114 In some situations, exceptions occur: | 114 In some situations, exceptions occur: |
| 115 - HTTP Redirect (generally raised by an action) | 115 - HTTP Redirect (generally raised by an action) |
| 116 - SendFile (generally raised by determine_context) | 116 - SendFile (generally raised by determine_context) |
| 117 serve up a FileClass "content" property | |
| 117 - SendStaticFile (generally raised by determine_context) | 118 - SendStaticFile (generally raised by determine_context) |
| 118 - Unauthorised (raised pretty much anywhere it needs to be) | 119 serve up a file from the tracker "html" directory |
| 119 - NotFound (see above... percolates up to the CGI interface) | 120 - Unauthorised (generally raised by an action) |
| 121 the action is cancelled, the request is rendered and an error | |
| 122 message is displayed indicating that permission was not | |
| 123 granted for the action to take place | |
| 124 - NotFound (raised wherever it needs to be) | |
| 125 percolates up to the CGI interface that called the client | |
| 120 ''' | 126 ''' |
| 121 self.content_action = None | 127 self.content_action = None |
| 122 self.ok_message = [] | 128 self.ok_message = [] |
| 123 self.error_message = [] | 129 self.error_message = [] |
| 124 try: | 130 try: |
| 579 "messages" property. | 585 "messages" property. |
| 580 __file | 586 __file |
| 581 Create a file and attach it to the current node's | 587 Create a file and attach it to the current node's |
| 582 "files" property. Attach the file to the message created from | 588 "files" property. Attach the file to the message created from |
| 583 the __note if it's supplied. | 589 the __note if it's supplied. |
| 590 | |
| 591 :required=property,property,... | |
| 592 The named properties are required to be filled in the form. | |
| 593 | |
| 584 ''' | 594 ''' |
| 585 cl = self.db.classes[self.classname] | 595 cl = self.db.classes[self.classname] |
| 586 | 596 |
| 587 # parse the props from the form | 597 # parse the props from the form |
| 588 try: | 598 try: |
| 651 return 0 | 661 return 0 |
| 652 | 662 |
| 653 def newItemAction(self): | 663 def newItemAction(self): |
| 654 ''' Add a new item to the database. | 664 ''' Add a new item to the database. |
| 655 | 665 |
| 656 This follows the same form as the editItemAction | 666 This follows the same form as the editItemAction, with the same |
| 667 special form values. | |
| 657 ''' | 668 ''' |
| 658 cl = self.db.classes[self.classname] | 669 cl = self.db.classes[self.classname] |
| 659 | 670 |
| 660 # parse the props from the form | 671 # parse the props from the form |
| 661 try: | 672 try: |
| 1031 link = self.db.classes[link] | 1042 link = self.db.classes[link] |
| 1032 link.set(nodeid, **{property: nid}) | 1043 link.set(nodeid, **{property: nid}) |
| 1033 | 1044 |
| 1034 | 1045 |
| 1035 def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')): | 1046 def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')): |
| 1036 '''Pull properties for the given class out of the form. | 1047 ''' Pull properties for the given class out of the form. |
| 1048 | |
| 1049 If a ":required" parameter is supplied, then the names property values | |
| 1050 must be supplied or a ValueError will be raised. | |
| 1037 ''' | 1051 ''' |
| 1052 required = [] | |
| 1053 if form.has_key(':required'): | |
| 1054 value = form[':required'] | |
| 1055 if isinstance(value, type([])): | |
| 1056 required = [i.value.strip() for i in value] | |
| 1057 else: | |
| 1058 required = [i.strip() for i in value.value.split(',')] | |
| 1059 | |
| 1038 props = {} | 1060 props = {} |
| 1039 keys = form.keys() | 1061 keys = form.keys() |
| 1040 for key in keys: | 1062 for key in keys: |
| 1041 if not cl.properties.has_key(key): | 1063 if not cl.properties.has_key(key): |
| 1042 continue | 1064 continue |
| 1043 proptype = cl.properties[key] | 1065 proptype = cl.properties[key] |
| 1066 | |
| 1067 # Get the form value. This value may be a MiniFieldStorage or a list | |
| 1068 # of MiniFieldStorages. | |
| 1069 value = form[key] | |
| 1070 | |
| 1071 # make sure non-multilinks only get one value | |
| 1072 if not isinstance(proptype, hyperdb.Multilink): | |
| 1073 if isinstance(value, type([])): | |
| 1074 raise ValueError, 'You have submitted more than one value'\ | |
| 1075 ' for the %s property'%key | |
| 1076 # we've got a MiniFieldStorage, so pull out the value and strip | |
| 1077 # surrounding whitespace | |
| 1078 value = value.value.strip() | |
| 1079 | |
| 1044 if isinstance(proptype, hyperdb.String): | 1080 if isinstance(proptype, hyperdb.String): |
| 1081 pass | |
| 1045 value = form[key].value.strip() | 1082 value = form[key].value.strip() |
| 1046 elif isinstance(proptype, hyperdb.Password): | 1083 elif isinstance(proptype, hyperdb.Password): |
| 1047 value = form[key].value.strip() | |
| 1048 if not value: | 1084 if not value: |
| 1049 # ignore empty password values | 1085 # ignore empty password values |
| 1050 continue | 1086 continue |
| 1051 value = password.Password(value) | 1087 value = password.Password(value) |
| 1052 elif isinstance(proptype, hyperdb.Date): | 1088 elif isinstance(proptype, hyperdb.Date): |
| 1053 value = form[key].value.strip() | |
| 1054 if value: | 1089 if value: |
| 1055 value = date.Date(form[key].value.strip()) | 1090 value = date.Date(form[key].value.strip()) |
| 1056 else: | 1091 else: |
| 1057 value = None | 1092 value = None |
| 1058 elif isinstance(proptype, hyperdb.Interval): | 1093 elif isinstance(proptype, hyperdb.Interval): |
| 1059 value = form[key].value.strip() | |
| 1060 if value: | 1094 if value: |
| 1061 value = date.Interval(form[key].value.strip()) | 1095 value = date.Interval(form[key].value.strip()) |
| 1062 else: | 1096 else: |
| 1063 value = None | 1097 value = None |
| 1064 elif isinstance(proptype, hyperdb.Link): | 1098 elif isinstance(proptype, hyperdb.Link): |
| 1065 value = form[key].value.strip() | |
| 1066 # see if it's the "no selection" choice | 1099 # see if it's the "no selection" choice |
| 1067 if value == '-1': | 1100 if value == '-1': |
| 1068 value = None | 1101 value = None |
| 1069 else: | 1102 else: |
| 1070 # handle key values | 1103 # handle key values |
| 1075 except KeyError: | 1108 except KeyError: |
| 1076 raise ValueError, _('property "%(propname)s": ' | 1109 raise ValueError, _('property "%(propname)s": ' |
| 1077 '%(value)s not a %(classname)s')%{'propname':key, | 1110 '%(value)s not a %(classname)s')%{'propname':key, |
| 1078 'value': value, 'classname': link} | 1111 'value': value, 'classname': link} |
| 1079 elif isinstance(proptype, hyperdb.Multilink): | 1112 elif isinstance(proptype, hyperdb.Multilink): |
| 1080 value = form[key] | 1113 if isinstance(value, type([])): |
| 1081 if not isinstance(value, type([])): | 1114 # it's a list of MiniFieldStorages |
| 1115 value = [i.value.strip() for i in value] | |
| 1116 else: | |
| 1117 # it's a MiniFieldStorage, but may be a comma-separated list | |
| 1118 # of values | |
| 1082 value = [i.strip() for i in value.value.split(',')] | 1119 value = [i.strip() for i in value.value.split(',')] |
| 1083 else: | |
| 1084 value = [i.value.strip() for i in value] | |
| 1085 link = cl.properties[key].classname | 1120 link = cl.properties[key].classname |
| 1086 l = [] | 1121 l = [] |
| 1087 for entry in map(str, value): | 1122 for entry in map(str, value): |
| 1088 if entry == '': continue | 1123 if entry == '': continue |
| 1089 if not num_re.match(entry): | 1124 if not num_re.match(entry): |
| 1095 'propname':key, 'value': entry, 'classname': link} | 1130 'propname':key, 'value': entry, 'classname': link} |
| 1096 l.append(entry) | 1131 l.append(entry) |
| 1097 l.sort() | 1132 l.sort() |
| 1098 value = l | 1133 value = l |
| 1099 elif isinstance(proptype, hyperdb.Boolean): | 1134 elif isinstance(proptype, hyperdb.Boolean): |
| 1100 value = form[key].value.strip() | |
| 1101 props[key] = value = value.lower() in ('yes', 'true', 'on', '1') | 1135 props[key] = value = value.lower() in ('yes', 'true', 'on', '1') |
| 1102 elif isinstance(proptype, hyperdb.Number): | 1136 elif isinstance(proptype, hyperdb.Number): |
| 1103 value = form[key].value.strip() | |
| 1104 props[key] = value = int(value) | 1137 props[key] = value = int(value) |
| 1105 | 1138 |
| 1106 # get the old value | 1139 # get the old value |
| 1107 if nodeid: | 1140 if nodeid: |
| 1108 try: | 1141 try: |
| 1115 # if changed, set it | 1148 # if changed, set it |
| 1116 if value != existing: | 1149 if value != existing: |
| 1117 props[key] = value | 1150 props[key] = value |
| 1118 else: | 1151 else: |
| 1119 props[key] = value | 1152 props[key] = value |
| 1153 | |
| 1154 # see if all the required properties have been supplied | |
| 1155 l = [] | |
| 1156 for property in required: | |
| 1157 if not props.has_key(property): | |
| 1158 l.append(property) | |
| 1159 if l: | |
| 1160 raise ValueError, 'Required properties %s not supplied'%(', '.join(l)) | |
| 1161 | |
| 1120 return props | 1162 return props |
| 1121 | 1163 |
| 1122 | 1164 |
