Index: trac/ticket/web_ui.py
===================================================================
--- trac/ticket/web_ui.py	(revision 4932)
+++ trac/ticket/web_ui.py	(working copy)
@@ -264,12 +264,13 @@
     def _process_newticket_request(self, req):
         context = Context(self.env, req)('ticket')
         req.perm.require('TICKET_CREATE')
+        preview = 'preview' in req.args
 
         if req.method == 'POST' and 'field_owner' in req.args and \
                'TICKET_MODIFY' not in req.perm:
             del req.args['field_owner']
 
-        if req.method == 'POST' and 'preview' not in req.args:
+        if req.method == 'POST' and not preview:
             self._do_create(context) # ...redirected
 
         # Preview a new ticket
@@ -279,9 +280,9 @@
         self._populate(req, ticket)
         ticket.values['reporter'] = get_reporter_id(req, 'reporter')
 
-        data = {}
-        data['ticket'] = ticket
-        data['context'] = context
+        data = {'ticket': ticket,
+                'context': context,
+                'action': preview and 'view' or 'edit'}
 
         field_names = [field['name'] for field in ticket.fields
                        if not field.get('custom')]
@@ -322,18 +323,19 @@
 
     def _process_ticket_request(self, req):
         req.perm.require('TICKET_VIEW')
-        action = req.args.get('action', ('history' in req.args and 'history' or
-                                         'view'))
+        action = 'history' in req.args and 'history' or 'view'
+        action = req.args.get('action', action)
+        # FIXME: currently action can also be a transtion: leave, accept, ...
+        edit = 'edit' in req.args
+        preview = 'preview' in req.args
         id = int(req.args.get('id'))
         
         context = Context(self.env, req)('ticket', id)
-        
         ticket = context.resource
         
-        data = {}
-        data['ticket'] = ticket
-        data['context'] = context
-        
+        data = {'ticket': ticket, 'context': context,
+                'edit': edit, 'preview': preview}
+
         if action in ('history', 'diff'):
             field = req.args.get('field')
             if field:
@@ -347,7 +349,7 @@
             elif action == 'diff':
                 return self._render_diff(context, data, text_fields)
         elif req.method == 'POST':
-            if 'preview' not in req.args:
+            if not preview and not edit:
                 self._do_save(context)
             else:
                 # Use user supplied values
Index: trac/ticket/templates/ticket.html
===================================================================
--- trac/ticket/templates/ticket.html	(revision 4932)
+++ trac/ticket/templates/ticket.html	(working copy)
@@ -39,11 +39,11 @@
       ${prevnext_nav('Ticket', 'Back to Query')}
     </div>
 
-    <div id="content" class="ticket">
-      <!-- Ticket number, summary, etc heading -->
+    <div id="content" class="ticket" py:with="latest_version = not version and version != 0 ">
+
+      <!-- Ticket number, summary, etc. heading -->
       <h1>
-    
-        <py:choose> 
+        <py:choose>
           <py:when test="ticket.id">
             <a py:strip="not version and version != 0" href="${href.ticket(ticket.id)}">
               Ticket #${ticket.id}
@@ -54,9 +54,11 @@
           </py:otherwise>
         </py:choose>
 
-        <span class="status">(<py:choose><py:when test="ticket.status">${ticket.status}</py:when><py:otherwise>new</py:otherwise></py:choose> <py:if
+        <span py:if="ticket.id" class="status">
+          (<py:choose><py:when test="ticket.status">${ticket.status}</py:when><py:otherwise>new</py:otherwise></py:choose> <py:if
             test="ticket.type">${ticket.type}</py:if><py:if
-            test="ticket.resolution">: ${ticket.resolution}</py:if>)</span>
+            test="ticket.resolution">: ${ticket.resolution}</py:if>)
+        </span>
         <py:choose test="">
           <py:when test="type(version) is Undefined" />
           <py:when test="version is None" />
@@ -69,72 +71,115 @@
         </py:choose>
       </h1>
 
-      <!-- Ticket (pre)view -->
-      <div id="ticket" py:attrs="(('preview' in req.args) or (not ticket.id)) and {'class':'ticketdraft'} or {}">
-        <div class="date">
-          <p py:if="opened">Opened ${dateinfo(opened)} ago</p>
-          <p py:if="lastmod">Last modified ${dateinfo(lastmod)} ago</p>
-          <p py:if="not opened"><i>(future ticket)</i></p>
-        </div>
-        <!-- use a placeholder if it's a new ticket -->
-        <h2 class="summary searchable">
-          <py:choose>
-            <py:when test="ticket.summary">
-              ${ticket.summary}
-            </py:when>
-            <py:otherwise>
-              <i>(ticket summary)</i>
-            </py:otherwise>
-          </py:choose>
-        </h2>
+      <form py:if="latest_version and ('TICKET_CHGPROP' in perm or ('TICKET_CREATE' in perm and not ticket.id))"
+            py:strip="not edit and not preview" action="#preview" method="post">
 
