fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2011-06-13 04:41 am

[dw-free] Plugin to generate form elements and automatically fill in values for .tt pages

[commit: http://hg.dwscoalition.org/dw-free/rev/8f6c6a6d6324]

http://bugs.dwscoalition.org/show_bug.cgi?id=3724

Template Toolkit plugin to generate form elements. Use the [% form %]
variable in views; autopopulated with the contents of $vars->{formdata} from
controllers, where formdata is a hashref in the from of: { formelement_name
=> formelement_value }.

Patch by [personal profile] fu.

Files modified:
  • cgi-bin/DW/Controller/Rename.pm
  • cgi-bin/DW/Template.pm
  • cgi-bin/DW/Template/Plugin/FormHTML.pm
  • cgi-bin/htmlcontrols.pl
  • t/template-plugin-formhtml.t
  • views/_init.tt
  • views/admin/rename_edit.tt
  • views/rename.tt
  • views/rename/swap.tt
--------------------------------------------------------------------------------
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 cgi-bin/DW/Controller/Rename.pm
--- a/cgi-bin/DW/Controller/Rename.pm	Mon Jun 13 11:10:14 2011 +0800
+++ b/cgi-bin/DW/Controller/Rename.pm	Mon Jun 13 12:38:50 2011 +0800
@@ -106,13 +106,13 @@
                 : ();
 
             # initialize the form based on previous posts (in case of error) or with some default values
-            $vars->{form} = {
+            $vars->{formdata} = {
                 authas      => $authas,
                 journaltype => $get_args->{type},
                 journalurl  => $get_args->{type} eq "P" ? $remote->journal_base : LJ::journal_base( "communityname", "community" ),
                 pageurl     => "/rename/" . $token->token,
                 token       => $token->token,
-                to          => $post_args->{touser} || $get_args->{to} || "",
+                touser      => $post_args->{touser} || $get_args->{to} || "",
                 redirect    => $post_args->{redirect} || "disconnect",
                 rel_types   => \@rel_types,
                 rel_options => %$post_args ? { map { $_ => 1 } $post_args->get( "rel_options" ) }
@@ -207,9 +207,8 @@
             authas => $post_args->{authas},
         } );
 
-    $vars->{form} = {
-        authas => $authas,
-    };
+    $vars->{authas} = $authas;
+    $vars->{formdata} = $post_args;
 
     return DW::Template->render_template( 'rename/swap.tt', $vars );
 }
@@ -314,7 +313,7 @@
 
     my $vars = {
         %$rv,
-        form => $form,
+        formdata => $form,
         token => $token,
     };
 
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 cgi-bin/DW/Template.pm
--- a/cgi-bin/DW/Template.pm	Mon Jun 13 11:10:14 2011 +0800
+++ b/cgi-bin/DW/Template.pm	Mon Jun 13 12:38:50 2011 +0800
@@ -74,6 +74,7 @@
         date => 'Template::Plugin::Date',
         url => 'Template::Plugin::URL',
         dw => 'DW::Template::Plugin',
+        form => 'DW::Template::Plugin::FormHTML',
     },
     PRE_PROCESS => '_init.tt',
 });
