Merge lp:~bac/launchpad/bug-39212 into lp:launchpad

Proposed by Brad Crittenden
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 11478
Proposed branch: lp:~bac/launchpad/bug-39212
Merge into: lp:launchpad
Diff against target: 601 lines (+264/-198)
3 files modified
lib/lp/registry/browser/person.py (+130/-73)
lib/lp/registry/stories/person/xx-person-edit-wikis.txt (+86/-61)
lib/lp/registry/templates/person-editwikinames.pt (+48/-64)
To merge this branch: bzr merge lp:~bac/launchpad/bug-39212
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+34196@code.launchpad.net

Commit message

Change +editwikinames to be a LaunchpadFormView and eliminate some data modification inconsistency issues.

Description of the change

= Summary =

Several of the person/+editthingy pages pre-date LaunchpadFormView. In
a misguided attempt to be super-friendly, the pages allowed in-place
editing of lots of data. Doing so ran the risk that internal
inconsistencies in the submitted batch would cause a failure.

== Proposed fix ==

Change +editwikinames to be a LaunchpadFormView and only allow existing
data to be displayed and marked for removal.

== Pre-implementation notes ==

None. Similar to the recent change to +editjabberids

== Implementation details ==

As above.

== Tests ==

bin/test -vvm lp.registry -t xx-person-edit-wikis.txt

== Demo and Q/A ==

https://launchpad.dev/people/+me/+editwikinames

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/templates/person-editwikinames.pt
  lib/lp/registry/stories/person/xx-person-edit-wikis.txt
  lib/lp/registry/browser/person.py

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

Some things came up and were discussed on IRC. Else-wise, everything is fine (IRC transcript follows).

<gmb> bac, What's with the wacky indenting around line 93-96 of the diff?
<bac> gmb: dunno. let me look
 gmb: dewhacked
<gmb> bac, Eyefankyoo
 bac, ALso, line 109 needs to be indented by one more stop to be consistent.
<bac> gmb: i am going to open a but about URIWidget not respecting displayWidth, too.
 gmb, yep, i caught that one too
<gmb> bac, Thanks x2
<bac> i think we have css that is harmful and should be removed, but i'm not certain of all of the areas affected.
<gmb> Fair neough.
* gmb switches lcoations; brb
<gmb> bac, I thought that ReST headers just had to have the underline rather than the overline, too.
 e.g.
 Person's wikinames
 ===============
 (etc.)
