Index: 0.11/trachacks/htdocs/js/trachacks.js
===================================================================
--- 0.11/trachacks/htdocs/js/trachacks.js	(revision 13419)
+++ 0.11/trachacks/htdocs/js/trachacks.js	(working copy)
@@ -1,98 +1,77 @@
-/* Add helpers to tag cloud. */
-var clean_tags = function(tags) {
-var split = tags.replace(/^ +| +$/g, '').split(/ +/);
+jQuery(document).ready(function($) {
+    var split_tags = function(text) {
+        return $.grep(text.split(/ +/g), function(v) { return !!v });
+    };
 
-  split.sort();
-  return split.join(' ');
-};
+    /* Highlight tags from the mini-cloud that are in the tags field. */
+    var highlight_tags = function() {
+        var tags = split_tags($('#tags').val());
 
-/* Highlight tags from the mini-cloud that are in the tags field. */
-var highlight_tags = function() {
-var tags = clean_tags($('#tags').attr('value')).split(/ +/);
+        $('#cloud a').each(function() {
+            var anchor = $(this);
+            var color = $.inArray(anchor.text(), tags) !== -1 ?
+                        'yellow' : 'transparent';
+            anchor.css('background-color', color);
+        });
+    };
 
-  $('#cloud a').each(function() {
-    if (tags.indexOf($(this).text()) != -1) {
-      $(this).css('background-color', 'yellow');
-    } else {
-      $(this).css('background-color', 'transparent');
-    }
-  });
-};
+    // Move the label for each field into the hint block.
+    $('.hint').each(function() {
+        var hint = this;
+        var fieldid = this.id.slice(0, -4);
+    });
 
-$(document).ready(function() {
-  // Move the label for each field into the hint block.
-  $('.hint').each(function() {
-  var hint = this;
-  var fieldid = hint.id.slice(0, -4);
+    // Handle focus/blur of input fields
+    $.fn.handleInfo = function(hint, label) {
+        return this.each(function() {
+            var hintid = $(!hint ? '#' + this.id + 'hint' : hint);
 
-  });
+            $(this).focus(function() { hintid.show() });
+            $(this).blur(function() { hintid.hide() });
 
-  // Handle focus/blur of input fields
-  $.fn.handleInfo = function(hint, label) {
-    return this.each(function() {
-    var hintid;
+            if (hintid.attr('copied_label') == undefined) {
+                var title = label;
+                hintid.attr('copied_label', true);
+                if (title == undefined) {
+                    $('label[for="' + this.id + '"]').each(function() {
+                        title = $(this).text();
+                    });
+                }
+                hintid.prepend('<strong>' + title + '</strong>' +
+                               '<span class="hint-pointer">&nbsp;</span>');
+            }
+        });
+    }
 
-      if (hint == undefined)
-        hintid = '#' + this.id + 'hint';
-      else
-        hintid = hint;
+    // Add hints to controls.
+    $('#name, #title, #description, #installation, #tags').handleInfo();
+    $('#cloud a').handleInfo('#tagshint');
+    $('input[name="type"]').handleInfo('#typehint', 'Type');
+    $('input[name="release"]').handleInfo('#releasehint', 'Compatibility');
 
-      hintid = $(hintid);
+    // Focus first error control. If none, focus #name.
+    $('input[class="error"], textarea[class="error"], #name').filter(':first')
+                                                             .focus();
 
-      $(hintid).hide();
+    $('#tags').bind('keyup change', highlight_tags);
 
-      $(this).focus(function() { hintid.show(); return true; });
-      $(this).blur(function() { hintid.hide(); return true; });
-
-      if (hintid.attr('copied_label') == undefined) {
-      var title = label;
-
-        hintid.attr('copied_label', true);
-        if (title == undefined) {
-          $('label[@for="' + this.id + '"]').each(function() {
-            title = $(this).text();
-          });
+    $('#cloud a').click(function() {
+        var anchor = $(this);
+        var tag = anchor.text();
+        var input = $('#tags');
+        var tags = split_tags(input.val());
+        var color;
+        if ($.inArray(tag, tags) !== -1) {
+            color = 'transparent';
+            tags = $.grep(tags, function(v) { return v !== tag });
+        } else {
+            color = 'yellow';
+            tags.push(tag);
         }
-        hintid.prepend('<strong>' + title + '</strong>' + '<span class="hint-pointer">&nbsp;</span>');
-      }
+        anchor.css('background-color', color);
+        input.val(tags.sort().join(' ') + ' ').focus().trigger('focus');
+        return false;
     });
-  }
 
-  // Add hints to controls.
-  $('#name, #title, #description, #installation, #tags').handleInfo();
-
-  $('#cloud a').handleInfo('#tagshint');
-
-  $('input[@name="type"]').handleInfo('#typehint', 'Type');
-  $('input[@name="release"]').handleInfo('#releasehint', 'Compatibility');
-
-  // Focus first error control. If none, focus #name.
-  var fields = $('input[@class="error"]:first, textarea[@class="error"]:first');
-  if (fields.size()) {
-    $(fields[0]).focus();
-  }
-
-  $('#tags').keyup(highlight_tags);
-  $('#tags').change(highlight_tags);
-
-  $('#cloud a').click(function() {
-  var a = this;
-  var tag = $(this).text();
-
-    $('#tags').each(function() {
-      if (-1 == this.value.search(tag)) {
-        $(a).css('background-color', 'yellow');
-        this.value = clean_tags(this.value + ' ' + tag);
-      } else {
-        $(a).css('background-color', 'transparent');
-        this.value = clean_tags(this.value.replace(tag, ''));
-      }
-      $(this).focus();
-    });
-    return false;
-  });
-
-
-  highlight_tags();
-
+    highlight_tags();
 });
Index: 0.11/trachacks/templates/hacks_new.html
===================================================================
--- 0.11/trachacks/templates/hacks_new.html	(revision 13419)
+++ 0.11/trachacks/templates/hacks_new.html	(working copy)
@@ -3,7 +3,8 @@
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:py="http://genshi.edgewall.org/"
-      xmlns:xi="http://www.w3.org/2001/XInclude">
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      xml:lang="en">
   <xi:include href="layout.html" />
   <xi:include href="macros.html" />
 
@@ -31,7 +32,7 @@
               CamelCase</a> name for your hack.
             </div>
 
-            <div id="namehint" class="hint">
+            <div id="namehint" class="hint" style="display:none">
               <span class="hint-pointer">&nbsp;</span>
               <p>
                 A new Wiki page will be created by concatenating the hack name
@@ -57,7 +58,7 @@
               You may use <a href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here.
             </div>
 
-            <div id="titlehint" class="hint">
+            <div id="titlehint" class="hint" style="display:none">
               <span class="hint-pointer">&nbsp;</span>
               <p>
                 This will be the main heading in the Wiki page for your hack.
@@ -78,7 +79,7 @@
               You may use <a href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here.
             </div>
 
-            <div id="descriptionhint" class="hint">
+            <div id="descriptionhint" class="hint" style="display:none">
               <span class="hint-pointer">&nbsp;</span>
               <p>
                 This is what visitors will read to determine if your hack
@@ -103,7 +104,7 @@
                 The type that best describes your hack.
               </div>
 
-              <div id="typehint" class="hint">
+              <div id="typehint" class="hint" style="display:none">
                 <p>
                   The title-cased version of the type will be appended to your
                   hack name to create your Wiki page.
@@ -128,7 +129,7 @@
             <dd id="release">
               <div>Release(s) of Trac this hack works with.</div>
 
-              <div id="releasehint" class="hint">
+              <div id="releasehint" class="hint" style="display:none">
                 <p>
                   <strong>anyrelease</strong> indicates your hack will work
                   with any Trac release.
@@ -156,7 +157,7 @@
             (You may use <a href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here)
             </div>
 
-            <div id="installationhint" class="hint">
+            <div id="installationhint" class="hint" style="display:none">
               <p>
                 Please include full instructions on how to install your hack,
                 including changes that need to be made to the <a
@@ -176,7 +177,7 @@
             <div>
               Space-separated list of tags classifying your hack.
             </div>
-            <div id="tagshint" class="hint">
+            <div id="tagshint" class="hint" style="display:none">
               <p>
                 Use any words you like. The more tags you use the easier it will
                 be for people to find your hack.
@@ -202,7 +203,5 @@
         disabled="${(form_context.errors or not (preview or create)) or None}"
         type="submit" name="create" value="Create Hack"/>
     </form>
-    <script type="text/javascript">
-    </script>
   </body>
 </html>
Index: 0.11/trachacks/web_ui.py
===================================================================
--- 0.11/trachacks/web_ui.py	(revision 13419)
+++ 0.11/trachacks/web_ui.py	(working copy)
@@ -8,6 +8,7 @@
 # you should have received as part of this distribution.
 #
 
+import os
 import random
 import re
 from string import Template
@@ -98,7 +99,7 @@
                 'Resulting component "%s" already exists.' % page_name
             )
 
-        authz_file = self.env.config.get('trac', 'authz_file')
+        authz_file = self.env.config.getpath('trac', 'authz_file')
         authz = AuthzFileReader().read(authz_file)
         authz_paths = [p.get_path() for p in authz.get_paths()]
         for ap in authz_paths:
@@ -230,8 +231,8 @@
         data['types'] = types
         data['releases'] = releases
 
-        selected_releases = req.args.get('release',
-                                         set(['0.10', '0.11', 'anyrelease']))
+        selected_releases = req.args.getlist('release') or \
+                            ('0.12', '1.0', 'anyrelease')
         data['selected_releases'] = selected_releases
 
         hacks = self.fetch_hacks(req, data, [t[0] for t in types],
@@ -354,7 +355,7 @@
                              int(min_px + percent * (max_px - min_px)))
 
         data['cloud'] = TagWikiMacros(self.env). \
-            render_cloud(req, cloud, cloud_renderer)
+                        render_cloud(req, cloud, cloud_renderer)
 
         add_script(req, 'common/js/wikitoolbar.js')
         add_script(req, 'common/js/folding.js')
@@ -429,39 +430,19 @@
         if have_lock:
             steps_done = []
             try:
-                # Step 1: create repository paths
-                from os import popen
-
-                svn_path = 'file://%s' % \
-                    self.env.config.get('trac', 'repository_dir')
-                svn_path = svn_path.rstrip('/')
                 page_name = vars['WIKINAME']
                 hack_path = vars['LCNAME']
-                paths = ['%s/%s' % (svn_path, hack_path)]
                 selected_releases = data['selected_releases']
-                if isinstance(selected_releases, (basestring, unicode)):
-                    selected_releases = [selected_releases, ]
-                for release in selected_releases:
-                    if release == 'anyrelease':
-                        continue
-                    paths.append("%s/%s/%s" % (svn_path, hack_path, release))
 
-                cmd = '/usr/bin/op create-hack %s ' % req.authname
-                cmd += '"New hack %s, created by %s" '\
-                       % (page_name, req.authname)
-                cmd += '%s 2>&1' % ' '.join(paths)
-                output = popen(cmd).readlines()
-                if output:
-                    raise Exception(
-                        "Failed to create Subversion paths:\n%s"
-                        % '\n'.join(output)
-                    )
+                # Step 1: create repository paths
+                self._create_repository_paths(req, page_name, hack_path,
+                                              selected_releases)
                 steps_done.append('repository')
 
                 # Step 2: Add permissions
                 from svnauthz.model import User, Path, PathAcl
 
-                authz_file = self.env.config.get('trac', 'authz_file')
+                authz_file = self.config.getpath('trac', 'authz_file')
                 authz = AuthzFileReader().read(authz_file)
 
                 svn_path_acl = PathAcl(User(req.authname), r=True, w=True)
@@ -503,7 +484,8 @@
                     if 'component' in steps_done:
                         TicketComponent(self.env, page_name).delete()
                     if 'permissions' in steps_done:
-                        authz_file = self.env.config.get('trac', 'authz_file')
+                        authz_file = self.config.getpath('trac',
+                                                             'authz_file')
                         authz = AuthzFileReader().read(authz_file)
                         authz.del_path(Path("/%s" % hack_path))
                         AuthzFileWriter().write(authz_file, authz)
@@ -516,6 +498,27 @@
                 raise TracError(str(e))
         return created, messages
 
+    def _create_repository_paths(self, req, page_name, hack_path,
+                                 selected_releases):
+        from subprocess import Popen, PIPE
+
+        env = os.environ.copy()
+        env['LC_ALL'] = env['LANG'] = 'en_US.UTF-8'
+        svn_path = 'file://%s' % self.config.get('trac', 'repository_dir')
+        svn_path = svn_path.rstrip('/')
+        paths = ['%s/%s' % (svn_path, hack_path)]
+        paths.extend('%s/%s/%s' % (svn_path, hack_path, release)
+                     for release in selected_releases
+                     if release != 'anyrelease')
+        message = 'New hack %s, created by %s' % (page_name, req.authname)
+        args = ['/usr/bin/svn', 'mkdir', '--username', req.authname,
+                '--non-interactive', '-m', message, '--'] + paths
+        proc = Popen(args, stdout=PIPE, stderr=PIPE, close_fds=True)
+        stdout, stderr = proc.communicate()
+        if proc.returncode != 0:
+            raise Exception('Failed to create Subversion paths:\n%s%s' %
+                            (stdout, stderr))
+
     def render_list(self, req, data, hacks):
         ul = builder.ul()
         for votes, rank, resource, tags, title \
@@ -544,7 +547,8 @@
 
         # TODO Top-n + sample
         cloud_hacks = dict([(hack[2].id, hack[0]) for hack in hacks])
-        data['body'] = render_cloud(self.env, req, cloud_hacks, link_renderer)
+        data['body'] = TagWikiMacros(self.env).render_cloud(req, cloud_hacks,
+                                                            link_renderer)
 
         return 'hacks_view.html', data, None
 