@@ -87,7 +88,7 @@
     STAT_TTL => $LJ::IS_DEV_SERVER ? 1 : 3600,
     PLUGINS => {
         dw => 'DW::Template::Plugin',
-        dw_scheme => 'DW::Template::Plugin::SiteScheme'
+        dw_scheme => 'DW::Template::Plugin::SiteScheme',
     },
 });
 
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 cgi-bin/DW/Template/Plugin/FormHTML.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Template/Plugin/FormHTML.pm	Mon Jun 13 12:38:50 2011 +0800
@@ -0,0 +1,241 @@
+#!/usr/bin/perl
+#
+# DW::Template::Plugin::FormHTML
+#
+# Template Toolkit plugin to generate HTML elements with preset values.
+#
+# Authors:
+#      Afuna <coder.dw@afunamatata.com>
+#
+# Copyright (c) 2011 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself.  For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+#
+package DW::Template::Plugin::FormHTML;
+use base 'Template::Plugin';
+use strict;
+
+=head1 NAME
+
+DW::Template::Plugin::FormHTML - Template Toolkit plugin to generate HTML elements
+with preset values
+
+=head1 SYNOPSIS
+
+The form plugin generates HTML elements with attributes suitably escaped, and values automatically prepopulated, depending on the form's data field.
+
+The "data" field is a hashref, with the keys being the form element's name, and the values being the form element's desired value.
+
+If a "formdata" property is available via the context, this is used to automatically populate the plugin's data field.
+=cut
+
+sub load {
+    return $_[0];
+}
+
+sub new {
+    my ( $class, $context, @params ) = @_;
+
+    my $self = bless {
+        _CONTEXT => $context,
+        data     => $context ? $context->stash->{formdata} : undef,
+    }, $class;
+
+    return $self;
+}
+
+=head1 METHODS
+
+=cut
+
+=head2 Common Arguments
+
+All methods which generate an HTML element can accept the following optional arguments:
+=item default - default value to use. Does not override the value of a previous form submission
+=item value - value to apply to the form element. Does override any previous form submissions
+=item selected - (for checkbox, radio) - whether the form element was selected or not
+               - (for select) - the selected option in the dropdown
+
+=item label - text for a label, which is paired with the form element if an id is provided
+=item labelclass - class for a label
+
+=item id - id of the element. Highly recommended, especially if you have a label
+=item name - name of the form element. You'll probably really want this
+=item class - CSS class of the form element
+
+=item (other valid HTML attributes)
+=cut
+
+=head2 [% form.checkbox( label="A label", id="elementid", name="elementname", .... ) %]
+
+Return a checkbox with a matching label, if provided. Values are prepopulated by the plugin's datasource.
+
+=cut
+
+sub checkbox {
+    my ( $self, $args ) = @_;
+
+    my $ret = "";
+
+    # FIXME: check an array of args, rather than expecting this to be an APR::RequestTable
+    # processes any previous form submissions. Doing this out here as it's special-cased
+    if ( ! defined $args->{selected} && $self->{data} ) {
+        my %selected = map { $_ => 1 } $self->{data}->get( $args->{name} );
+        $args->{selected} = $selected{$args->{value}};
+    }
+
+    # makes the form element use the default or an explicit value...
+    my $label_html = $self->_process_value_and_label( $args, use_as_value => "selected", noautofill => 1 );
+
+    $ret .= LJ::html_check( $args );
+    $ret .= $label_html;
+
+    return $ret;
+}
+
+=head2 [% form.hidden( name =... ) %]
+
+Return a hidden form element. Values are prepopulated by the plugin's datasource.
+
+=cut
+
+sub hidden {
+    my ( $self, $args ) = @_;
+
+    $self->_process_value_and_label( $args );
+    return LJ::html_hidden( $args );
+}
+
+=head2 [% form.radio( label = ... ) %]
+
+Return a radiobutton with a matching label, if provided. Values are prepopulated by the plugin's datasource.
+
+=cut
+
+sub radio {
+    my ( $self, $args ) = @_;
+
+    $args->{type} = "radio";
+
+    my $ret = "";
+
+    # FIXME: check an array of args, rather than expecting this to be an APR::RequestTable
+    # processes any previous form submissions. Doing this out here as it's special-cased
+    if ( ! defined $args->{selected} && $self->{data} ) {
+        my %selected = map { $_ => 1 } $self->{data}->get( $args->{name} );
+        $args->{selected} = $selected{$args->{value}};
+    }
+
+    # makes the form element use the default or an explicit value...
+    my $label_html = $self->_process_value_and_label( $args, use_as_value => "selected", noautofill => 1 );
+
+    $ret .= LJ::html_check( $args );
+    $ret .= $label_html;
+
+    return $ret;
+
+}
+
+=head2 [% form.select( label="A Label", id="elementid", name="elementname", items=[array of items], ... ) %]
+
+Return a select dropdown with a list of options, and matching label if provided. Values are prepopulated
+by the plugin's datasource.
+
+=cut
+sub select {
+    my ( $self, $args ) = @_;
+
+    my $items = delete $args->{items};
+
+    my $ret = "";
+    $ret .= $self->_process_value_and_label( $args, use_as_value => "selected" );
+    $ret .= LJ::html_select( $args, @{$items || []} );
+    return $ret;
+}
+
+=head2 [% form.textarea( label=... ) %]
+
+Return a textarea with a matching label, if provided. Values are prepopulated
+by the plugin's datasource.
+
+=cut
+
+sub textarea {
+    my ( $self, $args ) = @_;
+
+    my $ret = "";
+    $ret .= $self->_process_value_and_label( $args );
+    $ret .= LJ::html_textarea( $args );
+
+    return $ret;
+}
+
+
+=head2 [% form.textbox( label="A Label", id="elementid", name="elementname", ... ) %]
+
+Return a textbox (input type="text") with a matching label, if provided. Values
+are prepopulated by the plugin's datasource.
+
+=cut
+
+sub textbox {
+    my ( $self, $args ) = @_;
+
+    my $ret = "";
+    $ret .= $self->_process_value_and_label( $args );
+    $ret .= LJ::html_text( $args );
+
+    return $ret;
+}
+
+
+# populate the element's value, modifying the $args hashref
+# return the label HTML if applicable
+sub _process_value_and_label {
+    my ( $self, $args, %opts ) = @_;
+
+    my $valuekey = $opts{use_as_value} || "value";
+    if ( defined $args->{$valuekey} ) {
+        # explicitly override with a value when we created the form element
+        # do nothing! Just use what we passed in
+    } else {
+        # we didn't pass in an explicit value; check our data source (probably form post)
+        if ( $self->{data} && ! $opts{noautofill} ) {
+            $args->{$valuekey} = $self->{data}->{$args->{name}};
+        }
+
+        # no data source, value not set explicitly, use a default if provided
+        my $default = delete $args->{default};
+        $args->{$valuekey} ||= $default;
+    }
+
+    my $label_html = "";
+    my $label = delete $args->{label};
+    my $labelclass = delete $args->{labelclass} || "";
+    $label_html = LJ::labelfy( $args->{id} || "", LJ::ehtml( $label ), $labelclass )
+        if defined $label;
+
+    return $label_html || "";
+}
+
+=head1 AUTHOR
+
+=over
+
+=item Afuna <coder.dw@afunamatata.com>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (c) 2011 by Dreamwidth Studios, LLC.
+
+This program is free software; you may redistribute it and/or modify it under
+the same terms as Perl itself. For a copy of the license, please reference
+'perldoc perlartistic' or 'perldoc perlgpl'.
+
+=cut
+
+1;
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 cgi-bin/htmlcontrols.pl
--- a/cgi-bin/htmlcontrols.pl	Mon Jun 13 11:10:14 2011 +0800
+++ b/cgi-bin/htmlcontrols.pl	Mon Jun 13 12:38:50 2011 +0800
@@ -268,14 +268,17 @@
 # given a string and an id, return the string
 # in a label, respecting HTML
 sub labelfy {
-    my ($id, $text) = @_;
+    my ($id, $text, $class) = @_;
     $id = '' unless defined $id;
     $text = '' unless defined $text;
 
+    $class = LJ::ehtml( $class || "" );
+    $class = qq{class="$class"} if $class;
+
     $text =~ s!
         ^([^<]+)
         !
-        <label for="$id">
+        <label for="$id" $class>
             $1
         </label>
         !x;
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 t/template-plugin-formhtml.t
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/template-plugin-formhtml.t	Mon Jun 13 12:38:50 2011 +0800
@@ -0,0 +1,177 @@
+# -*-perl-*-
+
+use strict;
+use warnings;
+use Test::More;
+use lib "$ENV{LJHOME}/cgi-bin";
+require 'ljlib.pl';
+
+use DW::Template::Plugin::FormHTML;
+use HTML::Parser;
+
+my $form = DW::Template::Plugin::FormHTML->new();
+
+sub _parser_start {
+    my ( $parser, $tagname, $attr ) = @_;
+
+
+    my $tag = {
+        tag  => $tagname,
+        attr => $attr,
+    };
+
+    my $unclosed = $parser->{_parse_results}->{unclosed};
+    if ( $parser->{_parse_depth} ) {
+        my $current_ele = $unclosed->[-1];
+        $current_ele->{children} ||= [];
+        push @{$current_ele->{children}}, $tag;
+    } else {
+        my $parsed = $parser->{_parse_results}->{parsed};
+        push @$parsed, $tag;
+    }
+
+    push @$unclosed, $tag;
+    $parser->{_parse_depth}++;
+}
+
+sub _parser_text {
+    my ( $parser, $text ) = @_;
+
+    my $unclosed = $parser->{_parse_results}->{unclosed};
+
+    my $current_ele = $unclosed->[-1];
+    $current_ele->{text} = LJ::trim( $text ) if $current_ele;
+}
+
+sub _parser_end {
+    my ( $parser, $tagname ) = @_;
+
+    my $unclosed = $parser->{_parse_results}->{unclosed};
+
+    my $current_ele = $unclosed->[-1];
+    if ( $current_ele && $current_ele->{tag} eq $tagname ) {
+        pop @$unclosed;
+        $parser->{_parse_depth}--;
+    }
+}
+
+sub parse {
+    my ( $in ) = @_;
+    my $parser = HTML::Parser->new(
+        api_version => 3,
+    );
+
+    # quick and dirty parser which assumes correct nesting
+    $parser->handler( "start" => \&_parser_start, "self, tagname, attr" );
+    $parser->handler( "text"  => \&_parser_text, "self, text" );
+    $parser->handler( "end"   => \&_parser_end, "self, tagname" );
+    $parser->{_parse_results} = { parsed => [], unclosed => [] };
+    $parser->{_parse_depth} = 0;
+
+    $parser->parse( $in );
+    return $parser->{_parse_results}->{parsed};
+}
+
+
+
+$form->{data} = {
+    foo => "bar"
+};
+
+note( "basic generated select" );
+{
+my $select = parse( $form->select({
+    label   => "Select",
+    name    => "foo",
+    id      => "foo",
+    items   => [ qw( a apple b "banapple" c <strong>crabapple</strong> bar baz ) ]
+}) );
+
+my $label = $select->[0];
+is( $label->{text}, "Select", "Have label" );
+is( $label->{attr}->{for}, "foo" );
+
+my $dropdown = $select->[1];
+is( $dropdown->{attr}->{name}, "foo" );
+is( $dropdown->{attr}->{id}, "foo" );
+
+my @options;
+foreach ( @{$dropdown->{children}} ) {
+
+    my $option = { text => $_->{text} };
+    while ( my ( $k, $v ) = each %{$_->{attr}} ) {
+        $option->{$k} = $v;
+    }
+
+    push @options, $option;
+}
+
+
+is_deeply( \@options, [
+    { value => "a", text => "apple" },
+    { value => "b", text => "&quot;banapple&quot;" },                   # escape
+    { value => "c", text => "&lt;strong&gt;crabapple&lt;/strong&gt;" }, # escape
+    { value => "bar", text => "baz", selected => "selected" },          # selected automatically from data source
+], "Correctly escaped / processed / selected options" );
+
+}
+
+note( "check select where the value is overriden (nothing selected)" );
+{
+my $select = parse( $form->select({
+    label       => "Select",
+    name        => "foo",
+    id          => "foo",
+    selected    => "",
+    items       => [ qw( bar baz yes 1 no 2 ) ]
+}) );
+
+my @options;
+foreach ( @{$select->[1]->{children}} ) {
+
+    my $option = { text => $_->{text} };
+    while ( my ( $k, $v ) = each %{$_->{attr}} ) {
+        $option->{$k} = $v;
+    }
+
+    push @options, $option;
+}
+
+is_deeply( \@options, [
+    { value => "bar", text => "baz" },
+    { value => "yes", text => "1" },
+    { value => "no",  text => "2" },
+] );
+
+}
+
+note( "check select where the value is overriden (have something selected)" );
+{
+my $select = parse( $form->select({
+    label       => "Select",
+    name        => "foo",
+    id          => "foo",
+    selected    => "yes",
+    items       => [ qw( bar baz yes 1 no 2 ) ]
+}) );
+
+my @options;
+foreach ( @{$select->[1]->{children}} ) {
+
+    my $option = { text => $_->{text} };
+    while ( my ( $k, $v ) = each %{$_->{attr}} ) {
+        $option->{$k} = $v;
+    }
+
+    push @options, $option;
+}
+
+is_deeply( \@options, [
+    { value => "bar", text => "baz" },
+    { value => "yes", text => "1", selected => "selected" },
+    { value => "no",  text => "2" },
+] );
+
+}
+
+done_testing();
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 views/_init.tt
--- a/views/_init.tt	Mon Jun 13 11:10:14 2011 +0800
+++ b/views/_init.tt	Mon Jun 13 12:38:50 2011 +0800
@@ -15,4 +15,4 @@
 the same terms as Perl itself.  For a copy of the license, please reference
 'perldoc perlartistic' or 'perldoc perlgpl'.
 
-%][%- USE dw -%]
+%][%- USE dw -%][%- USE form -%]
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 views/admin/rename_edit.tt
--- a/views/admin/rename_edit.tt	Mon Jun 13 11:10:14 2011 +0800
+++ b/views/admin/rename_edit.tt	Mon Jun 13 12:38:50 2011 +0800
@@ -29,13 +29,13 @@
         <fieldset class="rename">
             <legend>Account information</legend>
             <div class="formfield">