<bac> gmb: really? i've often seen over/under
<gmb> bac, In that case, we have a problem with consistency
<bac> gmb: if you know better that's one less line...
<gmb> (Not least because I do underline only :))
<bac> gmb: duh
* gmb checks TestsStyleGuide
<gmb> bac, The example in TestsStyleGuide uses underline only, so let's standardise on that.
<bac> gmb: will do

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/person.py'
2--- lib/lp/registry/browser/person.py 2010-08-30 06:38:53 +0000
3+++ lib/lp/registry/browser/person.py 2010-08-31 14:04:45 +0000
4@@ -219,6 +219,7 @@
5 LaunchpadRadioWidgetWithDescription,
6 LocationWidget,
7 PasswordChangeWidget,
8+ URIWidget,
9 )
10 from canonical.widgets.image import ImageChangeWidget
11 from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget
12@@ -297,7 +298,10 @@
13 ITeamMembershipSet,
14 TeamMembershipStatus,
15 )
16-from lp.registry.interfaces.wikiname import IWikiNameSet
17+from lp.registry.interfaces.wikiname import (
18+ IWikiName,
19+ IWikiNameSet,
20+ )
21 from lp.services.fields import LocationField
22 from lp.services.openid.adapters.openid import CurrentOpenIDEndPoint
23 from lp.services.openid.browser.openiddiscovery import (
24@@ -3498,35 +3502,76 @@
25 sCoC_util.modifySignature(sig_id, self.user, comment, False)
26
27
28-class PersonEditWikiNamesView(LaunchpadView):
29+class PersonEditWikiNamesView(LaunchpadFormView):
30 """View for ~person/+editwikinames"""
31
32+ schema = IWikiName
33+ fields = ['wiki', 'wikiname']
34+ # Use custom widgets solely to get the width correct. The URIWidget has a
35+ # CSS class that does not respect the displayWidth, thus the need to use a
36+ # different cssClass.
37+ custom_widget('wiki', URIWidget, displayWidth=40, cssClass="textType")
38+ custom_widget('wikiname', TextWidget, displayWidth=40)
39+
40 @property
41 def label(self):
42 return smartquote("%s's wiki names" % self.context.displayname)
43
44 @property
45- def cancel_url(self):
46- return canonical_url(self.context, view_name="+edit")
47+ def next_url(self):
48+ return canonical_url(self.context)
49+
50+ cancel_url = next_url
51+
52+ def setUpFields(self):
53+ super(PersonEditWikiNamesView, self).setUpFields()
54+ if self.context.wiki_names.count() > 0:
55+ # Make the wiki and wiki_name entries optional on the edit page if
56+ # one or more ids already exist, which allows the removal of ids
57+ # without filling out the new wiki fields.
58+ wiki_field = self.form_fields['wiki']
59+ wikiname_field = self.form_fields['wikiname']
60+ # Copy the fields so as not to modify the interface.
61+ wiki_field.field = copy_field(wiki_field.field)
62+ wiki_field.field.required = False
63+ wikiname_field.field = copy_field(wikiname_field.field)
64+ wikiname_field.field.required = False
65
66 def _validateWikiURL(self, url):
67 """Validate the URL.
68
69 Make sure that the result is a valid URL with only the
70- appropriate schemes.
71+ appropriate schemes. The url is assumed to be a string.
72 """
73+ if url is None:
74+ return
75 try:
76 uri = URI(url)
77 if uri.scheme not in ('http', 'https'):
78- self.error_message = structured(
79- 'The URL scheme "%(scheme)s" is not allowed. '
80- 'Only http or https URLs may be used.', scheme=uri.scheme)
81- return False
82+ self.setFieldError(
83+ 'wiki',
84+ structured(
85+ 'The URL scheme "%(scheme)s" is not allowed. '
86+ 'Only http or https URLs may be used.',
87+ scheme=uri.scheme))
88 except InvalidURIError:
89- self.error_message = structured(
90- '"%(url)s" is not a valid URL.', url=url)
91- return False
92- return True
93+ self.setFieldError(
94+ 'wiki',
95+ structured(
96+ '"%(url)s" is not a valid URL.', url=url))
97+
98+ def _validateWikiName(self, name):
99+ """Ensure the wikiname is valid.
100+
101+ It must not be longer than 100 characters. Name is assumed to be a
102+ string.
103+ """
104+ max_len = 100
105+ if len(name) > max_len:
106+ self.setFieldError(
107+ 'wikiname',
108+ structured(
109+ 'The wiki name cannot exceed %d characters.' % max_len))
110
111 def _sanitizeWikiURL(self, url):
112 """Strip whitespaces and make sure :url ends in a single '/'."""
113@@ -3534,72 +3579,82 @@
114 return url
115 return '%s/' % url.strip().rstrip('/')
116
117- def initialize(self):
118- """Process the WikiNames form."""
119- self.error_message = None
120- if self.request.method != "POST":
121- # Nothing to do
122- return
123-
124- form = self.request.form
125- context = self.context
126+ def validate(self, data):
127+ # If there are already form errors then just show them.
128+ if self.errors:
129+ return
130+ wikiurl = self._sanitizeWikiURL(data.get('wiki'))
131+ wikiname = data.get('wikiname')
132+ if wikiurl or wikiname:
133+ if not wikiurl:
134+ self.setFieldError(
135+ 'wiki',
136+ structured(
137+ 'The Wiki URL must be specified.'))
138+ if not wikiname:
139+ self.setFieldError(
140+ 'wikiname',
141+ structured(
142+ 'The Wiki name must be specified.'))
143+
144+ if self.errors:
145+ return
146+
147+ if wikiurl is not None:
148+ self._validateWikiURL(wikiurl)
149+ if wikiname is not None:
150+ self._validateWikiName(wikiname)
151+
152+ if self.errors:
153+ return
154+
155 wikinameset = getUtility(IWikiNameSet)
156-
157- for w in context.wiki_names:
158- # XXX: GuilhermeSalgado 2005-08-25:
159- # We're exposing WikiName IDs here because that's the only
160- # unique column we have. If we don't do this we'll have to
161- # generate the field names using the WikiName.wiki and
162- # WikiName.wikiname columns (because these two columns make
163- # another unique identifier for WikiNames), but that's tricky and
164- # not worth the extra work.
165- if form.get('remove_%d' % w.id):
166- w.destroySelf()
167- else:
168- wiki = self._sanitizeWikiURL(form.get('wiki_%d' % w.id))
169- wikiname = form.get('wikiname_%d' % w.id)
170- if not (wiki and wikiname):
171- self.error_message = structured(
172- "Neither Wiki nor WikiName can be empty.")
173- return
174- if not self._validateWikiURL(wiki):
175- return
176- w.wiki = wiki
177- w.wikiname = wikiname
178-
179- wiki = self._sanitizeWikiURL(form.get('newwiki'))
180- wikiname = form.get('newwikiname')
181- if wiki or wikiname:
182- if wiki and wikiname:
183- existingwiki = wikinameset.getByWikiAndName(wiki, wikiname)
184- if existingwiki and existingwiki.person != context:
185- owner_name = urllib.quote(existingwiki.person.name)
186- merge_url = (
187- '%s/+requestmerge?field.dupe_person=%s'
188- % (canonical_url(getUtility(IPersonSet)), owner_name))
189- self.error_message = structured(
190+ existingwiki = wikinameset.getByWikiAndName(wikiurl, wikiname)
191+ if existingwiki:
192+ if existingwiki.person != self.context:
193+ owner_name = urllib.quote(existingwiki.person.name)
194+ merge_url = (
195+ '%s/+requestmerge?field.dupe_person=%s'
196+ % (canonical_url(getUtility(IPersonSet)), owner_name))
197+ self.setFieldError(
198+ 'wikiname',
199+ structured(
200 'The WikiName %s%s is already registered by '
201 '<a href="%s">%s</a>. If you think this is a '
202 'duplicated account, you can <a href="%s">merge it'
203 '</a> into your account.',
204- wiki, wikiname, canonical_url(existingwiki.person),
205- existingwiki.person.displayname, merge_url)
206- return
207-
208- elif existingwiki:
209- self.error_message = structured(
210- 'The WikiName %s%s already belongs to you.',
211- wiki, wikiname)
212- return
213- if not self._validateWikiURL(wiki):
214- return
215- wikinameset.new(context, wiki, wikiname)
216+ wikiurl, wikiname,
217+ canonical_url(existingwiki.person),
218+ existingwiki.person.displayname, merge_url))
219 else:
220- self.newwiki = wiki
221- self.newwikiname = wikiname
222- self.error_message = structured(
223- "Neither Wiki nor WikiName can be empty.")
224- return
225+ # The person already has this wiki.
226+ self.setFieldError(
227+ 'wikiname',
228+ 'The WikiName %s%s already belongs to you.' %
229+ (wikiurl, wikiname))
230+
231+ def _save(self, wikiurl, wikiname):
232+ """Given a wikiurl and wikiname, attempt to save it.
233+
234+ Verify someone else doesn't have it already.
235+ """
236+
237+ @action(_("Save Changes"), name="save")
238+ def save(self, action, data):
239+ """Process the wiki names form."""
240+ form = self.request.form
241+ for obj in self.context.wiki_names:
242+ if form.get('remove_%s' % obj.id):
243+ obj.destroySelf()
244+
245+ if not self.errors:
246+ wikiurl = self._sanitizeWikiURL(data.get('wiki'))
247+ wikiname = data.get('wikiname')
248+ # If either url or name are present then they both must be
249+ # entered.
250+ if wikiurl and wikiname:
251+ wikinameset = getUtility(IWikiNameSet)
252+ wikinameset.new(self.context, wikiurl, wikiname)
253
254
255 class PersonEditIRCNicknamesView(LaunchpadFormView):
256@@ -3664,6 +3719,8 @@
257 # ids already exist, which allows the removal of ids without
258 # filling out the new jabberid field.
259 jabber_field = self.form_fields['jabberid']
260+ # Copy the field so as not to modify the interface.
261+ jabber_field.field = copy_field(jabber_field.field)
262 jabber_field.field.required = False
263
264 @property
265
266=== modified file 'lib/lp/registry/stories/person/xx-person-edit-wikis.txt'
267--- lib/lp/registry/stories/person/xx-person-edit-wikis.txt 2009-11-15 20:27:01 +0000
268+++ lib/lp/registry/stories/person/xx-person-edit-wikis.txt 2010-08-31 14:04:45 +0000
269@@ -1,109 +1,134 @@
270-= Person's wikinames =
271+Person's wikinames
272+==================
273
274 A person can have any number of WikiNames registered in Launchpad, and
275 they can be managed on the +editwikinames page.
276
277 # A helper function for printing all wikinames of a person.
278- >>> import re
279- >>> def print_existing_wikinames(contents):
280- ... soup = find_main_content(contents)
281- ... wiki_url_inputs = soup.findAll(
282- ... 'input', attrs={'name': re.compile(r'wiki_\d+')})
283- ... wiki_urls = [input['value'] for input in wiki_url_inputs]
284- ... wiki_name_inputs = soup.findAll(
285- ... 'input', attrs={'name': re.compile(r'wikiname_\d+')})
286- ... wiki_names = [input['value'] for input in wiki_name_inputs]
287- ... wikis = zip(wiki_urls, wiki_names)
288- ... print '\n'.join('%s%s' % (wiki_url, wiki_name)
289- ... for wiki_url, wiki_name in wikis)
290+ >>> def print_existing_wikiurls(contents):
291+ ... trs = find_tags_by_class(contents, 'wikiurl')
292+ ... wikis = []
293+ ... for tr in trs:
294+ ... td = tr.find('td')
295+ ... wikis.append(td.contents[0])
296+ ... print '\n'.join(wikis)
297+
298+ >>> def print_feedback(browser):
299+ ... print "\n".join(get_feedback_messages(browser.contents))
300
301 Mark already has one WikiName registered.
302
303 >>> browser.addHeader('Authorization', 'Basic mark@example.com:test')
304- >>> browser.open('http://localhost/~mark/+editwikinames')
305- >>> print_existing_wikinames(browser.contents)
306+ >>> browser.open('http://launchpad.dev/~mark/+editwikinames')
307+ >>> print_existing_wikiurls(browser.contents)
308 https://wiki.ubuntu.com/MarkShuttleworth
309
310 But he wants to register another one.
311
312- >>> browser.getControl(name='newwiki').value = 'http://foo.bar/wiki/'
313- >>> browser.getControl(name='newwikiname').value = 'FooBar'
314+ >>> browser.getControl(name='field.wiki').value = 'http://foo.bar/wiki/'
315+ >>> browser.getControl(name='field.wikiname').value = 'FooBar'
316 >>> browser.getControl('Save Changes').click()
317- >>> browser.url
318- 'http://localhost/~mark/+editwikinames'
319- >>> print_existing_wikinames(browser.contents)
320+ >>> print_feedback(browser)
321+ >>> print browser.url
322+ http://launchpad.dev/~mark
323+ >>> browser.open('http://launchpad.dev/~mark/+editwikinames')
324+ >>> print_existing_wikiurls(browser.contents)
325 http://foo.bar/wiki/FooBar
326 https://wiki.ubuntu.com/MarkShuttleworth
327
328 He can't have two identical wiki names, though.
329
330- >>> browser.getControl(name='newwiki').value = 'http://foo.bar/wiki/'
331- >>> browser.getControl(name='newwikiname').value = 'FooBar'
332+ >>> browser.getControl(name='field.wiki').value = 'http://foo.bar/wiki/'
333+ >>> browser.getControl(name='field.wikiname').value = 'FooBar'
334 >>> browser.getControl('Save Changes').click()
335- >>> browser.url
336- 'http://localhost/~mark/+editwikinames'
337+ >>> print browser.url
338+ http://launchpad.dev/%7Emark/+editwikinames
339 >>> for message in find_tags_by_class(browser.contents, 'message'):
340 ... print message.renderContents()
341+ There is 1 error.
342 The WikiName http://foo.bar/wiki/FooBar already belongs to you.
343
344 Nor can he have a WikiName that is already registered in Launchpad.
345
346- >>> browser.getControl(name='newwiki').value = 'https://wiki.ubuntu.com/'
347- >>> browser.getControl(name='newwikiname').value = 'GuilhermeSalgado'
348+ >>> browser.getControl(name='field.wiki').value = (
349+ ... 'https://wiki.ubuntu.com/')
350+ >>> browser.getControl(name='field.wikiname').value = 'GuilhermeSalgado'
351 >>> browser.getControl('Save Changes').click()
352- >>> browser.url
353- 'http://localhost/~mark/+editwikinames'
354+ >>> print browser.url
355+ http://launchpad.dev/%7Emark/+editwikinames
356 >>> print "\n".join(get_feedback_messages(browser.contents))
357+ There is 1 error.
358 The WikiName https://wiki.ubuntu.com/GuilhermeSalgado is already
359 registered by Guilherme Salgado. If you think this is a duplicated
360 account, you can merge it into your account.
361
362 A WikiName's URL can't be empty nor invalid.
363
364- >>> browser.getControl(name='newwiki').value = ''
365- >>> browser.getControl(name='newwikiname').value = 'FooBar'
366- >>> browser.getControl('Save Changes').click()
367- >>> browser.url
368- 'http://localhost/~mark/+editwikinames'
369- >>> print "\n".join(get_feedback_messages(browser.contents))
370- Neither Wiki nor WikiName can be empty.
371-
372- >>> browser.getControl(name='newwiki').value = '/this-is-not-a-url/'
373- >>> browser.getControl(name='newwikiname').value = 'FooBar'
374- >>> browser.getControl('Save Changes').click()
375- >>> browser.url
376- 'http://localhost/~mark/+editwikinames'
377- >>> print "\n".join(get_feedback_messages(browser.contents))
378- "/this-is-not-a-url/" is not a valid URL.
379+ >>> browser.getControl(name='field.wiki').value = ''
380+ >>> browser.getControl(name='field.wikiname').value = 'FooBar'
381+ >>> browser.getControl('Save Changes').click()
382+ >>> print browser.url
383+ http://launchpad.dev/%7Emark/+editwikinames
384+ >>> print "\n".join(get_feedback_messages(browser.contents))
385+ There is 1 error.
386+ The Wiki URL must be specified.
387+
388+ >>> browser.getControl(name='field.wiki').value = '/this-is-not-a-url/'
389+ >>> browser.getControl(name='field.wikiname').value = 'FooBar'
390+ >>> browser.getControl('Save Changes').click()
391+ >>> print browser.url
392+ http://launchpad.dev/%7Emark/+editwikinames
393+ >>> print "\n".join(get_feedback_messages(browser.contents))
394+ There is 1 error.
395+ "/this-is-not-a-url/" is not a valid URI
396+
397+And it can't be incredibly long.
398+
399+ >>> browser.getControl(name='field.wiki').value = (
400+ ... 'https://wiki.ubuntu.com/')
401+ >>> wikiname = "z" * 101
402+ >>> browser.getControl(name='field.wikiname').value = wikiname
403+ >>> browser.getControl('Save Changes').click()
404+ >>> print browser.url
405+ http://launchpad.dev/%7Emark/+editwikinames
406+ >>> print "\n".join(get_feedback_messages(browser.contents))
407+ There is 1 error.
408+ The wiki name cannot exceed 100 characters.
409
410 The invalid value is escaped using HTML entities when displayed back to
411 the user.
412
413- >>> browser.getControl(name='newwiki').value = '<script>alert(1);</script>'
414- >>> browser.getControl(name='newwikiname').value = 'FooBar'
415+ >>> browser.getControl(name='field.wiki').value = (
416+ ... '<script>alert(1);</script>')
417+ >>> browser.getControl(name='field.wikiname').value = 'FooBar'
418 >>> browser.getControl('Save Changes').click()
419- >>> browser.url
420- 'http://localhost/~mark/+editwikinames'
421+ >>> print browser.url
422+ http://launchpad.dev/%7Emark/+editwikinames
423 >>> print "\n".join(get_feedback_messages(browser.contents))
424- "&lt;script&gt;alert(1);&lt;/script&gt;/" is not a valid URL.
425+ There is 1 error.
426+ "&lt;script&gt;alert(1);&lt;/script&gt;" is not a valid URI
427
428 Only http and https URLs are allowed for wikis.
429
430- >>> browser.getControl(name='newwiki').value = "javascript:void"
431- >>> browser.getControl(name='newwikiname').value = 'FooBar'
432+ >>> browser.getControl(name='field.wiki').value = "javascript:void"
433+ >>> browser.getControl(name='field.wikiname').value = 'FooBar'
434 >>> browser.getControl('Save Changes').click()
435- >>> browser.url
436- 'http://localhost/~mark/+editwikinames'
437+ >>> print browser.url
438+ http://launchpad.dev/%7Emark/+editwikinames
439 >>> print "\n".join(get_feedback_messages(browser.contents))
440- The URL scheme "javascript" is not allowed. Only http or https URLs may be
441- used.
442-
443-Mark can remove any of his wiki names, and here he'll remove the one he
444-just added.
445-
446+ There is 1 error.
447+ The URI scheme "javascript" is not allowed. Only URIs with the following
448+ schemes may be used: http, https
449+
450+Mark can remove any of his wiki names.
451+
452+ >>> browser.getControl(name='field.wiki').value = ""
453+ >>> browser.getControl(name='field.wikiname').value = ''
454 >>> browser.getControl('Remove', index=0).selected = True
455 >>> browser.getControl('Save Changes').click()
456- >>> browser.url
457- 'http://localhost/~mark/+editwikinames'
458- >>> print_existing_wikinames(browser.contents)
459+ >>> print browser.url
460+ http://launchpad.dev/~mark
461+ >>> print "\n".join(get_feedback_messages(browser.contents))
462+ >>> browser.open('http://launchpad.dev/~mark/+editwikinames')
463+ >>> print_existing_wikiurls(browser.contents)
464 https://wiki.ubuntu.com/MarkShuttleworth
465
466=== modified file 'lib/lp/registry/templates/person-editwikinames.pt'
467--- lib/lp/registry/templates/person-editwikinames.pt 2009-09-17 15:00:38 +0000
468+++ lib/lp/registry/templates/person-editwikinames.pt 2010-08-31 14:04:45 +0000
469@@ -6,14 +6,12 @@
470 metal:use-macro="view/macro:page/main_only"
471 i18n:domain="launchpad"
472 >
473- <body>
474-
475- <div metal:fill-slot="main">
476- <p tal:condition="view/error_message"
477- tal:content="structure view/error_message/escapedtext"
478- class="error message" />
479-
480- <form name="edit_wikiname" action="" method="POST">
481+<body>
482+
483+<div metal:fill-slot="main">
484+<div metal:use-macro="context/@@launchpad_form/form">
485+
486+ <div metal:fill-slot="widgets">
487 <table id="wikinames">
488
489 <tal:XXX condition="nothing">
490@@ -22,27 +20,19 @@
491 # there's a bug preventing us from # updating our version of storm
492 # to trunk.
493 </tal:XXX>
494- <tal:block condition="context/wiki_names/any">
495-
496- <tr><td colspan="2"><h2>Wiki names</h2></td></tr>
497-
498- <tr>
499- <td><label>Wiki base URL:</label></td>
500- <td><label>Wiki name:</label></td>
501- </tr>
502-
503- <tr tal:repeat="wiki context/wiki_names">
504- <td>
505- <input type="text" style="margin-bottom: 0.5em;"
506- tal:attributes="name string:wiki_${wiki/id};
507- value wiki/wiki" />
508- </td>
509- <td>
510- <input type="text" size="15"
511- tal:attributes="name string:wikiname_${wiki/id};
512- value wiki/wikiname" />
513- </td>
514-
515+
516+ <tal:existing_wiki condition="context/wiki_names/any">
517+
518+ <tr>
519+ <td><label>Existing wiki names</label></td>
520+ </tr>
521+
522+ <tr>
523+ <td><label>Wiki URL</label></td>
524+ </tr>
525+
526+ <tr tal:repeat="wiki context/wiki_names" class="wikiurl">
527+ <td tal:content="wiki/url"></td>
528 <td>
529 <label>
530 <input type="checkbox" value="Remove"
531@@ -51,41 +41,35 @@
532 </label>
533 </td>
534 </tr>
535-
536- </tal:block>
537-
538- <tr>
539- <td colspan="2"><h2>New wiki name</h2></td>
540- </tr>
541-
542- <tr>
543- <td><label>Wiki Base URL:</label></td>
544- <td><label>Wiki Name:</label></td>
545- </tr>
546-
547- <tr>
548- <td>
549- <input name="newwiki" type="text"
550- tal:attributes="value view/newwiki|nothing" />
551- </td>
552- <td>
553- <input name="newwikiname" type="text" size="15"
554- tal:attributes="value view/newwikiname|nothing" />
555- </td>
556- </tr>
557-
558- <tr>
559- <td class="formHelp">Example: https://wiki.ubuntu.com/</td>
560- <td class="formHelp">Example: YourName.</td>
561- </tr>
562- </table>
563-
564- <div class="actions">
565- <input type="submit" value="Save Changes" name="SAVE" />
566- or&nbsp; <a tal:attributes="href view/cancel_url">Cancel</a>
567- </div>
568- </form>
569- </div>
570+ <tr style="height:2em;"><td></td></tr>
571+ </tal:existing_wiki>
572+
573+
574+ <tr>
575+ <td><label>New wiki name</label></td>
576+ </tr>
577+
578+ <tal:widget define="widget nocall:view/widgets/wiki">
579+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
580+ </tal:widget>
581+
582+ <tr>
583+ <td class="formHelp">Example: https://wiki.ubuntu.com/</td>
584+ </tr>
585+
586+ <tal:widget define="widget nocall:view/widgets/wikiname">
587+ <metal:block use-macro="context/@@launchpad_form/widget_row" />
588+ </tal:widget>
589+
590+ <tr>
591+ <td class="formHelp">Example: YourName.</td>
592+ </tr>
593+
594+ </table>
595+ </div>
596+
597+</div>
598+</div>
599
600 </body>
601 </html>