Index: batchmod/web_ui.py
===================================================================
--- batchmod/web_ui.py	(revision 2711)
+++ batchmod/web_ui.py	(working copy)
@@ -1,120 +1,109 @@
 # -*- coding: utf-8 -*-
 # Copyright (C) 2006 Ashwin Phatak
+# Copyright (C) 2007 Dave Gynn
 
 from trac.core import *
-from trac.web.chrome import INavigationContributor, ITemplateProvider
-from trac.web.main import IRequestHandler
 from trac.perm import IPermissionRequestor        
+from trac.ticket import TicketSystem, Ticket
 from trac.ticket.query import QueryModule
-from trac.util.html import html
-from trac.ticket import TicketSystem
-from trac.ticket import Ticket
-from trac.mimeview.api import IContentConverter
-from trac.wiki import IWikiSyntaxProvider
-from trac.wiki.macros import WikiMacroBase
-from trac.ticket.query import TicketQueryMacro as Macro
+from trac.web.api import ITemplateStreamFilter
+from trac.web.chrome import ITemplateProvider, Chrome
+from trac.web.main import IRequestFilter
+from genshi.filters.transform import Transformer
 
-__all__ = ['BatchModifyModule','TicketQueryMacro']
+__all__ = ['BatchModifyModule']
 
 class BatchModifyModule(Component):
-    '''Allows batch modification of tickets'''
+    implements(IPermissionRequestor, ITemplateProvider, IRequestFilter, ITemplateStreamFilter)
 
-    implements(INavigationContributor, IRequestHandler, ITemplateProvider, \
-            IPermissionRequestor, IContentConverter, IWikiSyntaxProvider)
-    
-    # INavigationContributor methods
+    # IPermissionRequestor methods
+    def get_permission_actions(self):
+        yield 'TICKET_BATCH_MODIFY'
 
-    def get_active_navigation_item(self, req):
-        return QueryModule(self.env).get_active_navigation_item(req)
-
-    def get_navigation_items(self, req):
-        from trac.ticket.report import ReportModule
-        if req.perm.has_permission('TICKET_VIEW') and \
-                not self.env.is_component_enabled(ReportModule):
-            yield ('mainnav', 'tickets',
-                   html.A('View Tickets', href=req.href.query()))
-        yield ('mainnav', 'query',
-                   html.A('Custom Query', href=req.href.query()))
-
-
-    # IRequestHandler methods
-
-    def match_request(self, req):
-       return QueryModule(self.env).match_request(req)
-    
-    def process_request(self, req):
-        if req.args.has_key('batchmod'):
-            req.perm.assert_permission('TICKET_BATCH_MODIFY')
-            self._batch_modify(req)
-
-        QueryModule(self.env).process_request(req)
-        self._add_ticket_fields(req)
-     
-        return 'batchmod.cs', None
-
-
     # ITemplateProvider methods
+    def get_htdocs_dirs(self):
+        return []
 
     def get_templates_dirs(self):
         from pkg_resources import resource_filename
         return [resource_filename(__name__, 'templates')]
-   
- 
-    # IPermissionRequestor methods
-    def get_permission_actions(self):
-        yield 'TICKET_BATCH_MODIFY'
 
+    # IRequestFilter methods
+    def pre_process_request(self, req, handler):
+        """Look for QueryHandler posts and hijack them"""
+        if isinstance(handler, QueryModule):
+            if req.method=='POST' and req.args.get('batchmod'):
+                self._batch_modify(req)
+        return handler
 
-    # IContentConverter methods
-    def get_supported_conversions(self):
-        return QueryModule(self.env).get_supported_conversions()    
+    def post_process_request(self, req, template, content_type):
+        """No-op"""
+        return (template, content_type)
 
-    def convert_content(self, req, mimetype, query, key):
-        return QueryModule(self.env).convert_content(req, mimetype, query, key)
-
-
-    # IWikiSyntaxProvider methods
-
-    def get_wiki_syntax(self):
-        return QueryModule(self.env).get_wiki_syntax() 
-
-    def get_link_resolvers(self):
-        return QueryModule(self.env).get_link_resolvers()
-
-
+    def post_process_request(self, req, template, data, content_type):
+        """No-op"""
+        return (template, data, content_type)
+    
     # Internal methods 
     def _batch_modify(self, req):
         tickets = req.session['query_tickets'].split(' ')
