You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by gj...@apache.org on 2012/10/16 22:06:19 UTC

svn commit: r1398968 [23/28] - in /incubator/bloodhound/trunk/trac: ./ contrib/ doc/ doc/api/ doc/utils/ sample-plugins/ sample-plugins/permissions/ sample-plugins/workflow/ trac/ trac/admin/ trac/admin/templates/ trac/admin/tests/ trac/db/ trac/db/tes...

Modified: incubator/bloodhound/trunk/trac/trac/templates/attachment.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/templates/attachment.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/templates/attachment.html (original)
+++ incubator/bloodhound/trunk/trac/trac/templates/attachment.html Tue Oct 16 20:06:09 2012
@@ -38,18 +38,18 @@
             <py:if test="authname == 'anonymous'">
               <div class="field">
                 <label>Your email or username:<br />
-                  <input type="text" name="author" size="30" value="$author" />
+                  <input type="text" name="author" size="30" value="${attachment.author or author}" />
                 </label>
               </div>
               </py:if>
             <div class="field">
               <label>Description of the file (optional):<br />
-                <input type="text" name="description" size="60" /></label>
+                <input type="text" name="description" size="60" value="$attachment.description"/></label>
             </div>
             <br />
             <py:if test="authname and authname != 'anonymous'">
               <div class="options">
-                <label><input type="checkbox" name="replace" />
+                <label><input type="checkbox" name="replace" checked="${is_replace or None}" />
                   Replace existing attachment of the same name</label>
               </div>
               <br />

Modified: incubator/bloodhound/trunk/trac/trac/templates/diff_div.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/templates/diff_div.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/templates/diff_div.html (original)
+++ incubator/bloodhound/trunk/trac/trac/templates/diff_div.html Tue Oct 16 20:06:09 2012
@@ -24,7 +24,7 @@
                          .offset             - position within the file
 
                      .diffs_title  - a sequence of titles for the list of blocks
-                                     Note: integrate this into .diffs for 0.12 or 0.13.
+                                     Note: integrate this into .diffs for 0.12 or 1.0.
 
        diff      - dict specifying diff style and options
                      .style     - can be 'sidebyside' (4 columns) or 'inline' (3 columns)

Modified: incubator/bloodhound/trunk/trac/trac/templates/layout.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/templates/layout.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/templates/layout.html (original)
+++ incubator/bloodhound/trunk/trac/trac/templates/layout.html Tue Oct 16 20:06:09 2012
@@ -10,7 +10,10 @@
     <title py:with="title = list(select('title/text()'))">
       <py:if test="title">${title} – </py:if>${project.name or 'Trac'}
     </title>
-    <meta py:if="chrome.content_type == 'text/html'" http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <py:if test="chrome.content_type == 'text/html'">
+      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    </py:if>
     <meta py:for="meta in chrome.metas" py:attrs="meta"/>
     <!--[if IE]><script type="text/javascript">
       if (/^#__msie303:/.test(window.location.hash))
@@ -34,6 +37,14 @@
     <py:for each="script in chrome.scripts">
       ${script.prefix}<script type="${script.type}" charset="${script.charset}" src="${script.href}"></script>${script.suffix}
     </py:for>
+    <script py:if="chrome.warnings or chrome.notices" type="text/javascript">
+      jQuery(document).ready(function($) {
+        $(".trac-close-msg").show().click(function () {
+          $(this).closest(".system-message").hide();
+          return false;
+        });
+      });
+    </script>
     ${select("*[local-name() != 'title']|text()|comment()")}
   </head></py:match>
 

Modified: incubator/bloodhound/trunk/trac/trac/templates/list_of_attachments.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/templates/list_of_attachments.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/templates/list_of_attachments.html (original)
+++ incubator/bloodhound/trunk/trac/trac/templates/list_of_attachments.html Tue Oct 16 20:06:09 2012
@@ -25,7 +25,7 @@ Arguments:
   <py:if test="alist.attachments or alist.can_create">
     <div id="attachments" py:choose="">
       <py:when test="compact and alist.attachments">
-        <h3 class="${'foldable' if foldable else None}">Attachments</h3>
+        <h3 class="${'foldable' if foldable else None}">Attachments <span class="trac-count">(${len(alist.attachments)})</span></h3>
         <div>
           <ul>
             <py:for each="attachment in alist.attachments">
@@ -41,7 +41,7 @@ Arguments:
         </div>
       </py:when>
       <py:when test="not compact">
-        <h2 class="${'foldable' if foldable else None}">Attachments</h2>
+        <h3 class="${'foldable' if foldable else None}">Attachments <span class="trac-count">(${len(alist.attachments)})</span></h3>
         <div py:if="alist.attachments or alist.can_create" class="attachments">
           <dl py:if="alist.attachments" class="attachments">
             <py:for each="attachment in alist.attachments">

Modified: incubator/bloodhound/trunk/trac/trac/templates/theme.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/templates/theme.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/templates/theme.html (original)
+++ incubator/bloodhound/trunk/trac/trac/templates/theme.html Tue Oct 16 20:06:09 2012
@@ -33,19 +33,21 @@
     </div>
     ${navigation('mainnav')}
 
-    <div id="main">
+    <div id="main" class="${classes(uisymbols=req.session.get('ui.use_symbols'),
+                                    uinohelp=req.session.get('ui.hide_help'))}">
       <xi:include py:if="value_of('resourcepath_template')" href="${resourcepath_template}" />
 
       <div id="ctxtnav" class="nav">
         <h2>Context Navigation</h2>
-          <ul py:if="chrome.ctxtnav">
-              <li py:for="i, elm in enumerate(chrome.ctxtnav)"
-                  class="${classes(first_last(i, chrome.ctxtnav))}">$elm</li>
-          </ul>
+        <ul py:if="chrome.ctxtnav">
+          <li py:for="i, elm in enumerate(chrome.ctxtnav)"
+              class="${classes(first_last(i, chrome.ctxtnav))}">$elm</li>
+        </ul>
         <hr />
       </div>
 
       <div id="warning" py:if="chrome.warnings" class="system-message">
+        <a class="trac-close-msg" href="#" title="Hide this warning"><span>close</span></a>
         <py:choose test="len(chrome.warnings)">
           <strong>Warning:</strong>
           <py:when test="1">${chrome.warnings[0]}</py:when>
@@ -53,6 +55,7 @@
         </py:choose>
       </div>
       <div id="notice" py:if="chrome.notices" class="system-message">
+        <a class="trac-close-msg" href="#" title="Hide this notice"><span>close</span></a>
         <py:choose test="len(chrome.notices)">
           <py:when test="1">${chrome.notices[0]}</py:when>
           <py:otherwise><ul><li py:for="notice in chrome.notices">$notice</li></ul></py:otherwise>

Modified: incubator/bloodhound/trunk/trac/trac/test.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/test.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/test.py (original)
+++ incubator/bloodhound/trunk/trac/trac/test.py Tue Oct 16 20:06:09 2012
@@ -26,8 +26,9 @@ import sys
 
 try:
     from babel import Locale
+    locale_en = Locale.parse('en_US')
 except ImportError:
-    Locale = None
+    locale_en = None    
 
 from trac.config import Configuration
 from trac.core import Component, ComponentManager
@@ -264,6 +265,9 @@ class EnvironmentStub(Environment):
         self.config.set('logging', 'log_type', 'stderr')
         if enable is not None:
             self.config.set('components', 'trac.*', 'disabled')
+        else:
+            self.config.set('components', 'tracopt.versioncontrol.svn.*',
+                            'enabled')
         for name_or_class in enable or ():
             config_key = self._component_name(name_or_class)
             self.config.set('components', config_key, 'enabled')
@@ -298,7 +302,7 @@ class EnvironmentStub(Environment):
         self.abs_href = Href('http://example.org/trac.cgi')
 
         self.known_users = []
