Skip to content

Commit 643d586

Browse files
committed
issue1408570: fix that form values are lost
.. on edit exceptions. This occured for example if editing an issue with the classic template and setting 'superseder' to a non-existing issue number. All changes to the form where the original field was non-empty were lost.
1 parent f88f7e2 commit 643d586

File tree

4 files changed

+95
-6
lines changed

4 files changed

+95
-6
lines changed

CHANGES.txt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,11 @@ Fixed:
317317
%20. Fixes to allow a url_query method to be applied to
318318
HTMLStringProperty to properly quote string values passed as part of
319319
a url.
320-
- issue2550755: exceptions.NotFound(msg) msg is not reported to user
321-
in cgi. When an invalid column is specified return error code 400
322-
rather than 404. Make error code 400 also return an error message to
323-
the user. Reported by: Bernhard Reiter, analysis, fix by John Rouillard.
320+
- issue1408570: Finally fix that form values are lost on edit
321+
exceptions. This occured for example if editing an issue with the
322+
classic template and setting 'superseder' to a non-existing issue
323+
number. All changes to the form where the original field was non-empty
324+
were lost.
324325

325326
2016-01-11: 1.5.1
326327

roundup/cgi/client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ def __init__(self, instance, request, env, form=None, translator=None):
295295
self.env = env
296296
self.setTranslator(translator)
297297
self.mailer = Mailer(instance.config)
298+
# If True the form contents wins over the database contents when
299+
# rendering html properties. This is set when an error occurs so
300+
# that we don't lose submitted form contents.
301+
self.form_wins = False
298302

299303
# save off the path
300304
self.path = env['PATH_INFO']
@@ -421,6 +425,9 @@ def add_ok_message(self, msg, escape=True):
421425

422426
def add_error_message(self, msg, escape=True):
423427
add_message(self._error_message, msg, escape)
428+
# Want to interpret form values when rendering when an error
429+
# occurred:
430+
self.form_wins = True
424431

425432
def inner_main(self):
426433
"""Process a request.

roundup/cgi/templating.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1251,7 +1251,7 @@ def __init__(self, client, classname, nodeid, prop, name, value,
12511251
is_in = form.has_key(self._formname)
12521252
except TypeError:
12531253
is_in = False
1254-
if not self._value and is_in:
1254+
if is_in and (not self._value or self._client.form_wins):
12551255
if isinstance(prop, hyperdb.Multilink):
12561256
value = lookupIds(self._db, prop,
12571257
handleListCGIValue(form[self._formname]),

test/test_cgi.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
from roundup.cgi import client, actions, exceptions
1414
from roundup.cgi.exceptions import FormError
1515
from roundup.cgi.templating import HTMLItem, HTMLRequest, NoTemplate
16+
from roundup.cgi.templating import HTMLProperty, _HTMLItem
1617
from roundup.cgi.form_parser import FormParser
1718
from roundup import init, instance, password, hyperdb, date
1819

20+
# For testing very simple rendering
21+
from roundup.cgi.engine_zopetal import RoundupPageTemplate
22+
1923
from mocknull import MockNull
2024

2125
import db_test_base
@@ -117,13 +121,20 @@ def setUp(self):
117121
self.FV_SPECIAL = re.compile(FormParser.FV_LABELS%classes,
118122
re.VERBOSE)
119123

120-
def parseForm(self, form, classname='test', nodeid=None):
124+
def setupClient(self, form, classname, nodeid=None, template='item'):
121125
cl = client.Client(self.instance, None, {'PATH_INFO':'/',
122126
'REQUEST_METHOD':'POST'}, makeForm(form))
123127
cl.classname = classname
124128
cl.nodeid = nodeid
125129
cl.language = ('en',)
130+
cl.userid = '1'
126131
cl.db = self.db
132+
cl.user = 'admin'
133+
cl.template = template
134+
return cl
135+
136+
def parseForm(self, form, classname='test', nodeid=None):
137+
cl = self.setupClient(form, classname, nodeid)
127138
return cl.parsePropsFromForm(create=1)
128139

129140
def tearDown(self):
@@ -772,6 +783,76 @@ def testBackwardsCompat(self):
772783
'name': 'foo.txt', 'type': 'text/plain'}},
773784
[('issue', None, 'files', [('file', '-1')])]))
774785

786+
def testFormValuePreserveOnError(self):
787+
page_template = """
788+
<html>
789+
<body>
790+
<p tal:condition="options/error_message|nothing"
791+
tal:repeat="m options/error_message"
792+
tal:content="structure m"/>
793+
<p tal:content="context/title/plain"/>
794+
<p tal:content="context/priority/plain"/>
795+
<p tal:content="context/status/plain"/>
796+
<p tal:content="context/nosy/plain"/>
797+
<p tal:content="context/keyword/plain"/>
798+
<p tal:content="structure context/superseder/field"/>
799+
</body>
800+
</html>
801+
""".strip ()
802+
self.db.keyword.create (name = 'key1')
803+
self.db.keyword.create (name = 'key2')
804+
nodeid = self.db.issue.create (title = 'Title', priority = '1',
805+
status = '1', nosy = ['1'], keyword = ['1'])
806+
self.db.commit ()
807+
form = {':note': 'msg-content', 'title': 'New title',
808+
'priority': '2', 'status': '2', 'nosy': '1,2', 'keyword': '',
809+
'superseder': '5000', ':action': 'edit'}
810+
cl = self.setupClient(form, 'issue', '1')
811+
pt = RoundupPageTemplate()
812+
pt.pt_edit(page_template, 'text/html')
813+
out = []
814+
def wh(s):
815+
out.append(s)
816+
cl.write_html = wh
817+
# Enable the following if we get a templating error:
818+
#def send_error (*args, **kw):
819+
# import pdb; pdb.set_trace()
820+
#cl.send_error_to_admin = send_error
821+
# Need to rollback the database on error -- this usually happens
822+
# in web-interface (and for other databases) anyway, need it for
823+
# testing that the form values are really used, not the database!
824+
# We do this together with the setup of the easy template above
825+
def load_template(x):
826+
cl.db.rollback()
827+
return pt
828+
cl.instance.templates.load = load_template
829+
cl.selectTemplate = MockNull()
830+
cl.determine_context = MockNull ()
831+
def hasPermission(s, p, classname=None, d=None, e=None, **kw):
832+
return True
833+
actions.Action.hasPermission = hasPermission
834+
e1 = _HTMLItem.is_edit_ok
835+
_HTMLItem.is_edit_ok = lambda x : True
836+
e2 = HTMLProperty.is_edit_ok
837+
HTMLProperty.is_edit_ok = lambda x : True
838+
cl.inner_main()
839+
_HTMLItem.is_edit_ok = e1
840+
HTMLProperty.is_edit_ok = e2
841+
self.assertEqual(len(out), 1)
842+
self.assertEqual(out [0].strip (), """
843+
<html>
844+
<body>
845+
<p>Edit Error: issue has no node 5000</p>
846+
<p>New title</p>
847+
<p>urgent</p>
848+
<p>deferred</p>
849+
<p>admin, anonymous</p>
850+
<p></p>
851+
<p><input type="text" name="superseder" value="5000" size="30"></p>
852+
</body>
853+
</html>
854+
""".strip ())
855+
775856
#
776857
# SECURITY
777858
#

0 commit comments

Comments
 (0)