-        <table class="properties"
-               py:with="fields = [f for f in fields if not f.skip]">
-          <tr>
-            <th id="h_reporter">Reported by:</th>
-            <td headers="h_reporter" class="searchable">${authorinfo(ticket.reporter)}</td>
-            <th id="h_owner">Assigned to:</th>
-            <td headers="h_owner">${authorinfo(ticket.owner)}
-              <py:if test="ticket.status == 'assigned'">(accepted)</py:if>
-            </td>
-          </tr>
-          <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 py:if="idx == 0 or not fullrow">
-                <py:if test="field">${field.label or field.name}:</py:if>
-              </th>
-              <td py:if="idx == 0 or not fullrow"
+        <!-- Ticket (pre)view -->
+        <div id="ticket" py:attrs="preview and {'class':'ticketdraft'}">
+
+          <!--! created/last modified -->
+          <div class="date">
+            <p py:if="opened">Opened ${dateinfo(opened)} ago</p>
+            <p py:if="lastmod">Last modified ${dateinfo(lastmod)} ago</p>
+            <p py:if="not opened"><i>(not yet created)</i></p>
+          </div>
+
+          <!--! summary -->
+          <h2 class="summary searchable" py:choose="">
+            <py:when test="ticket.summary">${ticket.summary}<br /></py:when>
+            <py:otherwise>Summary: </py:otherwise>
+            <input py:attrs="edit and {'type': 'text', 'size': '70'} or {'type': 'hidden'}"
+              id="field-summary" name="field_summary" value="${ticket.summary}" />
+          </h2>
+
+          <table class="properties"
+            py:with="fields = [f for f in fields if not f.skip]">
+            <tr>
+              <th id="h_reporter">Reported by:</th>
+              <td headers="h_reporter" class="searchable">${authorinfo(ticket.reporter)}</td>
+              <th id="h_owner">Assigned to:</th>
+              <td headers="h_owner">${authorinfo(ticket.owner)}
+                <py:if test="ticket.status == 'assigned'">(accepted)</py:if>
+              </td>
+            </tr>
+            <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 py:if="idx == 0 or not fullrow">
+                  <py:if test="field">${field.label or field.name}:</py:if>
+                </th>
+                <td py:if="idx == 0 or not fullrow"
                   class="${field.name in ('cc', 'keywords') and 'searchable' or None}"
                   colspan="${fullrow and 3 or None}">
