Index: trac/ticket/query.py
===================================================================
--- trac/ticket/query.py	(revision 10847)
+++ trac/ticket/query.py	(working copy)
@@ -24,7 +24,7 @@
 
 from genshi.builder import tag
 
-from trac.config import Option, IntOption 
+from trac.config import Option, IntOption, ListOption 
 from trac.core import *
 from trac.db import get_column_names
 from trac.mimeview.api import Mimeview, IContentConverter, Context
@@ -122,10 +122,25 @@
                      c == 'id']
         self.rows = [c for c in rows if c in field_names]
         if self.order != 'id' and self.order not in field_names:
-            self.order = 'priority'
-
-        if self.group not in field_names:
+            default_order = QueryModule(self.env).default_order.split()
+                
+            conf_order = default_order[0] or None
+            conf_desc = len(default_order) > 1 and default_order[-1].lower().startswith('desc') and True or False
+                
+            if conf_order and (conf_order == 'id' or conf_order in field_names):
+                self.order = conf_order
+                self.desc = conf_desc
+            else:
+                # Fall back to 'priority' on invalid config
+                self.order = 'priority'
+                self.desc = False
+        
+        # Handle empty string as a request to not group the result
+        if self.group == '':
             self.group = None
+        elif self.group not in field_names:
+            default_group = QueryModule(self.env).default_group
+            self.group = default_group in field_names and default_group or None
 
         constraint_cols = {}
         for clause in self.constraints:
@@ -232,35 +247,55 @@
         return cols
 
     def get_default_columns(self):
-        cols = self.get_all_columns()
+        cols = QueryModule(self.env).default_cols
         
-        # Semi-intelligently remove columns that are restricted to a single
-        # value by a query constraint.
-        for col in [k for k in self.constraint_cols.keys()
-                    if k != 'id' and k in cols]:
-            constraints = self.constraint_cols[col]
-            for constraint in constraints:
-                if not (len(constraint) == 1 and constraint[0]
-                        and not constraint[0][0] in '!~^$' and col in cols
-                        and col not in self.time_fields):
-                    break
-            else:
-                cols.remove(col)
-            if col == 'status' and 'resolution' in cols:
+        # Use the columns defined in the config
+        if cols:
+            # Make sure the column we order by is visible.
+            if self.order not in cols:
+                cols.append(self.order)
+    
+            # Make sure to not show the column we group on.            
+            if self.group in cols:
+                cols.remove(self.group)
+                
+        # Try to automatically calculate what to show
+        else:
+            cols = self.get_all_columns()
+            
+            # Semi-intelligently remove columns that are restricted to a single
+            # value by a query constraint.
+            for col in [k for k in self.constraint_cols.keys()
+                        if k != 'id' and k in cols]:
+                constraints = self.constraint_cols[col]
                 for constraint in constraints:
-                    if 'closed' in constraint:
+                    if not (len(constraint) == 1 and constraint[0]
+                            and not constraint[0][0] in '!~^$' and col in cols
+                            and col not in self.time_fields):
                         break
                 else:
-                    cols.remove('resolution')
-        if self.group in cols:
-            cols.remove(self.group)
-
-        # Only display the first seven columns by default
-        cols = cols[:7]
-        # Make sure the column we order by is visible, if it isn't also
-        # the column we group by
-        if not self.order in cols and not self.order == self.group:
-            cols[-1] = self.order
+                    cols.remove(col)
+                if col == 'status' and 'resolution' in cols:
+                    for constraint in constraints:
+                        if 'closed' in constraint:
+                            break
+                    else:
+                        cols.remove('resolution')
+    
+            # Make sure to not show the column we group on.            
+            if self.group in cols:
+                cols.remove(self.group)
+            
+            # Only display the first seven columns by default
+            cols = cols[:7]
+            
+            # Make sure the column we order by is visible.
+            if self.order not in cols and self.order != self.group:
+                if len(cols) < 7:
+                    cols.append(self.order)
+                else:
+                    cols[-1] = self.order
+        
         return cols
 
     def count(self, req=None, db=None, cached_ids=None, authname=None,
@@ -406,7 +441,7 @@
         return href.query(constraints,
                           report=id,
                           order=order, desc=desc and 1 or None,
-                          group=self.group or None,
+                          group=self.group or "",
                           groupdesc=self.groupdesc and 1 or None,
                           col=cols,
                           row=self.rows,
@@ -827,6 +862,22 @@
     items_per_page = IntOption('query', 'items_per_page', 100,
         """Number of tickets displayed per page in ticket queries,
         by default (''since 0.11'')""")
+    
+    default_cols = ListOption('query', 'default_cols',  
+        default=None, 
+        doc="""List of columns to show in query unless defined by the query.
+        Default is to let trac calculate this internally.
+        (''since 0.12.3'')""")
+    
+    default_order = Option('query', 'default_order',
+        default='priority asc',
+        doc="""The default order to use for queries.
+        (''since 0.12.3'')""")
+    
+    default_group = Option('query', 'default_group',
+        default=None,
+        doc="""The default group to use for queries.
+        (''since 0.12.3'')""")
 
     # IContentConverter methods
 