-        translation.activate(Locale and Locale('en', 'US'))
+        translation.activate(locale_en)
         
     def reset_db(self, default_data=None):
         """Remove all data from Trac tables, keeping the tables themselves.
@@ -418,7 +422,9 @@ def suite():
     import trac.web.tests
     import trac.wiki.tests
     import tracopt.mimeview.tests
+    import tracopt.perm.tests
     import tracopt.versioncontrol.git.tests
+    import tracopt.versioncontrol.svn.tests
 
     suite = unittest.TestSuite()
     suite.addTest(trac.tests.basicSuite())
@@ -434,7 +440,9 @@ def suite():
     suite.addTest(trac.web.tests.suite())
     suite.addTest(trac.wiki.tests.suite())
     suite.addTest(tracopt.mimeview.tests.suite())
+    suite.addTest(tracopt.perm.tests.suite())
     suite.addTest(tracopt.versioncontrol.git.tests.suite())
+    suite.addTest(tracopt.versioncontrol.svn.tests.suite())
     suite.addTest(doctest.DocTestSuite(sys.modules[__name__]))
 
     return suite

Modified: incubator/bloodhound/trunk/trac/trac/tests/config.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/tests/config.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/tests/config.py (original)
+++ incubator/bloodhound/trunk/trac/trac/tests/config.py Tue Oct 16 20:06:09 2012
@@ -190,9 +190,10 @@ class ConfigurationTestCase(unittest.Tes
         config = self._read()
 
         class Foo(object):
-            option = ChoiceOption('a', 'option', ['Item1', 2, '3'])
-            other = ChoiceOption('a', 'other', [1, 2, 3])
-            invalid = ChoiceOption('a', 'invalid', ['a', 'b', 'c'])
+            # enclose in parentheses to avoid messages extraction
+            option = (ChoiceOption)('a', 'option', ['Item1', 2, '3'])
+            other = (ChoiceOption)('a', 'other', [1, 2, 3])
+            invalid = (ChoiceOption)('a', 'invalid', ['a', 'b', 'c'])
         
             def __init__(self):
                 self.config = config
@@ -294,7 +295,8 @@ class ConfigurationTestCase(unittest.Tes
         self.assertEquals(['a', 'b'], config.sections())
         
         class Foo(object):
-            section_c = ConfigSection('c', 'Doc for c')
+            # enclose in parentheses to avoid messages extraction
+            section_c = (ConfigSection)('c', 'Doc for c')
             option_c = Option('c', 'option', 'value')
         
         self.assertEquals(['a', 'b', 'c'], config.sections())

Modified: incubator/bloodhound/trunk/trac/trac/tests/functional/testenv.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/tests/functional/testenv.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/tests/functional/testenv.py (original)
+++ incubator/bloodhound/trunk/trac/trac/tests/functional/testenv.py Tue Oct 16 20:06:09 2012
@@ -110,7 +110,7 @@ class FunctionalTestEnvironment(object):
         environment creation.  For anything more complicated, use the
         :meth:`post_create` method.
         """
-        return []
+        return ['tracopt.versioncontrol.svn.*']
 
     def create(self):
         """Create a new test environment.

Modified: incubator/bloodhound/trunk/trac/trac/tests/wikisyntax.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/tests/wikisyntax.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/tests/wikisyntax.py (original)
+++ incubator/bloodhound/trunk/trac/trac/tests/wikisyntax.py Tue Oct 16 20:06:09 2012
@@ -151,7 +151,7 @@ def email_default_context():
 
     context = RenderingContext(Resource('wiki', 'WikiStart'), href=Href('/'), 
                                perm=NoEmailViewPerm())
-    context.req = None # 0.13 FIXME .req shouldn't be required by formatter
+    context.req = None # 1.0 FIXME .req shouldn't be required by formatter
     return context
 
 

Modified: incubator/bloodhound/trunk/trac/trac/ticket/admin.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/admin.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/admin.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/admin.py Tue Oct 16 20:06:09 2012
@@ -342,6 +342,8 @@ class MilestoneAdminPanel(TicketAdminPan
                     'milestones': milestones,
                     'default': default}
 
+        Chrome(self.env).add_jquery_ui(req)
+
         data.update({
             'datetime_hint': get_datetime_format_hint(req.lc_time),
         })
@@ -511,6 +513,8 @@ class VersionAdminPanel(TicketAdminPanel
                     'versions': model.Version.select(self.env),
                     'default': default}
 
+        Chrome(self.env).add_jquery_ui(req)
+
         data.update({
             'datetime_hint': get_datetime_format_hint(req.lc_time),
         })

Modified: incubator/bloodhound/trunk/trac/trac/ticket/api.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/api.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/api.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/api.py Tue Oct 16 20:06:09 2012
@@ -503,8 +503,10 @@ class TicketSystem(Component):
                 ranges = str(r)
                 if params:
                     params = '&' + params[1:]
-                return tag.a(label, 
-                             title=_("Tickets %(ranges)s", ranges=ranges),
+                label_wrap = label.replace(',', u',\u200b')
+                ranges_wrap = ranges.replace(',', u', ')
+                return tag.a(label_wrap,
+                             title=_("Tickets %(ranges)s", ranges=ranges_wrap),
                              href=formatter.href.query(id=ranges) + params)
         except ValueError:
             pass

Modified: incubator/bloodhound/trunk/trac/trac/ticket/batch.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/batch.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/batch.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/batch.py Tue Oct 16 20:06:09 2012
@@ -73,7 +73,7 @@ class BatchModifyModule(Component):
             name = field['name']
             if name not in ('id', 'resolution', 'status', 'owner', 'time',
                             'changetime', 'summary', 'reporter',
-                            'description') and field['type'] != 'text-area':
+                            'description') and field['type'] != 'textarea':
                 value = req.args.get('batchmod_value_' + name)
                 if value is not None:
                     values[name] = value
@@ -98,8 +98,7 @@ class BatchModifyModule(Component):
             {'name': _("add / remove"), 'value': "+-"},
             {'name': _("set to"), 'value': "="},
         ]
-        add_script_data(req, batch_modify=True,
-                             batch_list_modes=batch_list_modes,
+        add_script_data(req, batch_list_modes=batch_list_modes,
                              batch_list_properties=self.fields_as_list)
 
     def _get_action_controls(self, req, tickets):

Modified: incubator/bloodhound/trunk/trac/trac/ticket/default_workflow.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/default_workflow.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/default_workflow.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/default_workflow.py Tue Oct 16 20:06:09 2012
@@ -3,7 +3,7 @@
 # Copyright (C) 2006-2009 Edgewall Software
 # Copyright (C) 2006 Alec Thomas
 # Copyright (C) 2007 Eli Carter
-# Copyright (C) 2007 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2007 Christian Boos <cb...@edgewall.org>
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which

Modified: incubator/bloodhound/trunk/trac/trac/ticket/model.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/model.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/model.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/model.py Tue Oct 16 20:06:09 2012
@@ -3,7 +3,7 @@
 # Copyright (C) 2003-2009 Edgewall Software
 # Copyright (C) 2003-2006 Jonas Borgström <jo...@edgewall.com>
 # Copyright (C) 2005 Christopher Lenz <cm...@gmx.de>
-# Copyright (C) 2006 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2006 Christian Boos <cb...@edgewall.org>
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -59,8 +59,8 @@ class Ticket(object):
 
     def __init__(self, env, tkt_id=None, db=None, version=None):
         """
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         self.env = env
         if tkt_id is not None:
@@ -163,14 +163,16 @@ class Ticket(object):
         """
         try:
             value = self.values[name]
-            if value is not empty:
-                return value
-            field = [field for field in self.fields if field['name'] == name]
-            if field:
-                return field[0].get('value', '')
+            return value if value is not empty else self.get_default(name)
         except KeyError:
             pass
 
+    def get_default(self, name):
+        """Return the default value of a field."""
+        field = [field for field in self.fields if field['name'] == name]
+        if field:
+            return field[0].get('value', '')
+
     def populate(self, values):
         """Populate the ticket with 'suitable' values from a dictionary"""
         field_names = [f['name'] for f in self.fields]
@@ -186,8 +188,8 @@ class Ticket(object):
     def insert(self, when=None, db=None):
         """Add ticket to database.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert not self.exists, 'Cannot insert an existing ticket'
 
@@ -261,9 +263,9 @@ class Ticket(object):
         the database.  Returns False if there were no changes to save, True
         otherwise.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
-        :since 0.13: the `cnum` parameter is deprecated, and threading should
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
+        :since 1.0: the `cnum` parameter is deprecated, and threading should
         be controlled with the `replyto` argument
         """
         assert self.exists, "Cannot update a new ticket"
@@ -271,7 +273,8 @@ class Ticket(object):
         if 'cc' in self.values:
             self['cc'] = _fixup_cc_list(self.values['cc'])
 
-        if not self._old and not comment:
+        props_unchanged = all(self.values.get(k) == v for k, v in self._old.iteritems())
+        if (not comment or not comment.strip()) and props_unchanged:
             return False # Not modified
 
         if when is None:
@@ -368,8 +371,8 @@ class Ticket(object):
         the `permanent` flag is used to distinguish collateral changes
         that are not yet immutable (like attachments, currently).
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         sid = str(self.id)
         when_ts = to_utimestamp(when)
@@ -411,8 +414,8 @@ class Ticket(object):
     def delete(self, db=None):
         """Delete the ticket.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         with self.env.db_transaction as db:
             Attachment.delete_all(self.env, 'ticket', self.id, db)
@@ -426,8 +429,8 @@ class Ticket(object):
     def get_change(self, cnum=None, cdate=None, db=None):
         """Return a ticket change by its number or date.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         if cdate is None:
             row = self._find_change(cnum)
@@ -565,8 +568,8 @@ class Ticket(object):
         """Retrieve the edit history of a comment identified by its number or
         date.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         if cdate is None:
             row = self._find_change(cnum)