-                <label>Rename: </label>     [% form.from %] --> [% form.to %]
+                <label>Rename: </label>     [% form.data.from %] --> [% form.data.to %]
             </div>
             <div class="formfield">
-                <label>Account: </label>    [% form.user.ljuser_display %]
+                <label>Account: </label>    [% form.data.user.ljuser_display %]
             </div>
             <div class="formfield">
-                <label>Renamed by: </label> [% form.byuser.ljuser_display %]
+                <label>Renamed by: </label> [% form.data.byuser.ljuser_display %]
             </div>
 
         </fieldset>
@@ -47,22 +47,22 @@
         <legend>Username redirection options</legend>
         <input type="checkbox" name="override_redirect" id="override_redirect" /><label for="override_redirect">Override</label>
         <div class="formfield">
-            <input type="radio" name="redirect" value="forward" id="redirect_forward" disabled="disabled" [% 'checked="checked"' IF form.redirect == "forward" %]/><label for="redirect_forward">[%- '.form.rename.forward.label.' _ form.journaltype | ml %]</label>
+            <input type="radio" name="redirect" value="forward" id="redirect_forward" disabled="disabled" [% 'checked="checked"' IF form.data.redirect == "forward" %]/><label for="redirect_forward">[%- '.form.rename.forward.label.' _ form.data.journaltype | ml %]</label>
             <p class="note">Cannot reconnect the journal once disconnected.</p>
         </div>
         <div class="formfield">