-        comment = req.args.get('comment', '') 
+        comment = req.args.get('bmod_value_comment', '') 
         values = {} 
-        
+
+        # TODO: improve validation and better handle advanced statuses
         for field in TicketSystem(self.env).get_ticket_fields():
             name = field['name']
             if name not in ('summary', 'reporter', \
-                        'description', 'type', 'status',
-                        'resolution', 'owner'):
-                if req.args.has_key('bm_' + name):
-                    values[name] = req.args.get(name)
+                        'description', 'status',
+                        'resolution'):
+                if req.args.has_key('bmod_flag_' + name):
+                    if name == 'owner':
+                        values['status'] = 'assigned'
+                    values[name] = req.args.get('bmod_value_' + name)
 
-        for id in tickets:
-            t = Ticket(self.env, id) 
-            t.populate(values)
-            t.save_changes(req.authname, comment)
+        selectedTickets = req.args.get('selectedTickets')
+        selectedTickets = isinstance(selectedTickets, list) and selectedTickets or selectedTickets.split(',')
+        if not selectedTickets:
+            raise TracError, 'No Tickets selected'
+        
+        for id in selectedTickets:
+            if id in tickets:
+                t = Ticket(self.env, id) 
+                t.populate(values)
+                t.save_changes(req.authname, comment)
 
+                # TODO: Send email notifications - copied from ticket.web_ui
+                #try:
+                #    tn = TicketNotifyEmail(self.env)
+                #    tn.notify(ticket, newticket=False, modtime=now)
+                #except Exception, e:
+                #    self.log.exception("Failure sending notification on change to "
+                #                       "ticket #%s: %s" % (ticket.id, e))
+    
+                # TODO: deal with actions and side effects - copied from ticket.web_ui
+                #for controller in self._get_action_controllers(req, ticket,
+                #                                               action):
+                #    controller.apply_action_side_effects(req, ticket, action)
 
+    # ITemplateStreamFilter methods
+    def filter_stream(self, req, method, filename, stream, formdata):
+        """Adds BatchModify form to the query page"""
+        if filename != 'query.html' or not req.perm.has_permission('TICKET_BATCH_MODIFY'):
+            return stream
 
-    def _add_ticket_fields(self, req):   
-        for field in TicketSystem(self.env).get_ticket_fields():
-            name = field['name']
-            del field['name']
-            if name in ('summary', 'reporter', 'description', 'type', 'status',
-                        'resolution', 'owner'):
-                field['skip'] = True
-            req.hdf['ticket.fields.' + name] = field
+        return stream | Transformer('//div[@id="help"]').before(self._generate_form(req, formdata) )
 
+    
+    def _generate_form(self, req, data):
+        batchFormData = dict(data)
+        batchFormData['actionUri']= req.session['query_href'] or req.href.query()
 
-class TicketQueryMacro(WikiMacroBase):
-    __doc__ = Macro.__doc__
+        fields = []
+        for field in TicketSystem(self.env).get_ticket_fields():
+            if field['name'] not in ('summary', 'reporter', 'description'):
+                fields.append(field)
+        batchFormData['fields']=fields
 