@@ -586,7 +589,7 @@ class Ticket(object):
                     break
             if author0 is None:
                 for author0, last_comment in db("""
-                        SELECT author, new FROM ticket_change 
+                        SELECT author, newvalue FROM ticket_change 
                         WHERE ticket=%%s AND time=%%s AND NOT field %s LIMIT 1
                         """ % db.like(),
                         (self.id, ts0, db.like_escape('_') + '%')):
@@ -691,8 +694,8 @@ class AbstractEnum(object):
     def delete(self, db=None):
         """Delete the enum value.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert self.exists, "Cannot delete non-existent %s" % self.type
 
@@ -716,8 +719,8 @@ class AbstractEnum(object):
     def insert(self, db=None):
         """Add a new enum value.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert not self.exists, "Cannot insert existing %s" % self.type
         self.name = simplify_whitespace(self.name)
@@ -741,8 +744,8 @@ class AbstractEnum(object):
     def update(self, db=None):
         """Update the enum value.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert self.exists, "Cannot update non-existent %s" % self.type
         self.name = simplify_whitespace(self.name)
@@ -766,8 +769,8 @@ class AbstractEnum(object):
     @classmethod
     def select(cls, env, db=None):
         """
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         with env.db_query as db:
             for name, value in db("""
@@ -812,8 +815,8 @@ class Severity(AbstractEnum):
 class Component(object):
     def __init__(self, env, name=None, db=None):
         """
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         self.env = env
         self.name = self._old_name = self.owner = self.description = None
@@ -834,8 +837,8 @@ class Component(object):
     def delete(self, db=None):
         """Delete the component.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert self.exists, "Cannot delete non-existent component"
 
@@ -848,8 +851,8 @@ class Component(object):
     def insert(self, db=None):
         """Insert a new component.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert not self.exists, "Cannot insert existing component"
         self.name = simplify_whitespace(self.name)
@@ -867,8 +870,8 @@ class Component(object):
     def update(self, db=None):
         """Update the component.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert self.exists, "Cannot update non-existent component"
         self.name = simplify_whitespace(self.name)
@@ -891,8 +894,8 @@ class Component(object):
     @classmethod
     def select(cls, env, db=None):
         """
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         for name, owner, description in env.db_query(
                 "SELECT name, owner, description FROM component ORDER BY name"):
@@ -950,8 +953,8 @@ class Milestone(object):
     def delete(self, retarget_to=None, author=None, db=None):
         """Delete the milestone.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         with self.env.db_transaction as db:
             self.env.log.info("Deleting milestone %s", self.name)
@@ -976,8 +979,8 @@ class Milestone(object):
     def insert(self, db=None):
         """Insert a new milestone.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         self.name = simplify_whitespace(self.name)
         if not self.name:
@@ -998,8 +1001,8 @@ class Milestone(object):
     def update(self, db=None):
         """Update the milestone.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         self.name = simplify_whitespace(self.name)
         if not self.name:
@@ -1036,8 +1039,8 @@ class Milestone(object):
     @classmethod
     def select(cls, env, include_completed=True, db=None):
         """
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         sql = "SELECT name, due, completed, description FROM milestone "
         if not include_completed:
@@ -1092,8 +1095,8 @@ class Version(object):
     def delete(self, db=None):
         """Delete the version.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert self.exists, "Cannot delete non-existent version"
 
@@ -1106,8 +1109,8 @@ class Version(object):
     def insert(self, db=None):
         """Insert a new version.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert not self.exists, "Cannot insert existing version"
         self.name = simplify_whitespace(self.name)
@@ -1124,8 +1127,8 @@ class Version(object):
     def update(self, db=None):
         """Update the version.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         assert self.exists, "Cannot update non-existent version"
         self.name = simplify_whitespace(self.name)
@@ -1148,8 +1151,8 @@ class Version(object):
     @classmethod
     def select(cls, env, db=None):
         """
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         versions = []
         for name, time, description in env.db_query("""

Modified: incubator/bloodhound/trunk/trac/trac/ticket/notification.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/notification.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/notification.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/notification.py Tue Oct 16 20:06:09 2012
@@ -61,7 +61,7 @@ class TicketNotificationSystem(Component
         """Like ticket_subject_template but for batch modifications.
 
         By default, the template is `$prefix Batch modify: $tickets_descr`.
-        ''(since 0.13)''""")
+        ''(since 1.0)''""")
 
     ambiguous_char_width = Option('notification', 'ambiguous_char_width',
                                   'single',

Modified: incubator/bloodhound/trunk/trac/trac/ticket/query.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/query.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/query.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/query.py Tue Oct 16 20:06:09 2012
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2004-2009 Edgewall Software
 # Copyright (C) 2004-2005 Christopher Lenz <cm...@gmx.de>
-# Copyright (C) 2005-2007 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2005-2007 Christian Boos <cb...@edgewall.org>
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -273,8 +273,8 @@ class Query(object):
               tzinfo=None, locale=None):
         """Get the number of matching tickets for the present query.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         sql, args = self.get_sql(req, cached_ids, authname, tzinfo, locale)
         return self._count(sql, args)
@@ -290,8 +290,8 @@ class Query(object):
                 tzinfo=None, href=None, locale=None):
         """Retrieve the list of matching tickets.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         if req is not None:
             href = req.href
@@ -1129,6 +1129,7 @@ class QueryModule(Component):
         add_script_data(req, properties=properties, modes=data['modes'])
 
         add_stylesheet(req, 'common/css/report.css')
+        Chrome(self.env).add_jquery_ui(req)
         add_script(req, 'common/js/query.js')
 
         return 'query.html', data, None
@@ -1421,7 +1422,8 @@ class TicketQueryMacro(WikiMacroBase):
         if format == 'compact':
             if query.group:
                 groups = [(v, ' ', 
-                           tag.a('#%s' % ','.join([str(t['id']) for t in g]),
+                           tag.a('#%s' % u',\u200b'.join(str(t['id'])
+                                                         for t in g),
                                  href=href, class_='query', title=title))
                           for v, g, href, title in ticket_groups()]
                 return tag(groups[0], [(', ', g) for g in groups[1:]])

Modified: incubator/bloodhound/trunk/trac/trac/ticket/report.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/report.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/report.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/report.py Tue Oct 16 20:06:09 2012
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2003-2009 Edgewall Software
 # Copyright (C) 2003-2004 Jonas Borgström <jo...@edgewall.com>
-# Copyright (C) 2006 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2006 Christian Boos <cb...@edgewall.org>
 # Copyright (C) 2006 Matthew Good <tr...@matt-good.net>
 # All rights reserved.
 #
@@ -36,13 +36,18 @@ from trac.util.presentation import Pagin
 from trac.util.text import exception_to_unicode, to_unicode, quote_query_string
 from trac.util.translation import _, tag_
 from trac.web.api import IRequestHandler, RequestDone
-from trac.web.chrome import (INavigationContributor, Chrome, 
+from trac.web.chrome import (INavigationContributor, Chrome,
                              add_ctxtnav, add_link, add_notice, add_script,
                              add_stylesheet, add_warning, auth_link,
                              web_context)
 from trac.wiki import IWikiSyntaxProvider, WikiParser
 
 
+
+SORT_COLUMN = '@SORT_COLUMN@'
+LIMIT_OFFSET = '@LIMIT_OFFSET@'
+
+
 def cell_value(v):
     """Normalize a cell value for display.
     >>> (cell_value(None), cell_value(0), cell_value(1), cell_value('v'))
@@ -51,6 +56,58 @@ def cell_value(v):
     return '0' if v is 0 else unicode(v) if v else ''
 
 
+_sql_re = re.compile(r'''
+      --.*$                        # single line "--" comment
+    | /\*([^*/]|\*[^/]|/[^*])*\*/  # C style comment
+    | '(\\.|[^'\\])*'              # literal string
+    | \([^()]+\)                   # parenthesis group
+''', re.MULTILINE | re.VERBOSE)
+
+def _expand_with_space(m):
+    return ' ' * len(m.group(0))
+
+def sql_skeleton(sql):
+    """Strip an SQL query to leave only its toplevel structure.
+
+    This is probably not 100% robust but should be enough for most
+    needs.
+
+    >>> re.sub('\s+', lambda m: '<%d>' % len(m.group(0)), sql_skeleton(''' \\n\
+        SELECT a FROM (SELECT x FROM z ORDER BY COALESCE(u, ')/*(')) ORDER \\n\
+          /* SELECT a FROM (SELECT x /* FROM z                             \\n\
+                        ORDER BY */ COALESCE(u, '\)X(')) ORDER */          \\n\
+          BY c, (SELECT s FROM f WHERE v in ('ORDER BY', '(\\')')          \\n\
+                 ORDER BY (1), '') -- LIMIT                                \\n\
+         '''))
+    '<10>SELECT<1>a<1>FROM<48>ORDER<164>BY<1>c,<144>'
+    """
+    old = None
+    while sql != old:
+        old = sql
+        sql = _sql_re.sub(_expand_with_space, old)
+    return old
+
+_order_by_re = re.compile(r'ORDER\s+BY', re.MULTILINE)
+
+def split_sql(sql, clause_re, skel=None):
+    """Split an SQL query according to a toplevel clause regexp.
+
+    We assume there's only one such clause present in the outer query.
+
+    >>> split_sql('''SELECT a FROM x  ORDER \
+            BY u, v''', _order_by_re)
+    ('SELECT a FROM x  ', ' u, v')
+    """
+    if skel is None:
+        skel = sql_skeleton(sql)
+    blocks = clause_re.split(skel.upper())
+    if len(blocks) == 2:
+        return sql[:len(blocks[0])], sql[-len(blocks[1]):] # (before, after)
+    else:
+        return sql, '' # no single clause separator
+
+
+
 class ReportModule(Component):
 
     implements(INavigationContributor, IPermissionRequestor, IRequestHandler,
@@ -63,7 +120,7 @@ class ReportModule(Component):
     items_per_page_rss = IntOption('report', 'items_per_page_rss', 0,
         """Number of tickets displayed in the rss feeds for reports
         (''since 0.11'')""")
-    
+
     # INavigationContributor methods
 
     def get_active_navigation_item(self, req):
@@ -74,12 +131,12 @@ class ReportModule(Component):
             yield ('mainnav', 'tickets', tag.a(_('View Tickets'),
                                                href=req.href.report()))
 
-    # IPermissionRequestor methods  
+    # IPermissionRequestor methods
 
-    def get_permission_actions(self):  
-        actions = ['REPORT_CREATE', 'REPORT_DELETE', 'REPORT_MODIFY',  
-                   'REPORT_SQL_VIEW', 'REPORT_VIEW']  
-        return actions + [('REPORT_ADMIN', actions)]  
+    def get_permission_actions(self):
+        actions = ['REPORT_CREATE', 'REPORT_DELETE', 'REPORT_MODIFY',
+                   'REPORT_SQL_VIEW', 'REPORT_VIEW']
+        return actions + [('REPORT_ADMIN', actions)]
 
     # IRequestHandler methods
 
@@ -132,8 +189,8 @@ class ReportModule(Component):
         else:
             add_ctxtnav(req, _('Available Reports'))
 
-        # Kludge: only show link to custom query if the query module is actually
-        # enabled
+        # Kludge: only show link to custom query if the query module
+        # is actually enabled
         from trac.ticket.query import QueryModule
         if 'TICKET_VIEW' in req.perm and \
                 self.env.is_component_enabled(QueryModule):
@@ -244,12 +301,12 @@ class ReportModule(Component):
         sort = req.args.get('sort', 'report')
         asc = bool(int(req.args.get('asc', 1)))
         format = req.args.get('format')
-        
+
         rows = self.env.db_query("""
                 SELECT id, title, description FROM report ORDER BY %s %s
                 """ % ('title' if sort == 'title' else 'id',
                        '' if asc else 'DESC'))
-            
+
         if format == 'rss':
             data = {'rows': rows}
             return 'report_list.rss', data, 'application/rss+xml'
@@ -273,8 +330,8 @@ class ReportModule(Component):
                  _('Comma-delimited Text'), 'text/plain')
         add_link(req, 'alternate', report_href(format='tab'),
                  _('Tab-delimited Text'), 'text/plain')
-        
-        reports = [(id, title, description, 
+
+        reports = [(id, title, description,
                     'REPORT_MODIFY' in req.perm('report', id),
                     'REPORT_DELETE' in req.perm('report', id))
                    for id, title, description in rows]
@@ -351,7 +408,7 @@ class ReportModule(Component):
         asc = bool(int(asc)) # string '0' or '1' to int/boolean
 
         def report_href(**kwargs):
-            """Generate links to this report preserving user variables, 
+            """Generate links to this report preserving user variables,
             and sorting and paging variables.
             """
             params = args.copy()
@@ -361,7 +418,7 @@ class ReportModule(Component):
             if max:
                 params['max'] = max
             params.update(kwargs)
-            params['asc'] = '1' if params.get('asc', asc) else '0'            
+            params['asc'] = '1' if params.get('asc', asc) else '0'
             return req.href.report(id, params)
 
         data = {'action': 'view',
@@ -370,24 +427,31 @@ class ReportModule(Component):
                 'title': title, 'description': description,
                 'max': limit, 'args': args, 'show_args_form': False,
                 'message': None, 'paginator': None,
-                'report_href': report_href, 
+                'report_href': report_href,
                 }
 
+        res = None
         with self.env.db_query as db:
-            try:
-                cols, results, num_items, missing_args = \
-                    self.execute_paginated_report(req, db, id, sql, args, limit,
-                                                  offset)
-                results = [list(row) for row in results]
-                numrows = len(results)
+            res = self.execute_paginated_report(req, db, id, sql, args, limit,
+                                                offset)
 
-            except Exception, e:
-                data['message'] = tag_('Report execution failed: %(error)s',
-                        error=tag.pre(exception_to_unicode(e, traceback=True)))
-                return 'report_view.html', data, None
+        if len(res) == 2:
+             e, sql = res
+             data['message'] = \
+                 tag_("Report execution failed: %(error)s %(sql)s",
+                      error=tag.pre(exception_to_unicode(e)),
+                      sql=tag(tag.hr(),
+                              tag.pre(sql, style="white-space: pre")))
+             return 'report_view.html', data, None
+
+        cols, results, num_items, missing_args, limit_offset = res
+        need_paginator = limit > 0 and limit_offset
+        need_reorder = limit_offset is None
+        results = [list(row) for row in results]
+        numrows = len(results)
 
         paginator = None
-        if limit > 0:
+        if need_paginator:
             paginator = Paginator(results, page - 1, limit, num_items)
             data['paginator'] = paginator
             if paginator.has_next_page:
@@ -430,11 +494,11 @@ class ReportModule(Component):
 
             if col == sort_col:
                 header['asc'] = asc
-                if not paginator:
+                if not paginator and need_reorder:
                     # this dict will have enum values for sorting
                     # and will be used in sortkey(), if non-empty:
                     sort_values = {}
-                    if sort_col in ('status', 'resolution', 'priority', 
+                    if sort_col in ('status', 'resolution', 'priority',
                                     'severity'):
                         # must fetch sort values for that columns
                         # instead of comparing them as strings
@@ -540,8 +604,7 @@ class ReportModule(Component):
 
         data.update({'header_groups': header_groups,
                      'row_groups': row_groups,
-                     'numrows': numrows,
-                     'sorting_enabled': '__group__' not in cols})
+                     'numrows': numrows})
 
         if format == 'rss':
             data['email_map'] = chrome.get_email_map()
@@ -567,12 +630,12 @@ class ReportModule(Component):
             add_link(req, 'alternate', report_href(format='tab', page=p),
                      _('Tab-delimited Text'), 'text/plain')
             if 'REPORT_SQL_VIEW' in req.perm:
-                add_link(req, 'alternate', 
+                add_link(req, 'alternate',
                          req.href.report(id=id, format='sql'),
                          _('SQL Query'), 'text/plain')
 
             # reuse the session vars of the query module so that
-            # the query navigation links on the ticket can be used to 
+            # the query navigation links on the ticket can be used to
             # navigate report results as well
             try:
                 req.session['query_tickets'] = \
@@ -581,7 +644,7 @@ class ReportModule(Component):
                 req.session['query_href'] = \
                     req.session['query_href'] = report_href()
                 # Kludge: we have to clear the other query session
-                # variables, but only if the above succeeded 
+                # variables, but only if the above succeeded
                 for var in ('query_constraints', 'query_time'):
                     if var in req.session:
                         del req.session[var]
@@ -598,53 +661,110 @@ class ReportModule(Component):
 
     def execute_report(self, req, db, id, sql, args):
         """Execute given sql report (0.10 backward compatibility method)
-        
+
         :see: ``execute_paginated_report``
         """
-        return self.execute_paginated_report(req, db, id, sql, args)[:2]
+        res = self.execute_paginated_report(req, db, id, sql, args)
+        if len(res) == 2:
+            raise res[0]
+        return res[:5]
 
-    def execute_paginated_report(self, req, db, id, sql, args, 
+    def execute_paginated_report(self, req, db, id, sql, args,
                                  limit=0, offset=0):
         sql, args, missing_args = self.sql_sub_vars(sql, args, db)
         if not sql:
             raise TracError(_("Report {%(num)s} has no SQL query.", num=id))
+        self.log.debug('Report {%d} with SQL "%s"', id, sql)
+        self.log.debug('Request args: %r', req.args)
 
         cursor = db.cursor()
 
         num_items = 0
-        if id != -1 and limit > 0:
-            cursor.execute("SELECT COUNT(*) FROM (%s) AS tab" % sql, args)
+        order_by = []
+        limit_offset = None
+        base_sql = sql.replace(SORT_COLUMN, '1').replace(LIMIT_OFFSET, '')
+        if id == -1 or limit == 0:
+            sql = base_sql
+        else:
+            # The number of tickets is obtained
+            count_sql = 'SELECT COUNT(*) FROM (\n%s\n) AS tab' % base_sql
+            self.log.debug("Report {%d} SQL (count): %s", id, count_sql)
+            try:
+                cursor.execute(count_sql, args)
+            except Exception, e:
+                return e, count_sql
             num_items = cursor.fetchone()[0]
-    
-            # get the column names
-            cursor.execute("SELECT * FROM (%s) AS tab LIMIT 1" % sql, args)
+
+            # The column names are obtained
+            colnames_sql = 'SELECT * FROM (\n%s\n) AS tab LIMIT 1' % base_sql
+            self.log.debug("Report {%d} SQL (col names): %s", id, colnames_sql)
+            try:
+                cursor.execute(colnames_sql, args)
+            except Exception, e:
+                return e, colnames_sql
             cols = get_column_names(cursor)
 
+            # The ORDER BY columns are inserted
             sort_col = req.args.get('sort', '')
+            asc = req.args.get('asc', '1')
+            self.log.debug("%r %s (%s)", cols, sort_col, asc and '^' or 'v')
             order_cols = []
+            if sort_col and sort_col not in cols:
+                raise TracError(_('Query parameter "sort=%(sort_col)s" '
+                                  ' is invalid', sort_col=sort_col))
+            skel = None
             if '__group__' in cols:
-                sort_col = '' # sorting is disabled (#15030)
+                order_cols.append('__group__')
             if sort_col:
-                if sort_col in cols:
-                    order_cols.append(sort_col)
-                else:
-                    raise TracError(_('Query parameter "sort=%(sort_col)s" '
-                                      ' is invalid', sort_col=sort_col))
-
-            # get the (partial) report results
-            order_by = ''
-            if order_cols:
-                asc = req.args.get('asc', '1')
-                order_by = " ORDER BY %s %s" % (
-                       ', '.join(db.quote(col) for col in order_cols),
-                        'ASC' if asc == '1' else 'DESC')
-            sql = "SELECT * FROM (%s) AS tab %s LIMIT %s OFFSET %s" % \
-                    (sql, order_by, str(limit), str(offset))
-            self.log.debug("Query SQL: " + sql)
-        cursor.execute(sql, args)
+                sort_col = '%s %s' % (db.quote(sort_col),
+                                      asc == '1' and 'ASC' or 'DESC')
+
+            if SORT_COLUMN in sql:
+                # Method 1: insert sort_col at specified position
+                sql = sql.replace(SORT_COLUMN, sort_col or '1')
+            elif sort_col:
+                # Method 2: automagically insert sort_col (and __group__
+                # before it, if __group__ was specified) as first criterions
+                if '__group__' in cols:
+                    order_by.append('__group__ ASC')
+                order_by.append(sort_col)
+                # is there already an ORDER BY in the original sql?
+                skel = sql_skeleton(sql)
+                before, after = split_sql(sql, _order_by_re, skel)
+                if after: # there were some other criterions, keep them
+                    order_by.append(after)
+                sql = ' '.join([before, 'ORDER BY', ', '.join(order_by)])
+
+            # Add LIMIT/OFFSET if pagination needed
+            limit_offset = ''
+            if num_items > limit:
+                limit_offset = ' '.join(['LIMIT', str(limit),
+                                         'OFFSET', str(offset)])
+            if LIMIT_OFFSET in sql:
+                # Method 1: insert LIMIT/OFFSET at specified position
+                sql = sql.replace(LIMIT_OFFSET, limit_offset)
+            else:
+                # Method 2: limit/offset is added unless already present
+                skel = skel or sql_skeleton(sql)
+                if 'LIMIT' not in skel.upper():
+                    sql = ' '.join([sql, limit_offset])
+            self.log.debug("Report {%d} SQL (order + limit): %s", id, sql)
+        try:
+            cursor.execute(sql, args)
+        except Exception, e:
+            if order_by or limit_offset:
+                add_notice(req, _("Hint: if the report failed due to automatic"
+                                  " modification of the ORDER BY clause or the"
+                                  " addition of LIMIT/OFFSET, please look up"
+                                  " %(sort_column)s and %(limit_offset)s in"
+                                  " TracReports to see how to gain complete"
+                                  " control over report rewriting.",
+                                  sort_column=SORT_COLUMN,
+                                  limit_offset=LIMIT_OFFSET))
+            return e, sql
         rows = cursor.fetchall() or []
         cols = get_column_names(cursor)
-        return cols, rows, num_items, missing_args
+        return cols, rows, num_items, missing_args, limit_offset
 
     def get_var_args(self, req):
         # reuse somehow for #9574 (wiki vars)
@@ -663,8 +783,8 @@ class ReportModule(Component):
     def sql_sub_vars(self, sql, args, db=None):
         """Extract $XYZ-style variables from the `sql` query.
 
-        :since 0.13: the `db` parameter is no longer needed and will be removed
-        in version 0.14
+        :since 1.0: the `db` parameter is no longer needed and will be removed
+        in version 1.1.1
         """
         names = set()
         values = []
@@ -706,7 +826,7 @@ class ReportModule(Component):
                 sql_io.write(repl_literal(expr))
             else:
                 sql_io.write(var_re.sub(repl, expr))
-        
+
         # Remove arguments that don't appear in the SQL query
         for name in set(args) - names:
             del args[name]
@@ -748,7 +868,7 @@ class ReportModule(Component):
         req.send_header('Content-Length', len(data))
         if filename:
             req.send_header('Content-Disposition',
-                            content_disposition(filename=filename))
+                            content_disposition('attachment', filename))
         req.end_headers()
         req.write(data)
         raise RequestDone
@@ -769,18 +889,20 @@ class ReportModule(Component):
         req.send_header('Content-Length', len(data))
         if id:
             req.send_header('Content-Disposition',
-                            content_disposition(filename='report_%s.sql' % id))
+                            content_disposition('attachment',
+                                                'report_%s.sql' % id))
         req.end_headers()
         req.write(data)
         raise RequestDone
-        
+
     # IWikiSyntaxProvider methods
-    
+
     def get_link_resolvers(self):
         yield ('report', self._format_link)
 
     def get_wiki_syntax(self):
-        yield (r"!?\{(?P<it_report>%s\s*)\d+\}" % WikiParser.INTERTRAC_SCHEME,
+        yield (r"!?\{(?P<it_report>%s\s*)[0-9]+\}" % \
+                                                WikiParser.INTERTRAC_SCHEME,
                lambda x, y, z: self._format_link(x, 'report', y[1:-1], y, z))
 
     def _format_link(self, formatter, ns, target, label, fullmatch=None):

Modified: incubator/bloodhound/trunk/trac/trac/ticket/roadmap.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/roadmap.py?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/roadmap.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/roadmap.py Tue Oct 16 20:06:09 2012
@@ -2,7 +2,7 @@
 #
 # Copyright (C) 2004-2009 Edgewall Software
 # Copyright (C) 2004-2005 Christopher Lenz <cm...@gmx.de>
-# Copyright (C) 2006-2007 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2006-2007 Christian Boos <cb...@edgewall.org>
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -295,9 +295,9 @@ def get_ticket_stats(provider, tickets):
 def get_tickets_for_milestone(env, db=None, milestone=None, field='component'):
     """Retrieve all tickets associated with the given `milestone`.
 
-    .. versionchanged :: 0.13
+    .. versionchanged :: 1.0
        the `db` parameter is no longer needed and will be removed in
-       version 0.14
+       version 1.1.1
     """
     with env.db_query as db:
         fields = TicketSystem(env).get_ticket_fields()

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/batch_modify.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/batch_modify.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/batch_modify.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/batch_modify.html Tue Oct 16 20:06:09 2012
@@ -23,7 +23,9 @@
                 py:with="field_names = sorted(fields.iterkeys(), key=lambda name: fields[name].label.lower())">
           <option></option>
           <option py:for="field_name in field_names" py:with="field = fields[field_name]"
-                  py:if="field_name not in ('id', 'resolution', 'status', 'owner', 'time', 'changetime', 'summary', 'reporter', 'description') and fields[field_name].type != 'text-area'"
+                  py:if="field_name not in ('id', 'resolution', 'status', 'owner', 'time', 'changetime', 'summary',
+                                            'reporter', 'description')
+                         and fields[field_name].type != 'textarea'"
                   value="$field_name">${field.label}</option>
         </select>
       </td>
@@ -45,16 +47,16 @@
     </tr>
   </table>
   
+  <div id="batchmod_help" i18n:msg="">
+    <strong>Note:</strong> See <a href="${href.wiki('TracBatchModify')}">TracBatchModify</a> for help on using batch modify.
+  </div>
+
   <div>
     <input type="hidden" name="selected_tickets" value=""/>
     <input type="hidden" name="query_href" value="${query_href}"/>
     <input type="submit" id="batchmod_submit" name="batchmod_submit" value="${_('Change tickets')}" />
   </div>
   
-  <div id="batchmod_help" i18n:msg="">
-    <strong>Note:</strong> See <a href="${href.wiki('TracBatchModify')}">TracBatchModify</a> for help on using batch modify.
-  </div>
-
 </fieldset>
 
 </form>

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_delete.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_delete.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_delete.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_delete.html Tue Oct 16 20:06:09 2012
@@ -7,7 +7,7 @@
       xmlns:xi="http://www.w3.org/2001/XInclude">
   <xi:include href="layout.html" />
   <head>
-    <title>Delete Milestone ${milestone.name}</title>
+    <title i18n:msg="name">Delete Milestone ${milestone.name}</title>
     <link rel="stylesheet" type="text/css"
           href="${chrome.htdocs_location}css/roadmap.css" />
     <script type="text/javascript">
@@ -19,7 +19,7 @@
 
   <body>
     <div id="content" class="milestone">
-      <h1>Delete Milestone ${milestone.name}</h1>
+      <h1 i18n:msg="name">Delete Milestone ${milestone.name}</h1>
 
     <form id="edit" action="" method="post">
       <div>

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_edit.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_edit.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_edit.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_edit.html Tue Oct 16 20:06:09 2012
@@ -8,7 +8,7 @@
   <xi:include href="layout.html" />
   <head>
     <py:choose test="milestone.exists">
-      <title py:when="True">Edit Milestone ${milestone.name}</title>
+      <title py:when="True" i18n:msg="name">Edit Milestone ${milestone.name}</title>
       <title py:otherwise="">New Milestone</title>
     </py:choose>
     <link rel="stylesheet" type="text/css"
@@ -42,7 +42,7 @@
   <body>
     <div id="content" class="milestone">
       <py:choose test="milestone.exists">
-        <h1 py:when="True">Edit Milestone ${milestone.name}</h1>
+        <h1 py:when="True" i18n:msg="name">Edit Milestone ${milestone.name}</h1>
         <h1 py:otherwise="">New Milestone</h1>
       </py:choose>
 
@@ -95,7 +95,7 @@
           </div>
         </fieldset>
         <div class="field">
-          <fieldset class="iefix">
+          <fieldset>
             <label for="description" i18n:msg="">Description (you may use <a tabindex="42"
                    href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here):</label>
             <p><textarea id="description" name="description" class="wikitext trac-resizable" rows="10" cols="78">

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_view.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_view.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_view.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/milestone_view.html Tue Oct 16 20:06:09 2012
@@ -7,7 +7,7 @@
       xmlns:xi="http://www.w3.org/2001/XInclude">
   <xi:include href="layout.html" />
   <head>
-    <title>Milestone ${milestone.name}</title>
+    <title i18n:msg="name">Milestone ${milestone.name}</title>
     <link py:if="'MILESTONE_MODIFY' in perm(milestone.resource)" rel="alternate" type="application/x-wiki"
           title="Edit this milestone" href="${href.milestone(milestone.name, action='edit')}" />
     <script type="text/javascript">
@@ -20,7 +20,7 @@
 
   <body>
     <div id="content" class="milestone">
-      <h1>Milestone ${milestone.name}</h1>
+      <h1 i18n:msg="name">Milestone ${milestone.name}</h1>
       <div class="info trac-progress">
         <py:choose>
           <p py:when="milestone.completed" class="date">
@@ -65,7 +65,7 @@
         </fieldset>
       </form>
 
-      <div class="description" xml:space="preserve">
+      <div py:if="milestone.description" class="description trac-content" xml:space="preserve">
         ${wiki_to_html(context, milestone.description)}
       </div>
 

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/query.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/query.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/query.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/query.html Tue Oct 16 20:06:09 2012
@@ -9,23 +9,25 @@
   <head>
     <title>$title</title>
     <script type="text/javascript" src="${chrome.htdocs_location}js/folding.js"></script>
-    <script type="text/javascript">/*<![CDATA[*/
+    <script type="text/javascript">
       jQuery(document).ready(function($) {
         initializeFilters();
-        if(batch_modify) {
-          initializeBatch();
-        }
+      <py:if test="batch_modify">
+        initializeBatch();
+      </py:if>
         $("#group").change(function() {
           $("#groupdesc").enable(this.selectedIndex != 0)
         }).change();
         $("fieldset legend.foldable").enableFolding(false);
+      /*<![CDATA[*/
         /* Hide the filters for saved queries. */
         if (window.location.href.search(/[?&]report=[0-9]+/) != -1)
           $("#filters").toggleClass("collapsed");
+      /*]]>*/
         /* Hide the columns by default. */
         $("#columns").toggleClass("collapsed");
       });
-    /*]]>*/</script>
+    </script>
   </head>
 
   <body>
@@ -118,7 +120,7 @@
                         <py:when test="field.type in ('text', 'textarea', 'id')">
                           <input type="text" name="${n_field_name}" value="$constraint_value" size="42" />
                         </py:when>
-                        
+
                         <py:when test="field.type == 'time'"
                                  py:with="(start, end) = '..' in constraint_value and constraint_value.split('..', 1)
                                                          or (constraint_value, '')">
@@ -223,14 +225,14 @@
       <xi:include href="query_results.html" />
       <xi:include py:if="batch_modify" href="batch_modify.html" />
 
-      <div class="buttons"
+      <div id="trac-report-buttons" class="buttons"
            py:with="edit = report_resource and 'REPORT_MODIFY' in perm(report_resource);
                     new = 'REPORT_CREATE' in perm;
                     delete = report_resource and 'REPORT_DELETE' in perm(report_resource)">
         <form py:if="edit" method="get" action="${url_of(report_resource)}">
           <div>
             <input type="hidden" name="action" value="edit" />
-            <input type="submit" 
+            <input type="submit"
                    title="${_('Edit report {%(id)s} corresponding to this query', id=report_resource.id)}"
                    value="${_('Edit query')}" />
           </div>

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/query_results.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/query_results.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/query_results.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/query_results.html Tue Oct 16 20:06:09 2012
@@ -42,14 +42,15 @@
     </tr>
   </py:def>
   ${group_heading(*groups[0]) if groups else None}
-  <table class="listing tickets">
+  <table class="listing tickets"
+         py:with="num_cols = len(headers) + int(batch_modify or 0)">
     <thead py:strip="group_index">
       ${column_headers()}
     </thead>
     <py:for each="group_index, (groupname, results) in enumerate(groups)">
       <tbody py:if="group_index">
         <tr py:if="groupname is not None" class="trac-group">
-          <th colspan="${len(headers)}">
+          <th colspan="$num_cols">
             ${group_heading(groupname, results)}
           </th>
         </tr>
@@ -57,7 +58,7 @@
       </tbody>
       <tbody>
         <tr py:if="not results" class="even">
-          <td colspan="${len(headers)}">
+          <td colspan="$num_cols">
             No tickets found
           </td>
         </tr>
@@ -90,13 +91,13 @@
               <py:with vars="result_rows = [t for t in row if result[t]]">
                 <py:choose>
                   <tr py:when="ticket_context.resource in context" class="fullrow">
-                    <td colspan="${len(headers)}">
+                    <td colspan="$num_cols">
                       <p class="meta"><em>(this ticket)</em></p>
                     </td>
                   </tr>
                   <tr py:otherwise="" py:for="r in result_rows" class="fullrow">
                     <th class="meta">${fields.get(r, {'label': r or 'none'}).label}</th>
-                    <td colspan="${len(headers)-1}" xml:space="preserve">
+                    <td colspan="${num_cols - 1}" xml:space="preserve">
                       ${wiki_to_html(ticket_context, result[r])}
                     </td>
                   </tr>
@@ -106,7 +107,7 @@
           </py:with>
         </py:for>
         <tr py:if="group_index == len(groups)-1 and last_group_is_partial">
-          <td colspan="${len(headers)}">
+          <td colspan="$num_cols">
             <i>(more results for this group on next page)</i>
           </td>
         </tr>

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/report_list.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/report_list.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/report_list.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/report_list.html Tue Oct 16 20:06:09 2012
@@ -40,11 +40,12 @@
         <div py:if="saved_query_href">
           <form method="get" action="${href.report()}">
             <div class="inlinebuttons">
+
               <input type="hidden" name="action" value="clear" />
-              <input type="submit" class="inlinebutton" title="Forget last query" value="${_('Clear')}" />
+              <input type="submit" value="${captioned_button('x', _('Clear'))}" title="Forget last query" class="trac-delete" />
             </div>
           </form>
-          <h2><a href="$saved_query_href"><em>Return to Last Query</em></a></h2>
+          <h3><a href="$saved_query_href"><em>Return to Last Query</em></a></h3>
           <span class="foldable" />
           <div class="description">
             <p>Continue browsing through the current list of results,
@@ -53,7 +54,7 @@
         </div>
 
         <div py:if="query_href">
-          <h2><a href="$query_href"><em>Custom Query</em></a></h2>
+          <h3><a href="$query_href"><em>Custom Query</em></a></h3>
           <span class="foldable" />
           <div class="description">
             <p>Compose a new ticket query by selecting filters and columns to display.</p>
@@ -78,16 +79,21 @@
             <form py:if="can_delete" method="get" action="${href.report(id)}">
               <div class="inlinebuttons">
                 <input type="hidden" name="action" value="delete" />
-                <input type="submit" class="inlinebutton" title="Delete report" value="${_('Delete')}" />
+                <input type="submit" value="${captioned_button('–', _('Delete'))}"
+                     title="Delete report" class="trac-delete" />
               </div>
             </form>
             <form py:if="can_edit" method="get" action="${href.report(id)}">
               <div class="inlinebuttons">
                 <input type="hidden" name="action" value="edit" />
-                <input type="submit" class="inlinebutton" title="Edit report" value="${_('Edit')}" />
+                <input type="submit" value="${captioned_button('✎', _('Edit'))}"
+                     title="Edit report" />
               </div>
             </form>
-            <h2><a title="View report" href="${href.report(id)}">{$id} <em>$title</em></a></h2>
+            <h3><a title="View report" href="${href.report(id)}" py:choose="sort">
+              <py:when test="'title'">$title <em>{$id}</em></py:when>
+              <py:otherwise><em>{$id}</em> $title</py:otherwise>
+              </a></h3>
             <span class="foldable" />
             <div py:if="description" class="description" xml:space="preserve">
               ${wiki_to_html(context, description)}

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/report_view.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/report_view.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/report_view.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/report_view.html Tue Oct 16 20:06:09 2012
@@ -84,8 +84,7 @@
           <th py:for="header in header_group" py:if="not header.hidden" py:with="fullrow = header is header_group[-1]"
               colspan="${'100' if fullrow else None}"
               class="${'asc' if header.asc else 'desc' if header.asc is not None else None}">
-            <a py:strip="not sorting_enabled"
-              href="${report_href(sort=header.col, asc=not header.asc)}">
+            <a href="${report_href(sort=header.col, asc=not header.asc)}">
               $header.title
             </a>
           </th>

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/roadmap.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/roadmap.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/roadmap.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/roadmap.html Tue Oct 16 20:06:09 2012
@@ -12,7 +12,6 @@
 
   <body>
     <div id="content" class="roadmap">
-      <h1>Roadmap</h1>
 
       <form id="prefs" method="get" action="">
         <div>
@@ -30,6 +29,8 @@
         </div>
       </form>
 
+      <h1>Roadmap</h1>
+
       <div class="milestones">
         <div py:for="idx, milestone in enumerate(milestones)" class="milestone">
 

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket.html Tue Oct 16 20:06:09 2012
@@ -19,8 +19,12 @@
         $("div.description").find("h1,h2,h3,h4,h5,h6").addAnchor(_("Link to this section"));
         $(".foldable").enableFolding(false, true);
       <py:when test="ticket.exists">/*<![CDATA[*/
+        $("#attachments").toggleClass("collapsed");
+        $("#trac-up-attachments").click(function () {
+          $("#attachments").removeClass("collapsed");
+          return true;
+        });
         $("#modify").parent().toggleClass("collapsed");
-        $(".trac-topnav a").click(function() { $("#modify").parent().removeClass("collapsed"); });
 
         /* only enable control elements for the currently selected action */
         var actions = $("#action input[name='action']");
@@ -34,6 +38,30 @@
         actions.click(updateActionFields);
         updateActionFields();
 
+        function setRevertHandler() {
+          $("button.trac-revert").click(function() {
+            var div = $("div", this);
+            var field_name = div[0].id.substr(7);
+            var field_value = div.text();
+            var input = $("#propertyform *[name=field_" + field_name + "]");
+            if (input.length > 0) {
+              if (input.filter("input[type=radio]").length > 0) {
+                input.val([field_value]);
+              } else if (input.filter("input[type=checkbox]").length > 0) {
+                input.val(field_value == "1" ? [field_value] : []);
+              } else {
+                input.val(field_value);
+              }
+            } else { // Special case for CC checkbox
+              input = $("#propertyform input[name=cc_update]").val([]);
+            }
+            input.change();
+            $(this).closest("li").remove();
+            return false;
+          });
+        }
+        setRevertHandler();
+
         var comment_focused = false;
         $("#comment").focus(function() { comment_focused = true; })
                      .blur(function() { comment_focused = false; });
@@ -45,6 +73,8 @@
           if (!$('#trac-comments-oldest').checked())
             $('#trac-comments-oldest').click().change();
           $("#changelog").replaceWith(items.filter("#changelog"));
+          if ($('#trac-comments-only-toggle').attr('checked'))
+            $('#trac-comments-only-toggle').click().attr('checked', true);
           // Show warning
           var new_changes = $("#changelog .trac-new");
           $("#trac-edit-warning").toggle(new_changes.length != 0);
@@ -56,6 +86,7 @@
           var preview = $("#ticketchange").html(items.filter('#preview').children());
           var show_preview = preview.children().length != 0;
           $("#ticketchange").toggle(show_preview);
+          setRevertHandler();
           // Collapse property form if comment editor has focus
           if (show_preview && comment_focused)
             $("#modify").parent().addClass("collapsed");
@@ -66,7 +97,6 @@
         }, "#changelog .trac-loading");
         /*]]>*/
         <py:if test="preview_mode">