-            <input type="radio" name="redirect" value="disconnect" id="redirect_disconnect" [% 'checked="checked"' IF form.redirect == 'disconnect' %]/><label for="redirect_disconnect">[%- '.form.rename.disconnect.label.' _ form.journaltype | ml %]</label>
+            <input type="radio" name="redirect" value="disconnect" id="redirect_disconnect" [% 'checked="checked"' IF form.data.redirect == 'disconnect' %]/><label for="redirect_disconnect">[%- '.form.rename.disconnect.label.' _ form.data.journaltype | ml %]</label>
         </div>
         </fieldset>
 
-        [% IF form.rel_types.size > 0 %]
+        [% IF form.data.rel_types.size > 0 %]
         <fieldset class="relationships">
             <legend>[% '.form.relationships.header' | ml %]</legend>
             <input type="checkbox" name="override_relationships" id="override_relationships" /><label for="override_relationships">Override</label>
             <p class="note">be careful when leaving these unchecked! We cannot recover the list once the relationships have been broken, so make sure you don't carry over old opts. Otherwise you may delete everyone the user has added since they renamed</p>
-            [% FOREACH rel IN form.rel_types %]
+            [% FOREACH rel IN form.data.rel_types %]
                 <div class="formfield">
-                    <input type="checkbox" name="rel_options" value="[% rel %]" id="rel_[% rel %]" [%- 'checked="checked"' IF form.rel_options.$rel -%] /><label for="rel_[% rel %]">[% ".form.relationships.$rel" | ml %]</label>
+                    <input type="checkbox" name="rel_options" value="[% rel %]" id="rel_[% rel %]" [%- 'checked="checked"' IF form.data.rel_options.$rel -%] /><label for="rel_[% rel %]">[% ".form.relationships.$rel" | ml %]</label>
                 </div>
             [% END %]
         </fieldset>
