== scripts/trac-admin
==================================================================
--- scripts/trac-admin  (revision 1104)
+++ scripts/trac-admin  (revision 1105)
@@ -706,6 +706,23 @@
                 self._do_wiki_import(filename, page, cursor)
 
 
+    ## (Ticket) Category
+    _help_category = [('category list', 'Show possible ticket categories'),
+                       ('category add <value>', 'Add a category value option'),
+                       ('category change <value> <newvalue>',
+                        'Change a category value'),
+                       ('category remove <value>', 'Remove category value')]
+
+    def complete_category (self, text, line, begidx, endidx):
+        if begidx == 16:
+            comp = self.get_enum_list ('category')
+        elif begidx < 15:
+            comp = ['list','add','change','remove']
+        return self.word_complete(text, comp)
+
+    def do_category(self, line):
+        self._do_enum('category', line)
+
     ## (Ticket) Priority
     _help_priority = [('priority list', 'Show possible ticket priorities'),
                        ('priority add <value>', 'Add a priority value option'),
=== trac/core.py
==================================================================
--- trac/core.py  (revision 1104)
+++ trac/core.py  (revision 1105)
@@ -199,6 +199,8 @@
     pass
 
 def populate_hdf(hdf, env, db, req):
+    sql_to_hdf(db, "SELECT name FROM enum WHERE type='category' "
+               "ORDER BY value", hdf, 'enums.category')
     sql_to_hdf(db, "SELECT name FROM enum WHERE type='priority' "
                "ORDER BY value", hdf, 'enums.priority')
     sql_to_hdf(db, "SELECT name FROM enum WHERE type='severity' "
=== trac/db_default.py
==================================================================
--- trac/db_default.py  (revision 1104)
+++ trac/db_default.py  (revision 1105)
@@ -21,7 +21,7 @@
 
 
 # Database version identifier. Used for automatic upgrades.
-db_version = 7
+db_version = 8
 
 def __mkreports(reps):
     """Utility function used to create report data in same syntax as the
=== trac/Query.py
==================================================================
--- trac/Query.py  (revision 1104)
+++ trac/Query.py  (revision 1105)
@@ -59,6 +59,7 @@
             results.append({
                 'id': id,
                 'href': self.env.href.ticket(id),
+                'category': row['category'],
                 'summary': util.escape(row['summary'] or '(no summary)'),
                 'status': row['status'] or '',
                 'component': row['component'] or '',
@@ -78,6 +79,8 @@
         if self.args.has_key('search'):
             self.req.redirect(self.env.href.query(constraints, order, desc,
                                                   action='view'))
+        if self.args.has_key('clear'):
+            self.req.redirect(self.env.href.query())
 
         action = self.args.get('action')
         if not action and not constraints:
@@ -91,6 +94,7 @@
 
     def _render_editor(self, constraints, order, desc):
         self.req.hdf.setValue('title', 'Custom Query')
+        self.req.hdf.setValue('query.clear_href', self.env.href.query(action='edit'))
         util.add_to_hdf(constraints, self.req.hdf, 'query.constraints')
         self.req.hdf.setValue('query.order', order)
         if desc: self.req.hdf.setValue('query.desc', '1')
@@ -112,6 +116,8 @@
                 del constraints[field]
 
         cursor = self.db.cursor()
+        add_options('category', constraints, 'query.options.', cursor,
+                    "SELECT name FROM enum WHERE type='category' ORDER BY value")
         add_options('status', constraints, 'query.options.', cursor,
                     "SELECT name FROM enum WHERE type='status' ORDER BY value")
         add_options('resolution', constraints, 'query.options.', cursor,
@@ -146,7 +152,7 @@
 
         # FIXME: the user should be able to configure which columns should
         # be displayed
-        headers = [ 'id', 'summary', 'status', 'component', 'owner' ]
+        headers = [ 'category', 'id', 'summary', 'status', 'component', 'owner' ]
         cols = headers
         if not 'priority' in cols:
             cols.append('priority')
@@ -176,7 +182,7 @@
                       "(id=%s.ticket AND %s.name='%s')"
                       % (k, k, k, k))
 
-        for col in [c for c in ['status', 'resolution', 'priority', 'severity']
+        for col in [c for c in ['category', 'status', 'resolution', 'priority', 'severity']
                     if c in cols]:
             sql.append(" INNER JOIN (SELECT name AS %s_name, value AS %s_value " \
                                    "FROM enum WHERE type='%s')" \
@@ -194,7 +200,7 @@
         if clauses:
             sql.append(" WHERE " + " AND ".join(clauses))
 
-        if order in ['status', 'resolution', 'priority', 'severity']:
+        if order in ['category', 'status', 'resolution', 'priority', 'severity']:
             sql.append(" ORDER BY %s_value" % order)
         else:
             sql.append(" ORDER BY " + order)
=== trac/Timeline.py
==================================================================
--- trac/Timeline.py  (revision 1104)
+++ trac/Timeline.py  (revision 1105)
@@ -58,23 +58,26 @@
         q = []
         if changeset:
             q.append("SELECT time, rev AS idata, '' AS tdata, 1 AS type, "
-                     " message, author "
+                     " message, author, '' AS category "
                      "FROM revision WHERE time>=%s AND time<=%s" %
                      (start, stop))
         if tickets:
             q.append("SELECT time, id AS idata, '' AS tdata, 2 AS type, "
-                     "summary AS message, reporter AS author "
+                     " summary AS message, reporter AS author, category "
                      "FROM ticket WHERE time>=%s AND time<=%s" %
                      (start, stop))
-            q.append("SELECT time, ticket AS idata, '' AS tdata, 4 AS type, "
-                     "'' AS message, author "
-                     "FROM ticket_change WHERE field='status' "
-                     "AND newvalue='reopened' AND time>=%s AND time<=%s" %
+            q.append("SELECT t1.time, t1.ticket AS idata, '' AS tdata, 4 AS type, "
+                     " '' AS message, t1.author, t0.category AS category "
+                     "FROM ticket_change t1 "
+                     " LEFT JOIN ticket t0 ON t0.id = t1.ticket "
+                     "WHERE t1.field='status' "
+                     "AND t1.newvalue='reopened' AND t1.time>=%s AND t1.time<=%s" %
                      (start, stop))
             q.append("SELECT t1.time AS time, t1.ticket AS idata,"
                      "       t2.newvalue AS tdata, 3 AS type,"
-                     "       t3.newvalue AS message, t1.author AS author"
+                     "       t3.newvalue AS message, t1.author AS author, t0.category AS category"
                      " FROM ticket_change t1"
+                     "   LEFT JOIN ticket t0 ON t0.id = t1.ticket"
                      "   INNER JOIN ticket_change t2 ON t1.ticket = t2.ticket"
                      "     AND t1.time = t2.time"
                      "   LEFT OUTER JOIN ticket_change t3 ON t1.time = t3.time"
@@ -84,19 +87,22 @@
                      "   AND t1.time >= %s AND t1.time <= %s" % (start,stop))
         if wiki:
             q.append("SELECT time, -1 AS idata, name AS tdata, 5 AS type, "
-                     "comment AS message, author "
+                     "comment AS message, author, '' AS category "
                         "FROM wiki WHERE time>=%s AND time<=%s" %
                      (start, stop))
         if milestone:
             q.append("SELECT time, -1 AS idata, '' AS tdata, 6 AS type, "
-                     "name AS message, '' AS author " 
+                     "name AS message, '' AS author, '' AS category " 
                      "FROM milestone WHERE time>=%s AND time<=%s" %
                      (start, stop))
+
         if ticket_comments:
-            q.append("SELECT time, ticket AS idata, '' AS tdata, 7 AS type, "
-                     "newvalue AS message, author AS author "
-                     "FROM ticket_change WHERE field = 'comment' "
-                     "AND time>=%s AND time<=%s" % (start, stop))
+            q.append("SELECT t1.time AS time, t1.ticket AS idata, '' AS tdata, 7 AS type, "
+                     "t1.newvalue AS message, t1.author AS author, t0.category AS category "
+                     "FROM ticket_change t1"
+                     "   LEFT JOIN ticket t0 ON t0.id = t1.ticket "
+                     "WHERE t1.field = 'comment' "
+                     "AND t1.time>=%s AND t1.time<=%s" % (start, stop))
 
         q_str = string.join(q, ' UNION ALL ')
         q_str += ' ORDER BY time DESC'
@@ -121,6 +127,7 @@
                     'tdata': row['tdata'],
                     'type': int(row['type']),
                     'message': row['message'] or '',
+                    'category': row['category'],
                     'author': util.escape(row['author'] or 'anonymous')
                     }
 
=== trac/upgrades/db8.py
==================================================================
--- trac/upgrades/db8.py  (revision 1104)
+++ trac/upgrades/db8.py  (revision 1105)
@@ -0,0 +1,35 @@
+sql = """
+-- Add category to 'ticket'
+CREATE TEMP TABLE ticket_old AS SELECT * FROM ticket;
+DROP TABLE ticket;
+CREATE TABLE ticket (
+        id              integer PRIMARY KEY,
+        category        text,           -- the nature of the ticket
+        time            integer,        -- the time it was created
+        changetime      integer,
+        component       text,
+        severity        text,
+        priority        text,
+        owner           text,           -- who is this ticket assigned to
+        reporter        text,
+        cc              text,           -- email addresses to notify
+        url             text,           -- url related to this ticket
+        version         text,           --
+        milestone       text,           --
+        status          text,
+        resolution      text,
+        summary         text,           -- one-line summary
+        description     text,           -- problem description (long)
+        keywords        text
+);
+INSERT INTO ticket(id, category, time, changetime, component,
+                   severity, priority, owner, reporter, cc, url, version,
+                   milestone, status, resolution, summary, description, keywords)
+  SELECT id, '', time, changetime, component,
+         severity, priority, owner, reporter, cc, url, version,
+         milestone, status, resolution, summary, description, keywords FROM ticket_old;
+
+"""
+
+def do_upgrade(env, ver, cursor):
+    cursor.execute(sql)

Property changes on: trac/upgrades/db8.py
___________________________________________________________________
Name: svn:executable
 +*

=== trac/Ticket.py
==================================================================
--- trac/Ticket.py  (revision 1104)
+++ trac/Ticket.py  (revision 1105)
@@ -37,7 +37,7 @@
 class Ticket(UserDict):
     std_fields = ['time', 'component', 'severity', 'priority', 'milestone',
                   'reporter', 'owner', 'cc', 'url', 'version', 'status', 'resolution',
-                  'keywords', 'summary', 'description']
+                  'keywords', 'summary', 'description', 'category']
 
     def __init__(self, *args):
         UserDict.__init__(self)
@@ -318,6 +318,8 @@
                           self.env.get_config('ticket', 'default_component'))
         ticket.setdefault('milestone',
                           self.env.get_config('ticket', 'default_milestone'))
+        ticket.setdefault('category',
+                          self.env.get_config('ticket', 'default_category'))
         ticket.setdefault('priority',
                           self.env.get_config('ticket', 'default_priority'))
         ticket.setdefault('severity',
@@ -408,6 +410,7 @@
         util.hdf_add_if_missing(self.req.hdf, 'ticket.components', ticket['component'])
         util.hdf_add_if_missing(self.req.hdf, 'ticket.milestones', ticket['milestone'])
         util.hdf_add_if_missing(self.req.hdf, 'ticket.versions', ticket['version'])
+        util.hdf_add_if_missing(self.req.hdf, 'enums.category', ticket['category'])
         util.hdf_add_if_missing(self.req.hdf, 'enums.priority', ticket['priority'])
         util.hdf_add_if_missing(self.req.hdf, 'enums.severity', ticket['severity'])
         util.hdf_add_if_missing(self.req.hdf, 'enums.resolution', 'fixed')
=== templates/ticket.cs
==================================================================
--- templates/ticket.cs  (revision 1104)
+++ templates/ticket.cs  (revision 1105)
@@ -32,7 +32,7 @@
 
 <div id="ticket">
  <div class="date"><?cs var:ticket.opened ?></div>
- <h1>Ticket #<?cs var:ticket.id ?> <?cs
+ <h1><?cs var:ticket.category ?> Ticket #<?cs var:ticket.id ?> <?cs
  if:ticket.status == 'closed' ?>(Closed: <?cs var:ticket.resolution ?>)<?cs
  elif:ticket.status != 'new' ?>(<?cs var:ticket.status ?>)<?cs
  /if ?></h1>
@@ -166,6 +166,9 @@
      var:ticket.summary ?>" /><?cs
    if $trac.acl.TICKET_ADMIN ?>
     <br />
+    <label for="category">Category:</label><?cs
+    call:hdf_select(enums.category, "category", ticket.category) ?>
+    <br />
     <label for="description">Description:</label>
     <div style="float: left">
      <textarea id="description" name="description" rows="10" cols="68"><?cs
=== templates/newticket.cs
==================================================================
--- templates/newticket.cs  (revision 1104)
+++ templates/newticket.cs  (revision 1105)
@@ -9,8 +9,10 @@
 
 <div id="content" class="ticket">
 
-<h3>Create New Ticket:</h3>
 <form id="newticket" action="<?cs var:cgi_location ?>#preview" method="post">
+ <h3>Create New Ticket of <label for="category">category:</label><?cs
+  call:hdf_select(enums.category, "category", newticket.category) ?>
+ </h3>
  <div class="field">
   <label for="reporter">Your email or username:</label><br />
   <input type="text" id="reporter" name="reporter" size="40" value="<?cs
=== templates/query.cs
==================================================================
--- templates/query.cs  (revision 1104)
+++ templates/query.cs  (revision 1105)
@@ -2,11 +2,16 @@
 <?cs include:"header.cs" ?>
 <?cs include:"macros.cs" ?>
 
-<div id="ctxtnav" class="nav"><?cs if:query.edit_href ?>
- <ul>
-  <li class="last"><a href="<?cs var:query.edit_href ?>">Refine Query</a></li>
+<div id="ctxtnav" class="nav">
+ <ul><?cs 
+  if:query.edit_href ?>
+   <li class="last"><a href="<?cs var:query.edit_href ?>">Refine Query</a></li><?cs 
+  /if ?><?cs 
+  if:query.clear_href ?>
+   <li class="last"><a href="<?cs var:query.clear_href ?>">Clear Query</a></li><?cs 
+  /if ?>
  </ul>
-<?cs /if ?></div>
+</div>
 
 <div id="content" class="query">
  <h1><?cs var:title ?></h1>
@@ -20,6 +25,10 @@
   <?cs if:query.desc ?><input type="hidden" name="desc" value="1" /><?cs /if ?>
   <legend>Ticket Properties</legend>
   <div>
+   <label for="category" accesskey="t">Category:</label>
+   <?cs call:hdf_select_multiple(query.options.category, 'category', 4) ?>
+  </div>
+  <div>
    <label for="component" accesskey="c">Component:</label>
    <?cs call:hdf_select_multiple(query.options.component, 'component', 4) ?>
   </div>
@@ -112,7 +121,7 @@
  /if ?> matched this query.</p>
  <table id="tktlist" class="listing">
   <thead><tr><?cs each:header = query.headers ?><?cs
-   if:name(header) == 0 ?><th class="ticket<?cs
+   if:header.name == 'id' ?><th class="ticket<?cs
     if:header.order ?> <?cs var:header.order ?><?cs /if ?>">
     <a href="<?cs var:header.href ?>" title="Sort by ID (<?cs
       if:header.order == 'asc' ?>descending<?cs
@@ -131,8 +140,8 @@
      if:name(result) % 2 ?>odd<?cs else ?>even<?cs /if ?> <?cs
      var:result.priority ?>">
     <?cs each:header = query.headers ?><?cs
-     if:name(header) == 0 ?>
-      <td class="ticket"><a href="<?cs var:result.href ?>" title="View ticket"><?cs
+     if:header.name == 'id' ?>
+      <td class="ticket"><a href="<?cs var:result.href ?>" title="View ticket">#<?cs
         var:result.id ?></a></td><?cs
      else ?>
       <td><?cs if:header.name == 'summary' ?>
=== templates/timeline.cs
==================================================================
--- templates/timeline.cs  (revision 1104)
+++ templates/timeline.cs  (revision 1105)
@@ -68,7 +68,7 @@
     'Changeset <em>['+$item.idata+']</em> by '+$item.author,$item.node_list+item.message) ?>
  <?cs elif:item.type == #2 ?><!-- New ticket -->
   <?cs call:tlitem(item.href, 'newticket',
-    'Ticket <em>#'+$item.idata+'</em> created by '+$item.author, item.message) ?>
+    $item.category+' Ticket <em>#'+$item.idata+'</em> created by '+$item.author, item.message) ?>
  <?cs elif:item.type == #3 ?><!-- Closed ticket -->
   <?cs if:item.message ?>
    <?cs set:imessage = ' - ' + $item.message ?>
@@ -76,11 +76,11 @@
    <?cs set:imessage = '' ?>
   <?cs /if ?>
   <?cs call:tlitem(item.href, 'closedticket',
-    'Ticket <em>#'+$item.idata+'</em> resolved by '+$item.author, 
+    $item.category+' Ticket <em>#'+$item.idata+'</em> resolved by '+$item.author, 
     $item.tdata+$imessage) ?>
  <?cs elif:item.type == #4 ?><!-- Reopened ticket -->
   <?cs call:tlitem(item.href, 'newticket',
-    'Ticket <em>#'+$item.idata+'</em> reopened by '+$item.author, '') ?>
+    $item.category+' Ticket <em>#'+$item.idata+'</em> reopened by '+$item.author, '') ?>
  <?cs elif:item.type == #5 ?><!-- Wiki change -->
   <?cs call:tlitem(item.href, 'wiki',
     '<em>'+$item.tdata+'</em> edited by '+$item.author, item.message) ?>
@@ -89,7 +89,7 @@
     '<em>Milestone '+$item.message+'</em> reached', '') ?>
  <?cs elif:item.type == #7 ?><!-- Ticket comment -->
   <?cs call:tlitem(item.href, 'ticketcomment',
-    'Ticket <em>#'+$item.idata+'</em> contribution by '+$item.author, $item.message) ?>
+    $item.category+' Ticket <em>#'+$item.idata+'</em> contribution by '+$item.author, $item.message) ?>
  <?cs /if ?>
 <?cs /each ?>
 <?cs if:len(timeline.items) ?></dl><?cs /if ?>