-        $("#attachments").toggleClass("collapsed");
         $("#trac-add-comment").scrollToTop();
         </py:if>
       </py:when>
@@ -96,30 +126,10 @@
                   has_edit_comment = 'TICKET_EDIT_COMMENT' in perm(ticket.resource);
                   has_property_editor = not version and version != 0 and not cnum_edit
                                         and (can_append or can_modify or can_edit or can_create)">
-      <div class="trac-topnav" py:if="ticket.exists and has_property_editor">
-        <a href="#propertyform" title="Go to the ticket editor">Modify</a> &darr;
-      </div>
-      <h1 id="trac-ticket-title" py:choose="">
-        <py:when test="ticket.exists">
-          <a href="${href.ticket(ticket.id)}" i18n:msg="id">Ticket #${ticket.id}</a>
-          <span class="status">(${ticket.status}<py:if
-              test="ticket.type"> ${ticket.type}</py:if><py:if
-              test="ticket.resolution">: ${ticket.resolution}</py:if>)</span>
-          <py:choose test="">
-            <py:when test="version is None" />
-            <py:when test="version == 0">
-              &mdash; <i18n:msg>at <a href="#comment:description">Initial Version</a></i18n:msg>
-            </py:when>
-            <py:otherwise>
-              &mdash; <i18n:msg params="version">at <a href="#comment:$version">Version $version</a></i18n:msg>
-            </py:otherwise>
-          </py:choose>
-        </py:when>
-        <py:otherwise>
-          Create New Ticket <span py:if="preview_mode and ticket.type" class="status">(${ticket.type})</span>
-        </py:otherwise>
-      </h1>
 