@@ -72,7 +72,7 @@
             <legend>[% '.form.others.header' | ml %]</legend>
             <input type="checkbox" name="override_others" id="override_others" /><label for="override_others">Override</label>
             <div class="formfield">
-                <input type="checkbox" name="others" value="email" id="others_email" [%- 'checked="checked"' IF form.others.email -%]/><label for="others_email">[% '.form.others.email' | ml( sitename = site.nameshort ) %] <span id="others_email_note">([% '.form.others.email.note' | ml %])</span></label>
+                <input type="checkbox" name="others" value="email" id="others_email" [%- 'checked="checked"' IF form.data.others.email -%]/><label for="others_email">[% '.form.others.email' | ml( sitename = site.nameshort ) %] <span id="others_email_note">([% '.form.others.email.note' | ml %])</span></label>
                 <p class="note">Cannot reconnect the email once disconnected.</p>
             </div>
         </fieldset>
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 views/rename.tt
--- a/views/rename.tt	Mon Jun 13 11:10:14 2011 +0800
+++ b/views/rename.tt	Mon Jun 13 12:38:50 2011 +0800
@@ -33,37 +33,41 @@
         <fieldset class="rename">
             <legend>[%- '.form.rename.header' | ml %]</legend>
             <div class="formfield">
-                <label for="token">[%- '.form.rename.token.label' | ml -%]</label> [%- form.token | html -%]
+                <label for="token">[%- '.form.rename.token.label' | ml -%]</label> [%- form.data.token | html -%]
             </div>
             <div class="formfield">
