Skip to content

Commit a2d08f6

Browse files
author
Richard Jones
committed
Form handling now performs all actions in one go.
(That is, property setting including linking.) We figure out the linking dependencies and create items in an appropriate order.
1 parent 9f4b808 commit a2d08f6

File tree

1 file changed

+143
-95
lines changed

1 file changed

+143
-95
lines changed

roundup/cgi/client.py

Lines changed: 143 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: client.py,v 1.91 2003-02-18 01:59:10 richard Exp $
1+
# $Id: client.py,v 1.92 2003-02-18 03:58:18 richard Exp $
22

33
__doc__ = """
44
WWW request handler (also used in the stand-alone server).
@@ -778,16 +778,13 @@ def newItemAction(self):
778778
# try:
779779
if 1:
780780
# create the context here
781-
cn = self.classname
782-
nid = self._createnode(cn, props[(cn, None)])
783-
del props[(cn, None)]
781+
# cn = self.classname
782+
# nid = self._createnode(cn, props[(cn, None)])
783+
# del props[(cn, None)]
784784

785-
extra = self._editnodes(props, links, {(cn, None): nid})
786-
if extra:
787-
extra = '<br>' + extra
785+
# when it hits the None element, it'll set self.nodeid
786+
messages = self._editnodes(props, links) #, {(cn, None): nid})
788787

789-
# now do the rest
790-
messages = '%s %s created'%(cn, nid) + extra
791788
# except (ValueError, KeyError, IndexError), message:
792789
# # these errors might just be indicative of user dumbness
793790
# self.error_message.append(_('Error: ') + str(message))
@@ -798,7 +795,7 @@ def newItemAction(self):
798795

799796
# redirect to the new item's page
800797
raise Redirect, '%s%s%s?@ok_message=%s'%(self.base, self.classname,
801-
nid, urllib.quote(messages))
798+
self.nodeid, urllib.quote(messages))
802799

803800
def newItemPermission(self, props):
804801
''' Determine whether the user has permission to create (edit) this
@@ -816,6 +813,119 @@ def newItemPermission(self, props):
816813
return 1
817814
return 0
818815

816+
817+
#
818+
# Utility methods for editing
819+
#
820+
def _editnodes(self, all_props, all_links, newids=None):
821+
''' Use the props in all_props to perform edit and creation, then
822+
use the link specs in all_links to do linking.
823+
'''
824+
# figure dependencies and re-work links
825+
deps = {}
826+
links = {}
827+
for cn, nodeid, propname, vlist in all_links:
828+
for value in vlist:
829+
deps.setdefault((cn, nodeid), []).append(value)
830+
links.setdefault(value, []).append((cn, nodeid, propname))
831+
832+
# figure chained dependencies ordering
833+
order = []
834+
done = {}
835+
# loop detection
836+
change = 0
837+
while len(all_props) != len(done):
838+
for needed in all_props.keys():
839+
if done.has_key(needed):
840+
continue
841+
tlist = deps.get(needed, [])
842+
for target in tlist:
843+
if not done.has_key(target):
844+
break
845+
else:
846+
done[needed] = 1
847+
order.append(needed)
848+
change = 1
849+
if not change:
850+
raise ValueError, 'linking must not loop!'
851+
852+
# now, edit / create
853+
m = []
854+
for needed in order:
855+
props = all_props[needed]
856+
cn, nodeid = needed
857+
858+
if nodeid is not None and int(nodeid) > 0:
859+
# make changes to the node
860+
props = self._changenode(cn, nodeid, props)
861+
862+
# and some nice feedback for the user
863+
if props:
864+
info = ', '.join(props.keys())
865+
m.append('%s %s %s edited ok'%(cn, nodeid, info))
866+
else:
867+
m.append('%s %s - nothing changed'%(cn, nodeid))
868+
else:
869+
assert props
870+
871+
# make a new node
872+
newid = self._createnode(cn, props)
873+
if nodeid is None:
874+
self.nodeid = newid
875+
nodeid = newid
876+
877+
# and some nice feedback for the user
878+
m.append('%s %s created'%(cn, newid))
879+
880+
# fill in new ids in links
881+
if links.has_key(needed):
882+
for linkcn, linkid, linkprop in links[needed]:
883+
props = all_props[(linkcn, linkid)]
884+
cl = self.db.classes[linkcn]
885+
propdef = cl.getprops()[linkprop]
886+
if not props.has_key(linkprop):
887+
if linkid is None or linkid.startswith('-'):
888+
# linking to a new item
889+
if isinstance(propdef, hyperdb.Multilink):
890+
props[linkprop] = [newid]
891+
else:
892+
props[linkprop] = newid
893+
else:
894+
# linking to an existing item
895+
if isinstance(propdef, hyperdb.Multilink):
896+
existing = cl.get(linkid, linkprop)[:]
897+
existing.append(nodeid)
898+
props[linkprop] = existing
899+
else:
900+
props[linkprop] = newid
901+
902+
return '<br>'.join(m)
903+
904+
def _changenode(self, cn, nodeid, props):
905+
''' change the node based on the contents of the form
906+
'''
907+
# check for permission
908+
if not self.editItemPermission(props):
909+
raise PermissionError, 'You do not have permission to edit %s'%cn
910+
911+
# make the changes
912+
cl = self.db.classes[cn]
913+
return cl.set(nodeid, **props)
914+
915+
def _createnode(self, cn, props):
916+
''' create a node based on the contents of the form
917+
'''
918+
# check for permission
919+
if not self.newItemPermission(props):
920+
raise PermissionError, 'You do not have permission to create %s'%cn
921+
922+
# create the node and return its id
923+
cl = self.db.classes[cn]
924+
return cl.create(**props)
925+
926+
#
927+
# More actions
928+
#
819929
def editCSVAction(self):
820930
''' Performs an edit of all of a class' items in one go.
821931
@@ -1023,90 +1133,6 @@ def showAction(self):
10231133
url = '%s%s%s'%(self.db.config.TRACKER_WEB, t, n)
10241134
raise Redirect, url
10251135

1026-
1027-
#
1028-
# Utility methods for editing
1029-
#
1030-
def _editnodes(self, all_props, all_links, newids=None):
1031-
''' Use the props in all_props to perform edit and creation, then
1032-
use the link specs in all_links to do linking.
1033-
'''
1034-
m = []
1035-
if newids is None:
1036-
newids = {}
1037-
for (cn, nodeid), props in all_props.items():
1038-
if int(nodeid) > 0:
1039-
# make changes to the node
1040-
props = self._changenode(cn, nodeid, props)
1041-
1042-
# and some nice feedback for the user
1043-
if props:
1044-
info = ', '.join(props.keys())
1045-
m.append('%s %s %s edited ok'%(cn, nodeid, info))
1046-
else:
1047-
m.append('%s %s - nothing changed'%(cn, nodeid))
1048-
elif props:
1049-
# make a new node
1050-
newid = self._createnode(cn, props)
1051-
newids[(cn, nodeid)] = newid
1052-
nodeid = newid
1053-
1054-
# and some nice feedback for the user
1055-
m.append('%s %s created'%(cn, newid))
1056-
1057-
# handle linked nodes
1058-
keys = self.form.keys()
1059-
for cn, nodeid, propname, value in all_links:
1060-
cl = self.db.classes[cn]
1061-
property = cl.getprops()[propname]
1062-
if nodeid is None or nodeid.startswith('-'):
1063-
if not newids.has_key((cn, nodeid)):
1064-
continue
1065-
nodeid = newids[(cn, nodeid)]
1066-
1067-
# map the desired classnames to their actual created ids
1068-
for link in value:
1069-
if not newids.has_key(link):
1070-
continue
1071-
linkid = newids[link]
1072-
if isinstance(property, hyperdb.Multilink):
1073-
# take a dupe of the list so we're not changing the cache
1074-
existing = cl.get(nodeid, propname)[:]
1075-
existing.append(linkid)
1076-
cl.set(nodeid, **{propname: existing})
1077-
elif isinstance(property, hyperdb.Link):
1078-
# make the Link set
1079-
cl.set(nodeid, **{propname: linkid})
1080-
else:
1081-
raise ValueError, '%s %s is not a link or multilink '\
1082-
'property'%(cn, propname)
1083-
m.append('%s %s linked to <a href="%s%s">%s %s</a>'%(
1084-
link[0], linkid, cn, nodeid, cn, nodeid))
1085-
1086-
return '<br>'.join(m)
1087-
1088-
def _changenode(self, cn, nodeid, props):
1089-
''' change the node based on the contents of the form
1090-
'''
1091-
# check for permission
1092-
if not self.editItemPermission(props):
1093-
raise PermissionError, 'You do not have permission to edit %s'%cn
1094-
1095-
# make the changes
1096-
cl = self.db.classes[cn]
1097-
return cl.set(nodeid, **props)
1098-
1099-
def _createnode(self, cn, props):
1100-
''' create a node based on the contents of the form
1101-
'''
1102-
# check for permission
1103-
if not self.newItemPermission(props):
1104-
raise PermissionError, 'You do not have permission to create %s'%cn
1105-
1106-
# create the node and return its id
1107-
cl = self.db.classes[cn]
1108-
return cl.create(**props)
1109-
11101136
def parsePropsFromForm(self, num_re=re.compile('^\d+$')):
11111137
''' Pull properties out of the form.
11121138
@@ -1267,6 +1293,13 @@ class <designator> (where <designator> must be
12671293
raise ValueError, \
12681294
'link "%s" value "%s" not a designator'%(key, entry)
12691295
value.append((m.group(1), m.group(2)))
1296+
1297+
# make sure the link property is valid
1298+
if (not isinstance(propdef, hyperdb.Multilink) and
1299+
not isinstance(propdef, hyperdb.Link)):
1300+
raise ValueError, '%s %s is not a link or '\
1301+
'multilink property'%(cn, propname)
1302+
12701303
all_links.append((cn, nodeid, propname, value))
12711304
continue
12721305

@@ -1525,7 +1558,22 @@ class <designator> (where <designator> must be
15251558
if not props.get('content', ''):
15261559
del all_props[(cn, id)]
15271560

1528-
return all_props, all_links
1561+
# clean up the links, removing ones that aren't possible
1562+
l = []
1563+
for entry in all_links:
1564+
(cn, nodeid, propname, destlist) = entry
1565+
source = (cn, nodeid)
1566+
if not all_props.has_key(source) or not all_props[source]:
1567+
# nothing to create - don't try to link
1568+
continue
1569+
# nothing to create - don't try to link
1570+
continue
1571+
for dest in destlist[:]:
1572+
if not all_props.has_key(dest) or not all_props[dest]:
1573+
destlist.remove(dest)
1574+
l.append(entry)
1575+
1576+
return all_props, l
15291577

15301578
def fixNewlines(text):
15311579
''' Homogenise line endings.

0 commit comments

Comments
 (0)