+      <h1 py:if="not ticket.exists">
+        Create New Ticket <span py:if="preview_mode and ticket.type" class="status">(${ticket.type})</span>
+      </h1>
 
       <py:if test="ticket.exists">
         <xi:include href="ticket_box.html" py:with="preview_mode = change_preview.fields"/>
@@ -150,7 +160,7 @@
             </form>
           </div>
 
-          <h2 class="foldable">Change History</h2>
+          <h3 class="foldable">Change History <span class="trac-count">(${len(changes)})</span></h3>
 
           <div id="changelog">
             <py:for each="change in changes">
@@ -169,33 +179,30 @@
                       else href.newticket() + '#ticket'}">
         <!--! Add comment -->
         <div py:if="ticket.exists and can_append" id="trac-add-comment" class="field">
-          <div class="trac-nav">
-            <a href="#content" title="View ticket fields and description">View</a> &uarr;
-          </div>
-          <h2>
-            <a id="edit" onfocus="$('#comment').get(0).focus()">Add a comment</a>
-          </h2>
-          <div id="trac-edit-warning" class="warning system-message"
-               style="${'display: none' if start_time == ticket['changetime'] else None}"
-               i18n:msg="">
-            This ticket has been modified since you started editing. You should review the
-            <em class="trac-new">other modifications</em> which have been appended above,
-            and any <em class="trac-conflict">conflicts</em> shown in the preview below.
-            You can nevertheless proceed and submit your changes if you wish so.
-          </div>
-          <!--! Comment field -->
-          <fieldset class="iefix">
-            <label for="comment" i18n:msg="">You may use
-              <a tabindex="42" href="${href.wiki('WikiFormatting')}">WikiFormatting</a>
-              here.
-            </label>
-            <textarea id="comment" name="comment" class="wikitext trac-resizable" rows="10" cols="78">
+          <h3 class="foldable" id="edit">Add Comment</h3>
+          <div>
+            <div id="trac-edit-warning" class="warning system-message"
+                 style="${'display: none' if start_time == ticket['changetime'] else None}"
+                 i18n:msg="">
+              This ticket has been modified since you started editing. You should review the
+              <em class="trac-new">other modifications</em> which have been appended above,
+              and any <em class="trac-conflict">conflicts</em> shown in the preview below.
+              You can nevertheless proceed and submit your changes if you wish so.
+            </div>
+            <!--! Comment field -->
+            <fieldset>
+              <label for="comment" i18n:msg="">You may use
+                <a tabindex="42" href="${href.wiki('WikiFormatting')}">WikiFormatting</a>
+                here.
+              </label>
+              <textarea id="comment" name="comment" class="wikitext trac-resizable" rows="10" cols="78">
 ${comment}</textarea>