-                <label>[%- '.form.rename.fromuser.label' | ml -%]</label> [% form.authas %]
-                [% IF form.journaltype == "P" %]
-                    <a href="[%- form.pageurl | url -%]?type=C">[%- '.form.switchtype.comm' | ml -%]</a>
+                <label>[%- '.form.rename.fromuser.label' | ml -%]</label> [% form.data.authas %]
+                [% IF form.data.journaltype == "P" %]
+                    <a href="[%- form.data.pageurl | url -%]?type=C">[%- '.form.switchtype.comm' | ml -%]</a>
                 [% ELSE %]
-                    <a href="[%- form.pageurl | url -%]">[%- '.form.switchtype.journal' | ml -%]</a>
+                    <a href="[%- form.data.pageurl | url -%]">[%- '.form.switchtype.journal' | ml -%]</a>
                 [% END %]
             </div>
             <div class="formfield">
-                <label for="touser">[%- '.form.rename.touser.label' | ml -%]</label><input type="text" id="touser" name="touser" value="[%- form.to | html -%]" />
+                [%- touser_label = '.form.rename.touser.label' | ml;
+                form.textbox( label = touser_label
+                    name = "touser"
+                    id = "touser"
+                ) -%]
             </div>
             <div class="formfield">
                 <fieldset>