-    def render_macro(self, req, name, content):
-        return Macro(self.env).render_macro(req, name, content)
+        stream = Chrome(self.env).render_template(req, 'batchmod.html',
+              batchFormData, fragment=True)
+        return stream.select('//form[@id="batchmod-form"]')
Index: batchmod/templates/batchmod.html
===================================================================
--- batchmod/templates/batchmod.html	(revision 0)
+++ batchmod/templates/batchmod.html	(revision 0)
@@ -0,0 +1,101 @@
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "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">
+<body>
+<form id="batchmod-form" method="post" action="${actionURI}">
+<fieldset><legend>Batch Modify Properties</legend>
+<table class="properties">
+	<tr py:for="row in group(fields, 2, lambda f: f.type != 'textarea')"
+		py:with="fullrow = len(row) == 1">
+		<py:for each="idx, field in enumerate(row)">
+			<th style="text-align: left" class="col${idx + 1}"
+				py:if="idx == 0 or not fullrow">
+				<input py:if="field" type="checkbox" id="bmod_flag_${field.name}" name="bmod_flag_${field.name}" />
+				<label py:if="field" for="bmod_value_${field.name}" class="disabled">${field.label or field.name}:</label>
+			</th>
+			<td class="col${idx + 1}" py:if="idx == 0 or not fullrow"
+				colspan="${fullrow and 3 or None}">
+			<py:choose test="field.type" py:if="field">
+				<select py:when="'select'" id="bmod_value_${field.name}"
+					name="bmod_value_${field.name}" disabled="disabled">
+					<option py:if="field.optional" />
+					<option py:for="option in field.options" py:content="option"/>
+				</select>
+				<textarea py:when="'textarea'" id="bmod_value_${field.name}"
+					name="bmod_value_${field.name}" cols="${field.width}"
+					rows="${field.height}" disabled="disabled" />
+				<span py:when="'checkbox'"> <input type="checkbox"
+					id="bmod_value_${field.name}" name="bmod_value_${field.name}"
+					value="1" disabled="disabled" /> <input type="hidden"
+					name="bmod_value_checkbox_${field.name}" value="1" /> </span>
+				<label py:when="'radio'"
+					py:for="idx, option in enumerate(field.options)" class="disabled">
+					<input type="radio" name="bmod_value_${field.name}" 
+					value="${option}" disabled="disabled" /> ${option} 
+				</label>
+				<input py:otherwise="" type="text" id="bmod_value_${field.name}"
+					name="bmod_value_${field.name}" value="" disabled="disabled" />
+			</py:choose>
+			</td>
+		</py:for>
+	</tr>
+	<tr>
+		<th style="text-align: left"><input type="checkbox"
+			id="bmod_flag_comment" name="bmod_flag_comment" checked="checked" /> <label
+			for="bmod_value_comment">Comment:</label></th>
+		<td class="fullrow" colspan="3"><input type="text"
+			id="bmod_value_comment" name="bmod_value_comment" value="" size="70" />
+		</td>
+	</tr>
+</table>
+<div>
+<input type="hidden" name="selectedTickets" value=""/>
+<input type="submit" id="batchmod" name="batchmod" value="Change tickets" /></div>
+<py:for each="constraint_name, constraint in constraints.items()">
+  <py:for each="constraint_value in constraint['values']">
+    <input type="hidden" name="${constraint_name}" value="${constraint_value}" />
+    <input py:if="constraint['mode']" type="hidden" name="${constraint_name}_mode" value="${constraint['mode']}" />
+  </py:for>
+</py:for>
+<py:for each="column in col">
+  <input type="hidden" name="col" value="${column}" />
+</py:for>
+<input py:if="req.args['group']" type="hidden" name="group" value="${req.args['group']}" />
+<input py:if="req.args['groupdesc']" type="hidden" name="groupdesc" value="${req.args['groupdesc']}" />
+</fieldset>
+<script type="text/javascript">
+$(document).ready(function(){
+<!--
+  $("//table.listing//tr//td[@class=id]").each(
+    function() {
+      tId=$(this).text().substring(1); 
+      $(this).before('<td><input type="checkbox" name="selectedTicket" class="bmod_selector" value="'+tId+'"/></td>');
+    }  
+  ) 
+  $("//table.listing//tr//th[@class=id]").each(
+    function() { 
+      $(this).before('<th class="bmod_selector"><input type="checkbox" name="bmod_toggleGroup" /></th>');
+    }  
+  ) 
+  $("input[@name=bmod_toggleGroup]").change(function() { 
+    $("../../../..//input[@type=checkbox]",this).attr("checked",this.checked);
+  })
+-->
+  <py:for each="field in fields">
+    $("input[@id=bmod_flag_${field.name}]").change(function() { $("*[@name=bmod_value_${field.name}]").enable(this.checked);} )
+  </py:for>
+  $("input#bmod_flag_comment").change(function() {enableControl("bmod_value_comment",this.checked);} )
+  $("form#batchmod-form").submit(function() {
+    var selectedTix=[];    
+    $("input[@name=selectedTicket]:checked").each( function(){ selectedTix.push(this.value);} ); 
+    $("input[@name=selectedTickets]").val(selectedTix);
+  })
+  $(document.getElementById("columns")).toggleClass("collapsed");
+});
+</script>
+</form>
+</body>
+</html>
\ No newline at end of file
Index: batchmod/templates/batchmod.cs
===================================================================
--- batchmod/templates/batchmod.cs	(revision 2711)
+++ batchmod/templates/batchmod.cs	(working copy)
@@ -1,363 +0,0 @@
-<?cs include:"header.cs" ?>
-<?cs include:"macros.cs" ?>
-
-<div id="ctxtnav" class="nav"><?cs
- if:query.report_href ?><ul>
-  <li class="first"><a href="<?cs
-    var:query.report_href ?>">Available Reports</a></li>
-  </ul><?cs
- /if ?>
-</div>
-
-<?cs def:num_matches(v) ?><span class="numrows">(<?cs 
- alt:v ?>No<?cs /alt ?> match<?cs if:v != 1 ?>es<?cs /if ?>)</span><?cs
-/def ?>
-
-<div id="content" class="query">
- <h1><?cs var:title ?> <?cs call:num_matches(query.num_matches) ?></h1>
-
-<form id="query" method="post" action="<?cs var:trac.href.query ?>">
- <fieldset id="filters">
-  <legend>Filters</legend>
-  <?cs def:checkbox_checked(constraint, option) ?><?cs
-   set:checked = 0 ?><?cs
-   each:value = constraint.values ?><?cs
-    if:(value == option) == (constraint.mode == '') ?><?cs
-      set:checked = 1 ?><?cs
-    /if ?><?cs
-   /each ?><?cs
-   if:checked ?> checked="checked"<?cs /if ?><?cs
-  /def ?>
-  <table summary="Query filters">
-   <tbody><tr style="height: 1px"><td colspan="4"></td></tr></tbody><?cs
-   each:field = query.fields ?><?cs
-   each:constraint = query.constraints ?><?cs
-    if:name(field) == name(constraint) ?>
-     <tbody><tr class="<?cs var:name(field) ?>">
-      <th scope="row"><label><?cs var:field.label ?></label></th><?cs
-      if:field.type != "radio" && field.type != "checkbox" ?>
-       <td class="mode">
-        <select name="<?cs var:name(field) ?>_mode"><?cs
-         each:mode = query.modes[field.type] ?>
-          <option value="<?cs var:mode.value ?>"<?cs
-           if:mode.value == constraint.mode ?> selected="selected"<?cs
-           /if ?>><?cs var:mode.name ?></option><?cs
-         /each ?>
-        </select>
-       </td><?cs
-      /if ?>
-      <td class="filter"<?cs
-        if:field.type == "radio" || field.type == "checkbox" ?> colspan="2"<?cs
-        /if ?>><?cs
-       if:field.type == "select" ?><?cs
-        each:value = constraint.values ?>
-         <select name="<?cs var:name(constraint) ?>"><option></option><?cs
-         each:option = field.options ?>
-          <option<?cs if:option == value ?> selected="selected"<?cs /if ?>><?cs
-            var:option ?></option><?cs
-         /each ?></select><?cs
-         if:name(value) != len(constraint.values) - 1 ?>
-          </td>
-          <td class="actions"><input type="submit" name="rm_filter_<?cs
-             var:name(field) ?>_<?cs var:name(value) ?>" value="-" /></td>
-         </tr><tr class="<?cs var:name(field) ?>">
-          <th colspan="2"><label>or</label></th>
-          <td class="filter"><?cs
-         /if ?><?cs
-        /each ?><?cs
-       elif:field.type == "radio" ?><?cs
-        each:option = field.options ?>
-         <input type="checkbox" id="<?cs var:name(field) ?>_<?cs
-           var:option ?>" name="<?cs var:name(field) ?>" value="<?cs
-           var:option ?>"<?cs call:checkbox_checked(constraint, option) ?> />
-         <label for="<?cs var:name(field) ?>_<?cs var:option ?>"><?cs
-           alt:option ?>none<?cs /alt ?></label><?cs
-        /each ?><?cs
-       elif:field.type == "checkbox" ?>
-        <input type="radio" id="<?cs var:name(field) ?>_on" name="<?cs
-          var:name(field) ?>" value="1"<?cs
-          if:constraint.mode != '!' ?> checked="checked"<?cs /if ?> />
-        <label for="<?cs var:name(field) ?>_on">yes</label>
-        <input type="radio" id="<?cs var:name(field) ?>_off" name="<?cs
-          var:name(field) ?>" value="!1"<?cs
-          if:constraint.mode == '!' ?> checked="checked"<?cs /if ?> />
-        <label for="<?cs var:name(field) ?>_off">no</label><?cs
-       elif:field.type == "text" ?><?cs
-        each:value = constraint.values ?>
-        <input type="text" name="<?cs var:name(field) ?>" value="<?cs
-          var:value ?>" size="42" /><?cs
-         if:name(value) != len(constraint.values) - 1 ?>
-          </td>
-          <td class="actions"><input type="submit" name="rm_filter_<?cs
-             var:name(field) ?>_<?cs var:name(value) ?>" value="-" /></td>
-         </tr><tr class="<?cs var:name(field) ?>">
-          <th colspan="2"><label>or</label></th>
-          <td class="filter"><?cs
-         /if ?><?cs
-        /each ?><?cs
-       /if ?>
-      </td>
-      <td class="actions"><input type="submit" name="rm_filter_<?cs
-         var:name(field) ?><?cs
-         if:field.type != 'radio' ?>_<?cs
-          var:len(constraint.values) - 1 ?><?cs
-         /if ?>" value="-" /></td>
-     </tr></tbody><?cs /if ?><?cs
-    /each ?><?cs
-   /each ?>
-   <tbody><tr class="actions">
-    <td class="actions" colspan="4" style="text-align: right">
-     <label for="add_filter">Add filter</label>&nbsp;
-     <select name="add_filter" id="add_filter">
-      <option></option><?cs
-      each:field = query.fields ?>
-       <option value="<?cs var:name(field) ?>"<?cs
-         if:field.type == "radio" ?><?cs
-          if:len(query.constraints[name(field)]) != 0 ?> disabled="disabled"<?cs
-          /if ?><?cs
-         /if ?>><?cs var:field.label ?></option><?cs
-      /each ?>	
-     </select>
-     <input type="submit" name="add" value="+" />
-    </td>
-   </tr></tbody>
-  </table>
- </fieldset>
- <p class="option">
-  <label for="group">Group results by</label>
-  <select name="group" id="group">
-   <option></option><?cs
-   each:field = query.fields ?><?cs
-    if:field.type == 'select' || field.type == 'radio' ||
-       name(field) == 'owner' ?>
-     <option value="<?cs var:name(field) ?>"<?cs
-       if:name(field) == query.group ?> selected="selected"<?cs /if ?>><?cs
-       var:field.label ?></option><?cs
-    /if ?><?cs
-   /each ?>
-  </select>
-  <input type="checkbox" name="groupdesc" id="groupdesc"<?cs
-    if:query.groupdesc ?> checked="checked"<?cs /if ?> />
-  <label for="groupdesc">descending</label>
-  <script type="text/javascript">
-    var group = document.getElementById("group");
-    var updateGroupDesc = function() {
-      enableControl('groupdesc', group.selectedIndex > 0);
-    }
-    addEvent(window, 'load', updateGroupDesc);
-    addEvent(group, 'change', updateGroupDesc);
-  </script>
- </p>
- <p class="option">
-  <input type="checkbox" name="verbose" id="verbose"<?cs
-    if:query.verbose ?> checked="checked"<?cs /if ?> />
-  <label for="verbose">Show full description under each result</label>
- </p>
- <div class="buttons">
-  <input type="hidden" name="order" value="<?cs var:query.order ?>" />
-  <?cs if:query.desc ?><input type="hidden" name="desc" value="1" /><?cs /if ?>
-  <input type="submit" name="update" value="Update" />
- </div>
- <hr />
-</form>
-<script type="text/javascript"><?cs set:idx = 0 ?>
- var properties={<?cs each:field = query.fields ?><?cs
-  var:name(field) ?>:{type:"<?cs var:field.type ?>",label:"<?cs
-  var:field.label ?>",options:[<?cs
-   each:option = field.options ?>"<?cs var:option ?>"<?cs
-    if:name(option) < len(field.options) -1 ?>,<?cs /if ?><?cs
-   /each ?>]}<?cs
-  set:idx = idx + 1 ?><?cs if:idx < len(query.fields) ?>,<?cs /if ?><?cs
- /each ?>};<?cs set:idx = 0 ?>
- var modes = {<?cs each:type = query.modes ?><?cs var:name(type) ?>:[<?cs
-  each:mode = type ?>{text:"<?cs var:mode.name ?>",value:"<?cs var:mode.value ?>"}<?cs
-   if:name(mode) < len(type) -1 ?>,<?cs /if ?><?cs
-  /each ?>]<?cs
-  set:idx = idx + 1 ?><?cs if:idx < len(query.modes) ?>,<?cs /if ?><?cs
- /each ?>};
- initializeFilters();
-</script>
-
-<?cs def:thead() ?>
- <thead><tr><?cs each:header = query.headers ?>
-  <th class="<?cs var:header.name ?><?cs if:query.order == header.name ?> <?cs
-    if:query.desc ?>desc<?cs else ?>asc<?cs /if ?><?cs /if ?>">
-   <a title="Sort by <?cs var:header.label ?><?cs
-     if:query.order == header.name && !query.desc ?> (descending)<?cs
-     /if ?>" href="<?cs var:header.href ?>"><?cs var:header.label ?></a>
-  </th><?cs
- /each ?></tr></thead>
-<?cs /def ?>
-
-<?cs if:len(query.results) ?><?cs
- if:!query.group ?>
-  <table class="listing tickets">
-  <?cs call:thead() ?><tbody><?cs
- /if ?><?cs
- each:result = query.results ?><?cs
-  if:result[query.group] != prev_group ?>
-   <?cs if:prev_group ?></tbody></table><?cs /if ?>
-   <h2><?cs
-    each:field = query.fields ?><?cs
-     if:name(field) == query.group ?><?cs
-      var:field.label ?><?cs
-     /if ?><?cs
-    /each ?>: <?cs var:result[query.group] ?> <?cs call:num_matches(query.num_matches_group[result[query.group]]) ?></h2>
-   <table class="listing tickets">
-   <?cs call:thead() ?><tbody><?cs
-  /if ?>
-  <tr class="<?cs
-   if:name(result) % 2 ?>odd<?cs else ?>even<?cs /if ?> prio<?cs
-   var:result.priority_value ?><?cs
-   if:result.added ?> added<?cs /if ?><?cs
-   if:result.changed ?> changed<?cs /if ?><?cs
-   if:result.removed ?> removed<?cs /if ?>"><?cs
-  each:header = query.headers ?><?cs
-   if:name(header) == 0 ?><td class="id"><a href="<?cs
-    var:result.href ?>" title="View ticket"><?cs var:result.id ?></a></td><?cs
-   else ?><td class="<?cs var:header.name ?>"><?cs
-     if:header.name == 'summary' ?><a href="<?cs
-      var:result.href ?>" title="View ticket"><?cs
-      var:result.summary ?></a><?cs
-     else ?><span><?cs var:result[header.name] ?></span><?cs
-     /if ?></td><?cs
-   /if ?><?cs
-  /each ?>
-  <?cs if:query.verbose ?>
-   </tr><tr class="fullrow"><td colspan="<?cs var:len(query.headers) ?>">
-    <p class="meta">Reported by <strong><?cs var:result.reporter ?></strong>,
-    <?cs var:result.time ?><?cs if:result.description ?>:<?cs /if ?></p>
-    <?cs if:result.description ?><p><?cs var:result.description ?></p><?cs /if ?>
-   </td>
-  <?cs /if ?><?cs set:prev_group = result[query.group] ?>
- </tr><?cs /each ?>
-</tbody></table><?cs
-/if ?>
-
-
-<?cs if:trac.acl.TICKET_BATCH_MODIFY ?>
-
-<br/>
-<form id="batchmod" method="post" action="<?cs var:trac.href.query ?>">
-<fieldset id="properties">
-<legend>Batch Modify Properties</legend>
-
-  <table>
-    <tr>
-        <th style="text-align: left">
-            <input type="checkbox" id="bm_comment" name="bm_comment" />
-            <label for="comment">Comment:</label>
-        </th>
-        <td class="fullrow" colspan="3">
-         <input type="text" id="comment" name="comment" value="<?cs
-         var:ticket.summary ?>" size="70" disabled="true"/>
-        </td>
-        <script type="text/javascript">
-            var bm_comment = document.getElementById("bm_comment");
-            var comment = document.getElementById("comment");
-            var updateComment = function() {
-                enableControl('comment', bm_comment.checked);
-            }
-            addEvent(window, 'load', updateComment);
-            addEvent(bm_comment, 'change', updateComment);
-        </script>
-    </tr>
-
-  <tr><?cs set:num_fields = 0 ?><?cs
-  each:field = ticket.fields ?><?cs
-   if:!field.skip ?><?cs
-    set:num_fields = num_fields + 1 ?><?cs
-   /if ?><?cs
-  /each ?><?cs set:idx = 0 ?><?cs
-   each:field = ticket.fields ?><?cs
-    if:!field.skip ?>
-
-
-   <?cs set:fullrow = field.type == 'textarea' ?><?cs
-     if:fullrow && idx % 2 ?><?cs set:idx = idx + 1 ?><th class="col2"></th><td></td></tr><tr><?cs /if ?>
-
-    <th class="col<?cs var:idx % 2 + 1 ?>" style="text-align: left">
-    <?cs if:field.type != 'radio' ?>
-        <input type="checkbox" id="bm_<?cs var:name(field) ?>" name="bm_<?cs var:name(field) ?>" />
-        <label for="<?cs var:name(field) ?>">
-        <script type="text/javascript">
-            var bm_<?cs var:name(field) ?> = document.getElementById("bm_<?cs var:name(field) ?>");
-            var ctrl_<?cs var:name(field) ?> = document.getElementById("<?cs var:name(field) ?>");
-            var update<?cs var:name(field) ?>  = function() {
-                enableControl('<?cs var:name(field) ?>', bm_<?cs var:name(field) ?>.checked);
-            }
-            addEvent(window, 'load', update<?cs var:name(field) ?>);
-            addEvent(bm_<?cs var:name(field) ?>, 'change', update<?cs var:name(field) ?>);
-        </script>
-    <?cs /if ?>
-    <?cs alt:field.label ?>
-        <input type="checkbox" id="bm_<?cs var:field.name ?>" name="bm_<?cs var:field.name ?>" />
-        <?cs var:field.name ?>
-        <script type="text/javascript">
-            var bm_<?cs var:field.name ?> = document.getElementById("bm_<?cs var:field.name ?>");
-            var ctrl_<?cs var:field.name ?> = document.getElementById("<?cs var:field.name ?>");
-            var update<?cs var:field.name ?>  = function() {
-                enableControl('<?cs var:field.name ?>', bm_<?cs var:field.name ?>.checked);
-            }
-            addEvent(window, 'load', update<?cs var:field.name ?>);
-            addEvent(bm_<?cs var:field.name ?>, 'change', update<?cs var:field.name ?>);
-        </script>
-    <?cs /alt ?>:
-    <?cs if:field.type != 'radio' ?></label><?cs /if ?>
-    </th>
-
-
-     <td<?cs if:fullrow ?> colspan="3"<?cs /if ?>><?cs
-      if:field.type == 'text' ?><input type="text" id="<?cs
-        var:name(field) ?>" name="<?cs
-        var:name(field) ?>" value="<?cs var:ticket[name(field)] ?>" disabled="true" /><?cs
-      elif:field.type == 'select' ?>
-        <select id="<?cs
-        var:name(field) ?>" name="<?cs
-        var:name(field) ?>" enabled="false"><?cs
-        if:field.optional ?><option></option><?cs /if ?><?cs
-        each:option = field.options ?><option<?cs
-         if:option == ticket[name(field)] ?> selected="selected"<?cs /if ?>><?cs
-         var:option ?></option><?cs
-        /each ?></select><?cs
-      elif:field.type == 'checkbox' ?><input type="hidden" name="checkbox_<?cs
-        var:name(field) ?>" /><input type="checkbox" id="<?cs
-        var:name(field) ?>" name="<?cs
-        var:name(field) ?>" value="1"<?cs
-        if:ticket[name(field)] ?> checked="checked"<?cs /if ?> disabled="true" /><?cs
-      elif:field.type == 'textarea' ?><textarea id="<?cs
-        var:name(field) ?>" name="<?cs
-        var:name(field) ?>"<?cs
-        if:field.height ?> rows="<?cs var:field.height ?>"<?cs /if ?><?cs
-        if:field.width ?> cols="<?cs var:field.width ?>"<?cs /if ?> disabled="true" >
-<?cs var:ticket[name(field)] ?></textarea><?cs
-      elif:field.type == 'radio' ?><?cs set:optidx = 0 ?><?cs
-       each:option = field.options ?><label><input type="radio" id="<?cs
-         var:name(field) ?>" name="<?cs
-         var:name(field) ?>" value="<?cs var:option ?>"<?cs
-         if:ticket[name(field)] == option ?> checked="checked"<?cs /if ?> disabled="true"/> <?cs
-         var:option ?></label> <?cs set:optidx = optidx + 1 ?><?cs
-        /each ?><?cs
-      /if ?></td><?cs
-     if:idx % 2 || fullrow ?><?cs
-      if:idx < num_fields - 1 ?></tr><tr><?cs
-      /if ?><?cs
-     elif:idx == num_fields - 1 ?><th class="col2"></th><td></td><?cs
-     /if ?><?cs set:idx = idx + #fullrow + 1 ?><?cs
-    /if ?><?cs
-   /each ?></tr>
-
-  </table>
-</fieldset>
-
-<input type="submit" name="batchmod" value="Batch Modify" />
-</form>
-<?cs /if ?>
-
-<div id="help">
- <strong>Note:</strong> See <a href="<?cs var:trac.href.wiki ?>/TracQuery">TracQuery</a> 
- for help on using queries.
-</div>
-
-</div>
-<?cs include:"footer.cs" ?>
Index: setup.py
===================================================================
--- setup.py	(revision 2711)
+++ setup.py	(working copy)
@@ -1,21 +1,22 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
-# Copyright (C) 2006 Ashwin Phatak 
+# Copyright (C) 2006 Ashwin Phatak
+# Copyright (C) 2007 Dave Gynn (dgynn@optaros.com)
 
 from setuptools import setup, find_packages
 
 PACKAGE = 'BatchModify'
-VERSION = '0.1.0'
+VERSION = '0.5.0'
 
 setup(
     name=PACKAGE, version=VERSION,
     description='Allows batch modification of tickets',
     author="Ashwin Phatak", author_email="ashwinpphatak@gmail.com",
     license='BSD', url='http://trac-hacks.org/wiki/BatchModifyPlugin',
-    packages=find_packages(exclude=['ez_setup', '*.tests*']),
+    packages = ['batchmod'],
     package_data={
         'batchmod': [
-            'templates/*.cs'
+            'templates/*.html'
         ]
     },
     entry_points = {