-                <py:if test="field">${ticket[field.name]}</py:if>
-              </td>
-            </py:for>
-          </tr>
-        </table>
-        <form py:if="ticket.description" method="get" action="#comment"
-              class="printableform">
-          <div class="description">
-            <h3 id="comment:description">
-              <!-- check ticket.id; you can't reply to a ticket that does not yet exist. -->
-              <span py:if="'TICKET_APPEND' in perm and ticket.id" class="inlinebuttons">
-                <input type="hidden" name="replyto" value="description" />
-                <input type="submit" name="reply" value="Reply" title="Reply, quoting this description" />
-              </span>
-              Description
-              <span py:if="description_change" class="lastmod"
-                title="$description_change.date">
-                (last modified by ${authorinfo(description_change.author)})
-                (<a href="${href.ticket(ticket.id, action='diff', version=description_change.cnum)}">diff</a>)
-              </span>
-            </h3>
-            <div class="searchable">
-              ${wiki_to_html(context, ticket.description)}
+                  <py:if test="field">
+                    <input py:if="preview" type="hidden" id="field-${field.name}"
+                      name="field_${field.name}" value="${ticket[field.name]}" />
+                    <py:if test="ticket.id">${ticket[field.name]} <br /></py:if>
+                    <py:choose test="edit and field.type">
+                      <py:when test="False" />
+                      <select py:when="'select'" id="field-${field.name}" name="field_${field.name}">
+                        <option py:if="field.optional"></option>
+                        <option py:for="option in field.options"
+                          selected="${ticket[field.name] == option or None}"
+                          py:content="option"></option>
+                      </select>
+                      <textarea py:when="'textarea'" id="field-${field.name}" name="field_${field.name}"
+                        cols="${field.width}" rows="${field.height}"
+                        py:content="ticket[field.name]"></textarea>
+                      <span py:when="'checkbox'">
+                        <input type="checkbox" id="field-${field.name}" name="field_${field.name}"
+                          checked="${ticket[field.name] == '1' and 'checked' or None}" value="1" />
+                        <input type="hidden" name="field_checkbox_${field.name}" value="1" />
+                      </span>
+                      <label py:when="'radio'"
+                        py:for="idx, option in enumerate(field.options)">
+                        <input type="radio" name="field_${field.name}" value="${option}"
+                          checked="${ticket[field.name] == option or None}" />
+                        ${option}
+                      </label>
+                      <input py:otherwise="" type="text" id="field-${field.name}"
+                        name="field_${field.name}" value="${ticket[field.name]}" />
+                    </py:choose>
+                  </py:if>
+                </td>
+              </py:for>
+            </tr>
+          </table>
+          <form py:strip="edit or preview" method="get" action="#comment"
+            class="printableform">
+            <div class="description">
+              <h3 id="comment:description">
+                <!-- check ticket.id; you can't reply to a ticket that does not yet exist. -->
+                <span py:if="'TICKET_APPEND' in perm and ticket.id and not edit" class="inlinebuttons">
+                  <input type="hidden" name="replyto" value="description" />
+                  <input type="submit" name="reply" value="Reply" title="Reply, quoting this description" />
+                </span>
+                Description
+                <py:choose test="">
+                  <py:when test="edit">
+                    (you may use <a tabindex="42" class="lastmod" href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here):
+                  </py:when>
+                  <span py:otherwise="" py:if="description_change" class="lastmod"
+                    title="$description_change.date">
+                    (last modified by ${authorinfo(description_change.author)})
+                    (<a href="${href.ticket(ticket.id, action='diff', version=description_change.cnum)}">diff</a>)
+                  </span>
+                </py:choose>
+              </h3>
+              <div class="searchable" py:choose="">
+                <py:if test="ticket.description">${wiki_to_html(context, ticket.description)}</py:if>
+                <py:choose>
+                  <textarea py:when="edit"
+                    id="field-description" name="field_description" class="wikitext"
+                    rows="10" cols="68" py:content="ticket.description" />
+                  <input py:otherwise="" type="hidden" id="field-description" name="field_description"
+                    value="ticket.description" />
+                </py:choose>
+              </div>
             </div>
-          </div>
-        </form>
-      </div>
-      <!-- End of ticket (pre)view -->
+          </form>
+        </div>
+        <!-- End of ticket (pre)view -->
 
       <!-- do not show attachments for old versions of this ticket or for new
         tickets -->
@@ -215,11 +260,13 @@
         </div>
       </py:if>
 
+      <form py:if="latest_version and ('TICKET_APPEND' in perm)"
+            py:strip="edit" action="#preview" method="post">
+
       <!-- TODO: edit form; handle ticket create perms. -->
-      <form py:if="not version and version != 0 and ('TICKET_APPEND' in perm or 'TICKET_CHGPROP' in perm or ('TICKET_CREATE' in perm and not ticket.id))"
-            action="#preview" method="post">
+      <!--! -->
         <h3 py:if="ticket.id"><a name="edit" onfocus="$('#comment').get(0).focus()">
-            Add/Change #${ticket.id} (${ticket.summary})</a></h3>
+            Comment on #${ticket.id} (${ticket.summary})</a></h3>
         <div py:if="authname == 'anonymous'" class="field">
           <fieldset>
             <table>
@@ -245,75 +292,6 @@
           </fieldset>
         </div>
 