-                <legend>[%- '.form.rename.oldusername.header.' _ form.journaltype | ml -%]</legend>
-                <input type="radio" name="redirect" value="forward" id="redirect_forward" [% 'checked="checked"' IF form.redirect == "forward" %]/><label for="redirect_forward">[%- '.form.rename.forward.label.' _ form.journaltype | ml %]</label>
-                <p class='detail'>[%- '.form.rename.forward.note.' _ form.journaltype | ml( journalurl = form.journalurl ) %]</p>
-                <input type="radio" name="redirect" value="disconnect" id="redirect_disconnect" [% 'checked="checked"' IF form.redirect == 'disconnect' %]/><label for="redirect_disconnect">[%- '.form.rename.disconnect.label.' _ form.journaltype | ml %]</label>
-                <p class='detail'>[%- '.form.rename.disconnect.note.' _ form.journaltype | ml( journalurl = form.journalurl ) %]</p>
+                <legend>[%- '.form.rename.oldusername.header.' _ form.data.journaltype | ml -%]</legend>
+                <input type="radio" name="redirect" value="forward" id="redirect_forward" [% 'checked="checked"' IF form.data.redirect == "forward" %]/><label for="redirect_forward">[%- '.form.rename.forward.label.' _ form.data.journaltype | ml %]</label>
+                <p class='detail'>[%- '.form.rename.forward.note.' _ form.data.journaltype | ml( journalurl = form.data.journalurl ) %]</p>
+                <input type="radio" name="redirect" value="disconnect" id="redirect_disconnect" [% 'checked="checked"' IF form.data.redirect == 'disconnect' %]/><label for="redirect_disconnect">[%- '.form.rename.disconnect.label.' _ form.data.journaltype | ml %]</label>
+                <p class='detail'>[%- '.form.rename.disconnect.note.' _ form.data.journaltype | ml( journalurl = form.data.journalurl ) %]</p>
                 </fieldset>
             </div>
         </fieldset>
 
 
-        [% IF form.rel_types.size > 0 %]
+        [% IF form.data.rel_types.size > 0 %]
         <fieldset class="relationships">
             <legend>[% '.form.relationships.header' | ml %]</legend>
-            [% FOREACH rel IN form.rel_types %]
+            [% FOREACH rel IN form.data.rel_types %]
                 <div class="formfield">
-                    <input type="checkbox" name="rel_options" value="[% rel %]" id="rel_[% rel %]" [%- 'checked="checked"' IF form.rel_options.$rel -%] /><label for="rel_[% rel %]">[% ".form.relationships.$rel" | ml %]</label>
+                    <input type="checkbox" name="rel_options" value="[% rel %]" id="rel_[% rel %]" [%- 'checked="checked"' IF form.data.rel_options.$rel -%] /><label for="rel_[% rel %]">[% ".form.relationships.$rel" | ml %]</label>
                 </div>
             [% END %]
         </fieldset>
@@ -72,7 +76,7 @@
         <fieldset class="others">
             <legend>[% '.form.others.header' | ml %]</legend>
             <div class="formfield">
-                <input type="checkbox" name="others" value="email" id="others_email" [%- 'checked="checked"' IF form.others.email -%]/><label for="others_email">[% '.form.others.email' | ml( sitename = site.nameshort ) %] <span id="others_email_note">([% '.form.others.email.note' | ml %])</span></label>
+                <input type="checkbox" name="others" value="email" id="others_email" [%- 'checked="checked"' IF form.data.others.email -%]/><label for="others_email">[% '.form.others.email' | ml( sitename = site.nameshort ) %] <span id="others_email_note">([% '.form.others.email.note' | ml %])</span></label>
             </div>
         </fieldset>
 
diff -r 4e2ea8791bd3 -r 8f6c6a6d6324 views/rename/swap.tt
--- a/views/rename/swap.tt	Mon Jun 13 11:10:14 2011 +0800
+++ b/views/rename/swap.tt	Mon Jun 13 12:38:50 2011 +0800
@@ -22,7 +22,7 @@
             <li>[% error %] </li>
         [% END %]
     </ul>
-    </div>>
+    </div>
 [% END %]
 
 <p>[% '.intro' | ml %]</p>
@@ -31,11 +31,14 @@
         [% dw.form_auth %]
         <div class='formfield'>
             <label for='authas'>[% '.form.journal' | ml %]</label>
-            [% form.authas %]
+            [% authas %]
         </div>
         <div class='formfield'>
-            <label for='swapjournal'>[% '.form.swapjournal' | ml %]</label>
-            <input type='text' name='swapjournal' id='swapjournal' />
+            [%- swapjournal_label = '.form.swapjournal' | ml;
+            form.textbox( label = swapjournal_label
+                name = "swapjournal"
+                id = "swapjournal"
+            ) -%]
         </div>
 
         <input type='submit' value='Swap' />
--------------------------------------------------------------------------------