-          </fieldset>
+            </fieldset>
+          </div>
         </div>
 
         <div>
-          <h2 py:if="ticket.exists" class="foldable">Modify Ticket</h2>
+          <h3 py:if="ticket.exists" class="foldable">Modify Ticket</h3>
           <div id="modify">
             <!--! Properties -->
             <fieldset id="properties" py:if="can_modify or can_edit or can_create"
@@ -225,7 +232,7 @@ ${comment}</textarea>
                   <tr>
                     <th><label for="field-description">Description:</label></th>
                     <td class="fullrow" colspan="3">
-                      <fieldset class="iefix">
+                      <fieldset>
                         <label for="field-description" id="field-description-help" i18n:msg="">You may use
                           <a tabindex="42" href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here.
                         </label>
@@ -364,7 +371,7 @@ ${value}</textarea>
         </p>
 
         <div py:if="ticket.exists" class="trac-nav">
-          <a href="#attachments" title="Go to the list of attachments">Attachments</a> &uarr;
+          <a href="#attachments" id="trac-up-attachments" title="Go to the list of attachments">Attachments</a> &uarr;
         </div>
         <div class="buttons">
           <py:if test="ticket.exists">
@@ -378,7 +385,8 @@ ${value}</textarea>
 
       </form>
 
-      <xi:include href="ticket_box.html" py:if="not ticket.exists" py:with="preview_mode = True"/>
+      <xi:include href="ticket_box.html" py:if="not ticket.exists"
+                  py:with="hide = not preview_mode; preview_mode = True"/>
 
       <div id="help" i18n:msg="">
         <strong>Note:</strong> See

