Skip to content

Commit 90b0bfd

Browse files
committed
Make @template support two alternate templates for error and ok cases.
Setting @template=oktmpl|errortmpl in a form will display the next page using the oktmpl if the change did not cause an error. If submitting the form caused an error (raised by an auditor or something else), the user is displayed a page using the errortmpl. Docs in customizing.tmpl. Look for modal edit.
1 parent 1229ab7 commit 90b0bfd

File tree

4 files changed

+105
-1
lines changed

4 files changed

+105
-1
lines changed

CHANGES.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ Features:
160160
string, the string is appended to the displayed option value. This
161161
allows the user to reset the value for the menu (select) to the
162162
original value. (John Rouillard)
163+
- @template html url parameter can be set to "oktmpl|errortmpl". When
164+
a form is submitted, if the form passes validation the oktmpl is
165+
used for the resulting page. If the form fails submission the
166+
errortmpl page is used to display the form. The errortmpl will
167+
usually be the same template used to edit the form. See the section
168+
on "Implementing Modal Editing Using @template" in
169+
``customizing.txt``. (John Rouillard)
163170

164171
Fixed:
165172

doc/customizing.txt

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1825,6 +1825,48 @@ template doesn't exit it will try to use
18251825
``test/_generic.item.html``. If that template doesn't exist
18261826
it will return an error.
18271827

1828+
Implementing Modal Editing Using @template
1829+
------------------------------------------
1830+
1831+
Many item templates allow you to edit the item. They contain
1832+
code that renders edit boxes if the user has edit permissions.
1833+
Otherwise the template will just display the item information.
1834+
1835+
In some cases you want to do a modal edit. The user has to take some
1836+
action (click a button or follow a link) to shift from display mode to
1837+
edit mode. When the changes are submitted, ending the edit mode,
1838+
the user is returned to display mode.
1839+
1840+
Modal workflows usually slow things down and are not implemented by
1841+
default templates. However for some workflows a modal edit is useful.
1842+
For example a batch edit mode that allows the user to edit a number of
1843+
issues all from one form could be implemented as a modal workflow of:
1844+
1845+
* search for issues to modify
1846+
* switch to edit mode and change values
1847+
* exit back to the results of the search
1848+
1849+
To implement the modal edit, assume you have an issue.edit.html
1850+
template that implements an edit form. On the display page (a version
1851+
of issue.item.html modified to only display information) add a link
1852+
that calls the display url, but adds ``@template=edit`` to the link.
1853+
1854+
This will now display the edit page. On the edit page you want to add
1855+
a hidden text field to your form named ``@template`` with the value:
1856+
``item|edit``. When the form is submitted it is validated. If the
1857+
form is correct the user will see the item rendered using the item
1858+
template. If there is an error (validation failed) the item will be
1859+
rendered using the edit template. The edit template that is rendered
1860+
will display all the changes that the user made to the form before it
1861+
was submitted. The user can correct the error and resubmit the changes
1862+
until the form validates.
1863+
1864+
If the form failed to validate but the ``@template`` field had the
1865+
value ``item`` the user would still see the error, but all of the data
1866+
the user entered would be discarded. The user would have to redo all
1867+
the edits again.
1868+
1869+
18281870
How the templates work
18291871
----------------------
18301872

@@ -2219,7 +2261,7 @@ list lists all of the active (not retired) items in the class.
22192261
csv return the items of this class as a chunk of CSV text.
22202262
propnames lists the names of the properties of this class.
22212263
filter lists of items from this class, filtered and sorted. Two
2222-
options are avaible for sorting:
2264+
options are available for sorting:
22232265

22242266
1. by the current *request* filterspec/filter/sort/group args
22252267
2. by the "filterspec", "sort" and "group" keyword args.

roundup/cgi/client.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,13 @@ def selectTemplate(self, name, view):
11631163
classname (name parameter) and template request variable
11641164
(view parameter) and return its name.
11651165
1166+
View can be a single template or two templates separated
1167+
by a vbar '|' character. If the Client object has a
1168+
non-empty _error_message attribute, the right hand
1169+
template (error template) will be used. If the
1170+
_error_message is empty, the left hand template (ok
1171+
template) will be used.
1172+
11661173
In most cases the name will be "classname.view", but
11671174
if "view" is None, then template name "classname" will
11681175
be returned.
@@ -1172,6 +1179,20 @@ def selectTemplate(self, name, view):
11721179
11731180
[ ] cover with tests
11741181
"""
1182+
1183+
# determine if view is oktmpl|errortmpl. If so assign the
1184+
# right one to the view parameter. If we don't have alternate
1185+
# templates, just leave view alone.
1186+
if (view.find('|') != -1 ):
1187+
# we have alternate templates, parse them apart.
1188+
(oktmpl, errortmpl) = view.split("|", 2)
1189+
if self._error_message:
1190+
# we have an error, use errortmpl
1191+
view = errortmpl
1192+
else:
1193+
# no error message recorded, use oktmpl
1194+
view = oktmpl
1195+
11751196
loader = self.instance.templates
11761197

11771198
# if classname is not set, use "home" template

test/test_cgi.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,40 @@ def testrenderContext(self):
12691269
self.assertNotEqual(-1, result.index('ok message'))
12701270
# print result
12711271

1272+
def testRenderAltTemplates(self):
1273+
# check that right page is returned when rendering
1274+
# @template=oktempl|errortmpl
1275+
1276+
# set up the client;
1277+
# run determine_context to set the required client attributes
1278+
# run renderContext(); check result for proper page
1279+
1280+
# Test ok state template that uses user.forgotten.html
1281+
self.client.form=makeForm({"@template": "forgotten|item"})
1282+
self.client.path = 'user'
1283+
self.client.determine_context()
1284+
self.assertEqual((self.client.classname, self.client.template, self.client.nodeid), ('user', 'forgotten|item', None))
1285+
self.assertEqual(self.client._ok_message, [])
1286+
1287+
result = self.client.renderContext()
1288+
self.assertNotEqual(-1,
1289+
result.index('<!-- SHA: 6fdb58c55fd854904ae98906d5935549a221fabf -->'))
1290+
1291+
# now set an error in the form to get error template user.item.html
1292+
self.client.form=makeForm({"@template": "forgotten|item",
1293+
"@error_message": "this is an error"})
1294+
self.client.path = 'user'
1295+
self.client.determine_context()
1296+
self.assertEqual((self.client.classname, self.client.template, self.client.nodeid), ('user', 'forgotten|item', None))
1297+
self.assertEqual(self.client._ok_message, [])
1298+
self.assertEqual(self.client._error_message, ["this is an error"])
1299+
1300+
result = self.client.renderContext()
1301+
print result
1302+
self.assertNotEqual(-1,
1303+
result.index('<!-- SHA: 3b7ce7cbf24f77733c9b9f64a569d6429390cc3f -->'))
1304+
1305+
12721306
def testexamine_url(self):
12731307
''' test the examine_url function '''
12741308

0 commit comments

Comments
 (0)