-        <fieldset id="properties" py:if="'TICKET_CHGPROP' in req.perm or ('TICKET_CREATE' in req.perm and not ticket.id)"
-                  py:with="fields = [f for f in fields if not f.skip]">
-          <legend>Change Properties</legend>
-          <table>
-            <tr>
-              <th><label for="field-summary">Summary:</label></th>
-              <td class="fullrow" colspan="3">
-                <input type="text" id="field-summary" name="field_summary" value="${ticket.summary}"
-                  size="70" />
-              </td>
-            </tr>
-            <py:if test="'TICKET_ADMIN' in perm">
-              <tr>
-                <th><label for="field-reporter">Reporter:</label></th>
-                <td class="fullrow" colspan="3">
-                  <input type="text" id="field-reporter" name="field_reporter"
-                    value="${ticket.reporter}" size="70" />
-                </td>
-              </tr>
-            </py:if>
-            <py:if test="'TICKET_ADMIN' in perm or not ticket.id">
-              <tr>
-                <th><label for="field-description">Description:</label></th>
-                <td class="fullrow" colspan="3">
-                  <textarea id="field-description" name="field_description" class="wikitext"
-                    rows="10" cols="68" py:content="ticket.description"></textarea>
-                </td>
-              </tr>
-            </py:if>
-            <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 class="col${idx + 1}" py:if="idx == 0 or not fullrow">
-                  <label for="field-${field.name}" py:if="field"
-                         py:strip="field.type == 'radio'">${field.label or field.name}:</label>
-                </th>
-                <td class="col${idx + 1}" py:if="idx == 0 or not fullrow"
-                    py:attrs="{'colspan': fullrow and 3 or None}">
-                  <py:choose test="field.type" py:if="field">
-                    <select py:when="'select'" id="field-${field.name}" name="field_${field.name}">
-                      <option py:if="field.optional"></option>
-                      <option py:for="option in field.options"
-                              selected="${ticket[field.name] == option or None}"
-                              py:content="option"></option>
-                    </select>
-                    <textarea py:when="'textarea'" id="field-${field.name}" name="field_${field.name}"
-                              cols="${field.width}" rows="${field.height}"
-                              py:content="ticket[field.name]"></textarea>
-                    <span py:when="'checkbox'">
-                      <input type="checkbox" id="field-${field.name}" name="field_${field.name}"
-                             checked="${ticket[field.name] == '1' and 'checked' or None}" value="1" />
-                      <input type="hidden" name="field_checkbox_${field.name}" value="1" />
-                    </span>
-                    <label py:when="'radio'"
-                           py:for="idx, option in enumerate(field.options)">
-                      <input type="radio" name="field_${field.name}" value="${option}"
-                             checked="${ticket[field.name] == option or None}" />
-                      ${option}
-                    </label>
-                    <input py:otherwise="" type="text" id="field-${field.name}"
-                           name="field_${field.name}" value="${ticket[field.name]}" />
-                  </py:choose>
-                </td>
-              </py:for>
-            </tr>
-          </table>
-
-        </fieldset>
-
         <py:choose test="not actions or actions == ['leave']">
 
           <input py:when="True" type="hidden" name="action" value="leave" />
@@ -361,17 +339,53 @@
           <input type="hidden" name="ts" value="${timestamp}" />
           <input type="hidden" name="replyto" value="${replyto}" />
           <input type="hidden" name="cnum" value="${cnum}" />
-          <input type="submit" name="preview" value="Preview" accesskey="r" />&nbsp;
+          <py:choose test="">
+            <input py:when="edit" type="submit" name="preview" value="Preview" accesskey="r" />
+            <input py:otherwise="" type="submit" name="edit" value="Edit" accesskey="e" />
+          </py:choose>
           <input type="submit" py:attrs="ticket.id and {'value':'Submit changes'} or {'value':'Create ticket'}" />
         </div>
-
       </form>
+      </form>
 
       <div id="help">
         <strong>Note:</strong> See
         <a href="${href.wiki('TracTickets')}">TracTickets</a> for help on using
         tickets.
       </div>
+
+<!--!
+
+        <fieldset id="properties" py:if="'TICKET_CHGPROP' in req.perm or ('TICKET_CREATE' in req.perm and not ticket.id)"
+                  py:with="fields = [f for f in fields if not f.skip]">
+          <legend>Change Properties</legend>
+          <table>
+            <py:if test="'TICKET_ADMIN' in perm">
+              <tr>
+                <th><label for="field-reporter">Reporter:</label></th>
+                <td class="fullrow" colspan="3">
+                  <input type="text" id="field-reporter" name="field_reporter"
+                    value="${ticket.reporter}" size="70" />
+                </td>
+              </tr>
+            </py:if>
+            <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 class="col${idx + 1}" py:if="idx == 0 or not fullrow">
+                  <label for="field-${field.name}" py:if="field"
+                         py:strip="field.type == 'radio'">${field.label or field.name}:</label>
+                </th>
+                <td class="col${idx + 1}" py:if="idx == 0 or not fullrow"
+                    py:attrs="{'colspan': fullrow and 3 or None}">
+                </td>
+              </py:for>
+            </tr>
+          </table>
+
+        </fieldset>
+-->
+
     </div>
   </body>
 </html>