Modified: incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket_box.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket_box.html?rev=1398968&r1=1398967&r2=1398968&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket_box.html (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/templates/ticket_box.html Tue Oct 16 20:06:09 2012
@@ -7,6 +7,7 @@ Arguments:
  - description_change: metadata about changes in the description
  - can_append: True if the user is allowed to append to tickets
  - preview_mode: if True, show the "draft" background
+ - hide=False: if True, hide the box
  - reporter_link=None: rendered link for the reporter field
  - owner_link=None: rendered link for the owner field
 -->
@@ -14,32 +15,63 @@ Arguments:
      xmlns:py="http://genshi.edgewall.org/"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      xmlns:i18n="http://genshi.edgewall.org/i18n"
-     id="ticket" class="${'ticketdraft' if preview_mode else None}">
+     id="ticket" class="trac-content ${'ticketdraft' if preview_mode else None}"
+     style="${'display: none' if value_of('hide', False) else None}">
+
   <div class="date">
     <p i18n:msg="created" py:if="ticket.exists">Opened ${pretty_dateinfo(ticket.time)}</p>
-    <p i18n:msg="modified" py:if="ticket.changetime != ticket.time">Last modified ${pretty_dateinfo(ticket.changetime)}</p>
+    <p i18n:msg="closed" py:if="closetime">Closed ${pretty_dateinfo(closetime)}</p>
+    <p i18n:msg="modified" py:if="ticket.changetime != ticket.time and ticket.changetime != closetime">
+      Last modified ${pretty_dateinfo(ticket.changetime)}</p>
     <p py:if="not ticket.exists"><span class="trac-loading"/><i>(ticket not yet created)</i></p>
   </div>
   <!--! use a placeholder if it's a new ticket -->
-  <h2 class="summary searchable">$ticket.summary</h2>
+
+  <h2>
+    <a href="${href.ticket(ticket.id)}" class="trac-id">#${ticket.id}</a>
+    <span class="trac-status">
+      ${'status' in fields_map and fields[fields_map['status']].rendered or ticket.status}
+    </span>
+    <span class="trac-type" py:if="ticket.type">
+      ${'type' in fields_map and fields[fields_map['type']].rendered or ticket.type}
+    </span>
+    <span class="trac-resolution" py:if="ticket.resolution">
+      (${'resolution' in fields_map and fields[fields_map['resolution']].rendered or ticket.resolution})
+    </span>
+  </h2>
+
+  <h1 id="trac-ticket-title" class="searchable">
+    <span class="summary">$ticket.summary</span>
+    <py:choose test="">
+      <py:when test="version is None" />
+      <py:when test="version == 0">
+        &mdash; <i18n:msg>at <a href="#comment:description">Initial Version</a></i18n:msg>
+      </py:when>
+      <py:otherwise>
+        &mdash; <i18n:msg params="version">at <a href="#comment:$version">Version $version</a></i18n:msg>
+      </py:otherwise>
+    </py:choose>
+  </h1>
 
   <table class="properties"
          py:with="fields = [f for f in fields if not f.skip and f.name not in ('type', 'owner')]">
-    <tr>
-      <th id="h_reporter">Reported by:</th>
-      <td headers="h_reporter" class="searchable">
-        ${reporter_link if defined('reporter_link') else authorinfo(ticket.reporter)}
-      </td>
-      <th id="h_owner">Owned by:</th>
-      <td headers="h_owner">
-        ${(owner_link if defined('owner_link') else authorinfo(ticket.owner)) if ticket.owner else ''}
-      </td>
+    <tr py:with="
+      v_reporter = reporter_link if defined('reporter_link') else authorinfo(ticket.reporter);
+      v_owner = (owner_link if defined('owner_link') else authorinfo(ticket.owner)) if ticket.owner else ''
+      ">
+      <th id="h_reporter" class="${classes(missing=not v_reporter)}">Reported by:</th>
+      <td headers="h_reporter" class="searchable">$v_reporter</td>
+      <th id="h_owner" class="${classes(missing=not v_owner)}">Owned by:</th>
+      <td headers="h_owner">$v_owner</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"
-            id="${'h_' + field.name if field else None}">
+            id="${'h_' + field.name if field else None}"
+            class="${classes(missing=not field or
+                              ('rendered' in field and not field.rendered) or
+                               not ticket[field.name])}" >
           <py:if test="field"><i18n:msg params="field">${field.label or field.name}:</i18n:msg></py:if>
         </th>
         <td py:if="idx == 0 or not fullrow"
@@ -59,10 +91,10 @@ Arguments:
   <div class="description">
     <h3 id="comment:description">
       Description
-      <span py:if="description_change" class="lastmod" title="$description_change.date">
+      <a href="${href.ticket(ticket.id, action='diff', version=description_change.cnum)}"
+         py:if="description_change" class="lastmod trac-diff" title="$description_change.date">
         <i18n:msg params="author">(last modified by ${authorinfo(description_change.author)})</i18n:msg>
-        (<a href="${href.ticket(ticket.id, action='diff', version=description_change.cnum)}">diff</a>)
-      </span>
+      </a>
     </h3>
 
     <!--! Quote the description (only for existing tickets) -->
@@ -70,7 +102,7 @@ Arguments:
           id="addreply" method="get" action="#comment">
       <div class="inlinebuttons">
         <input type="hidden" name="replyto" value="description" />
-        <input type="submit" name="reply" value="${_('Reply')}" title="Reply, quoting this description" />
+        <input type="submit" name="reply" value="${captioned_button('↳', _('Reply'))}" title="Reply, quoting this description" />
       </div>
     </form>
     <div py:if="ticket.description" class="searchable" xml:space="preserve">