Author: matevz
Date: Thu Aug  8 13:19:01 2013
New Revision: 1511771

URL: http://svn.apache.org/r1511771
Log:
#596 - pagination on /dashboard confused

Added:
    bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/batch.py
Modified:
    bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py
    bloodhound/trunk/bloodhound_multiproduct/multiproduct/env.py
    bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py
    bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/query.py
    bloodhound/trunk/bloodhound_multiproduct/setup.py
    bloodhound/trunk/bloodhound_multiproduct/tests/product-query-link-tests.txt
    bloodhound/trunk/bloodhound_multiproduct/tests/wikisyntax.py
    bloodhound/trunk/bloodhound_theme/bhtheme/theme.py
    bloodhound/trunk/trac/trac/ticket/tests/query.py

Modified: bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py 
(original)
+++ bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/query.py Thu Aug  
8 13:19:01 2013
@@ -42,7 +42,6 @@ from bhdashboard.util import WidgetBase,
                               trac_tags
 
 from multiproduct.env import ProductEnvironment
-from multiproduct.ticket.query import ProductQueryModule
 
 class TicketQueryWidget(WidgetBase):
     """Display tickets matching a TracQuery using a grid
@@ -94,12 +93,11 @@ class TicketQueryWidget(WidgetBase):
             more_link_href = req.href('query', args)
             args.update({'page' : page, 'max': maxrows})
 
-            qrymdl = self.env[QueryModule
-                if isinstance(self.env, ProductEnvironment)
-                else ProductQueryModule]
+            qrymdl = self.env[QueryModule]
             if qrymdl is None :
                 raise TracError('Query module not available (disabled?)')
-            data = qrymdl.process_request(fakereq)[1]
+
+            data = qrymdl.process_request(fakereq, self.env)[1]
         except TracError, exc:
             if data is not None:
                 exc.title = data.get('title', 'TracQuery')

Modified: bloodhound/trunk/bloodhound_multiproduct/multiproduct/env.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/multiproduct/env.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/multiproduct/env.py (original)
+++ bloodhound/trunk/bloodhound_multiproduct/multiproduct/env.py Thu Aug  8 
13:19:01 2013
@@ -892,6 +892,10 @@ class ProductEnvironment(Component, Comp
     # Multi-product API extensions
 
     @classmethod
+    def lookup_global_env(cls, env):
+        return env.parent if isinstance(env, ProductEnvironment) else env
+
+    @classmethod
     def lookup_env(cls, env, prefix=None, name=None):
         """Instantiate environment according to product prefix or name
 

Modified: bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py (original)
+++ bloodhound/trunk/bloodhound_multiproduct/multiproduct/hooks.py Thu Aug  8 
13:19:01 2013
@@ -20,6 +20,8 @@
 import multiproduct.env
 import multiproduct.dbcursor
 import multiproduct.versioncontrol
+import multiproduct.ticket.query
+import multiproduct.ticket.batch
 
 import re
 
@@ -31,7 +33,7 @@ from trac.web.main import RequestWithSes
 
 PRODUCT_RE = re.compile(r'^/products(?:/(?P<pid>[^/]*)(?P<pathinfo>.*))?')
 REDIRECT_DEFAULT_RE = \
-    re.compile(r'^/(?P<section>milestone|roadmap|query|report|newticket|'
+    re.compile(r'^/(?P<section>milestone|roadmap|report|newticket|'
                r'ticket|qct|timeline|diff|batchmodify|search|'
                r'(raw-|zip-)?attachment/(ticket|milestone))(?P<pathinfo>.*)')
 

Added: bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/batch.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/batch.py?rev=1511771&view=auto
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/batch.py 
(added)
+++ bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/batch.py Thu 
Aug  8 13:19:01 2013
@@ -0,0 +1,40 @@
+from trac.ticket.batch import BatchModifyModule
+from trac.util.translation import _
+from trac.web.chrome import add_script_data
+from multiproduct.env import ProductEnvironment
+
+
+class ProductBatchModifyModule(BatchModifyModule):
+    def add_template_data(self, req, data, tickets):
+        if isinstance(self.env, ProductEnvironment):
+            super(ProductBatchModifyModule, self).add_template_data(
+                req, data, tickets)
+            return
+
+        data['batch_modify'] = True
+        data['query_href'] = req.session['query_href'] or req.href.query()
+
+        tickets_by_product = {}
+        for t in tickets:
+            tickets_by_product.setdefault(t['product'], []).append(t)
+
+        data['action_controls'] = []
+        global_env = ProductEnvironment.lookup_global_env(self.env)
+        tmpenv = self.env
+        for k,v in tickets_by_product.iteritems():
+            self.env = ProductEnvironment(global_env, k)
+            data['action_controls'] += self._get_action_controls(req, v)
+        self.env = tmpenv
+        batch_list_modes = [
+            {'name': _("add"), 'value': "+"},
+            {'name': _("remove"), 'value': "-"},
+            {'name': _("add / remove"), 'value': "+-"},
+            {'name': _("set to"), 'value': "="},
+        ]
+        add_script_data(req, batch_list_modes=batch_list_modes,
+                             batch_list_properties=self._get_list_fields())
+
+import trac.ticket.batch
+trac.ticket.batch.BatchModifyModule = ProductBatchModifyModule
+trac.ticket.BatchModifyModule = ProductBatchModifyModule
+

Modified: bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/query.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/query.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/query.py 
(original)
+++ bloodhound/trunk/bloodhound_multiproduct/multiproduct/ticket/query.py Thu 
Aug  8 13:19:01 2013
@@ -20,6 +20,8 @@
 
 from __future__ import with_statement
 
+import re
+
 from itertools import groupby
 from math import ceil
 from datetime import datetime, timedelta
@@ -52,6 +54,9 @@ class ProductQuery(Query):
     """
     
     def _count(self, sql, args):
+        if isinstance(self.env, ProductEnvironment):
+            return super(ProductQuery, self)._count(sql, args)
+
         cnt = self.env.db_direct_query("SELECT COUNT(*) FROM (%s) AS x"
                                 % sql, args)[0][0]
         # "AS x" is needed for MySQL ("Subqueries in the FROM Clause")
@@ -79,6 +84,10 @@ class ProductQuery(Query):
         :since 1.0: the `db` parameter is no longer needed and will be removed
         in version 1.1.1
         """
+        if isinstance(self.env, ProductEnvironment):
+            return super(ProductQuery, self).execute(req, db, cached_ids, 
authname,
+                tzinfo, href, locale)
+
         if req is not None:
             href = req.href
         with self.env.db_direct_query as db:
@@ -142,184 +151,68 @@ class ProductQuery(Query):
             cursor.close()
             return results
 
+import trac.ticket.query
+trac.ticket.query.Query = ProductQuery
+trac.ticket.Query = ProductQuery
 
-class ProductQueryModule(QueryModule):
-    def process_request(self, req):
-        req.perm.assert_permission('TICKET_VIEW')
-
-        constraints = self._get_constraints(req)
-        args = req.args
-        if not constraints and not 'order' in req.args:
-            # If no constraints are given in the URL, use the default ones.
-            if req.authname and req.authname != 'anonymous':
-                qstring = self.default_query
-                user = req.authname
-            else:
-                email = req.session.get('email')
-                name = req.session.get('name')
-                qstring = self.default_anonymous_query
-                user = email or name or None
-
-            self.log.debug('QueryModule: Using default query: %s', 
str(qstring))
-            if qstring.startswith('?'):
-                arg_list = parse_arg_list(qstring[1:])
-                args = arg_list_to_args(arg_list)
-                constraints = self._get_constraints(arg_list=arg_list)
-            else:
-                query = ProductQuery.from_string(self.env, qstring)
-                args = {'order': query.order, 'group': query.group,
-                        'col': query.cols, 'max': query.max}
-                if query.desc:
-                    args['desc'] = '1'
-                if query.groupdesc:
-                    args['groupdesc'] = '1'
-                constraints = query.constraints
-
-            # Substitute $USER, or ensure no field constraints that depend
-            # on $USER are used if we have no username.
-            for clause in constraints:
-                for field, vals in clause.items():
-                    for (i, val) in enumerate(vals):
-                        if user:
-                            vals[i] = val.replace('$USER', user)
-                        elif val.endswith('$USER'):
-                            del clause[field]
-                            break
-
-        cols = args.get('col')
-        if isinstance(cols, basestring):
-            cols = [cols]
-        # Since we don't show 'id' as an option to the user,
-        # we need to re-insert it here.
-        if cols and 'id' not in cols:
-            cols.insert(0, 'id')
-        rows = args.get('row', [])
-        if isinstance(rows, basestring):
-            rows = [rows]
-        format = req.args.get('format')
-        max = args.get('max')
-        if max is None and format in ('csv', 'tab'):
-            max = 0 # unlimited unless specified explicitly
-        query = ProductQuery(self.env, req.args.get('report'),
-                      constraints, cols, args.get('order'),
-                      'desc' in args, args.get('group'),
-                      'groupdesc' in args, 'verbose' in args,
-                      rows,
-                      args.get('page'),
-                      max)
-
-        if 'update' in req.args:
-            # Reset session vars
-            for var in ('query_constraints', 'query_time', 'query_tickets'):
-                if var in req.session:
-                    del req.session[var]
-            req.redirect(query.get_href(req.href))
-
-        # Add registered converters
-        for conversion in Mimeview(self.env).get_supported_conversions(
-                                             'trac.ticket.Query'):
-            add_link(req, 'alternate',
-                     query.get_href(req.href, format=conversion[0]),
-                     conversion[1], conversion[4], conversion[0])
-
-        if format:
-            filename = 'query' if format != 'rss' else None
-            Mimeview(self.env).send_converted(req, 'trac.ticket.Query', query,
-                                              format, filename=filename)
-
-        return self.display_html(req, query)
-
-    def display_html(self, req, query):
-        # The most recent query is stored in the user session;
-        orig_list = None
-        orig_time = datetime.now(utc)
-        query_time = int(req.session.get('query_time', 0))
-        query_time = datetime.fromtimestamp(query_time, utc)
-        query_constraints = unicode(query.constraints)
-        try:
-            if query_constraints != req.session.get('query_constraints') \
-                    or query_time < orig_time - timedelta(hours=1):
-                tickets = query.execute(req)
-                # New or outdated query, (re-)initialize session vars
-                req.session['query_constraints'] = query_constraints
-                req.session['query_tickets'] = ' '.join([str(t['id'])
-                                                         for t in tickets])
-            else:
-                orig_list = [int(id) for id
-                             in req.session.get('query_tickets', '').split()]
-                tickets = query.execute(req, cached_ids=orig_list)
-                orig_time = query_time
-        except QueryValueError, e:
-            tickets = []
-            for error in e.errors:
-                add_warning(req, error)
-
-        context = web_context(req, 'query')
-        owner_field = [f for f in query.fields if f['name'] == 'owner']
-        if owner_field:
-            TicketSystem(self.env).eventually_restrict_owner(owner_field[0])
-        data = query.template_data(context, tickets, orig_list, orig_time, req)
-
-        req.session['query_href'] = query.get_href(context.href)
-        req.session['query_time'] = to_timestamp(orig_time)
-        req.session['query_tickets'] = ' '.join([str(t['id'])
-                                                 for t in tickets])
-        title = _('Custom Query')
-
-        # Only interact with the report module if it is actually enabled.
-        #
-        # Note that with saved custom queries, there will be some convergence
-        # between the report module and the query module.
-        from trac.ticket.report import ReportModule
-        if 'REPORT_VIEW' in req.perm and \
-               self.env.is_component_enabled(ReportModule):
-            data['report_href'] = req.href.report()
-            add_ctxtnav(req, _('Available Reports'), req.href.report())
-            add_ctxtnav(req, _('Custom Query'), req.href.query())
-            if query.id:
-                for title, description in self.env.db_query("""
-                        SELECT title, description FROM report WHERE id=%s
-                        """, (query.id,)):
-                    data['report_resource'] = Resource('report', query.id)
-                    data['description'] = description
-        else:
-            data['report_href'] = None
 
-        # Only interact with the batch modify module it it is enabled
-        # TODO: fix this for multiproduct
-        """
-        from trac.ticket.batch import BatchModifyModule
-        if 'TICKET_BATCH_MODIFY' in req.perm and \
-                self.env.is_component_enabled(BatchModifyModule):
-            self.env[BatchModifyModule].add_template_data(req, data, tickets)
-        """
-
-        data.setdefault('report', None)
-        data.setdefault('description', None)
-        data['title'] = title
-
-        data['all_columns'] = query.get_all_columns()
-        # Don't allow the user to remove the id column
-        data['all_columns'].remove('id')
-        data['all_textareas'] = query.get_all_textareas()
-
-        properties = dict((name, dict((key, field[key])
-                                      for key in ('type', 'label', 'options',
-                                                  'optgroups')
-                                      if key in field))
-                          for name, field in data['fields'].iteritems())
-        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')
+class ProductQueryModule(QueryModule):
+    def process_request(self, req, env=None):
+        tmpenv = self.env
+        if isinstance(self.env, ProductEnvironment) and env is not None:
+            self.env = env
+        result = super(ProductQueryModule, self).process_request(req)
+        self.env = tmpenv
+        return result
 
-        return 'query.html', data, None
+trac.ticket.query.QueryModule = ProductQueryModule
+trac.ticket.QueryModule = ProductQueryModule
 
 
 class ProductTicketQueryMacro(TicketQueryMacro):
     """TracQuery macro retrieving results across product boundaries. 
     """
+    @staticmethod
+    def parse_args(content):
+        """Parse macro arguments and translate them to a query string."""
+        clauses = [{}]
+        argv = []
+        kwargs = {}
+        for arg in TicketQueryMacro._comma_splitter.split(content):
+            arg = arg.replace(r'\,', ',')
+            m = re.match(r'\s*[^=]+=', arg)
+            if m:
+                kw = arg[:m.end() - 1].strip()
+                value = arg[m.end():]
+                if kw in ('order', 'max', 'format', 'col', 'product'):
+                    kwargs[kw] = value
+                else:
+                    clauses[-1][kw] = value
+            elif arg.strip() == 'or':
+                clauses.append({})
+            else:
+                argv.append(arg)
+        clauses = filter(None, clauses)
+
+        if len(argv) > 0 and not 'format' in kwargs: # 0.10 compatibility hack
+            kwargs['format'] = argv[0]
+        if 'order' not in kwargs:
+            kwargs['order'] = 'id'
+        if 'max' not in kwargs:
+            kwargs['max'] = '0' # unlimited by default
+
+        format = kwargs.pop('format', 'list').strip().lower()
+        if format in ('list', 'compact'): # we need 'status' and 'summary'
+            if 'col' in kwargs:
+                kwargs['col'] = 'status|summary|' + kwargs['col']
+            else:
+                kwargs['col'] = 'status|summary'
+
+        query_string = '&or&'.join('&'.join('%s=%s' % item
+                                            for item in clause.iteritems())
+                                   for clause in clauses)
+        return query_string, kwargs, format
+
     def expand_macro(self, formatter, name, content):
         req = formatter.req
         query_string, kwargs, format = self.parse_args(content)
@@ -327,7 +220,9 @@ class ProductTicketQueryMacro(TicketQuer
             query_string += '&'
         query_string += '&'.join('%s=%s' % item
                                  for item in kwargs.iteritems())
-        query = ProductQuery.from_string(self.env, query_string)
+
+        env = ProductEnvironment.lookup_global_env(self.env)
+        query = ProductQuery.from_string(env, query_string)
 
         if format == 'count':
             cnt = query.count(req)
@@ -342,7 +237,7 @@ class ProductTicketQueryMacro(TicketQuer
 
             add_stylesheet(req, 'common/css/report.css')
 
-            return Chrome(self.env).render_template(
+            return Chrome(env).render_template(
                 req, 'query_results.html', data, None, fragment=True)
 
         if format == 'progress':
@@ -354,7 +249,7 @@ class ProductTicketQueryMacro(TicketQuer
             add_stylesheet(req, 'common/css/roadmap.css')
 
             def query_href(extra_args, group_value = None):
-                q = Query.from_string(self.env, query_string)
+                q = ProductQuery.from_string(env, query_string)
                 if q.group:
                     extra_args[q.group] = group_value
                     q.group = None
@@ -363,9 +258,9 @@ class ProductTicketQueryMacro(TicketQuer
                 if not q.constraints:
                     q.constraints.append(extra_args)
                 return q.get_href(formatter.context)
-            chrome = Chrome(self.env)
-            tickets = apply_ticket_permissions(self.env, req, tickets)
-            stats_provider = RoadmapModule(self.env).stats_provider
+            chrome = Chrome(env)
+            tickets = apply_ticket_permissions(env, req, tickets)
+            stats_provider = RoadmapModule(env).stats_provider
             by = query.group
             if not by:
                 stat = get_ticket_stats(stats_provider, tickets)
@@ -393,7 +288,7 @@ class ProductTicketQueryMacro(TicketQuer
                     'legend': False,
                 }
 
-            groups = grouped_stats_data(self.env, stats_provider, tickets, by,
+            groups = grouped_stats_data(env, stats_provider, tickets, by,
                                         per_group_stats_data)
             data = {
                 'groups': groups, 'grouped_by': by,
@@ -438,7 +333,7 @@ class ProductTicketQueryMacro(TicketQuer
         def ticket_groups():
             groups = []
             for v, g in groupby(tickets, lambda t: t[query.group]):
-                q = Query.from_string(self.env, query_string)
+                q = ProductQuery.from_string(env, query_string)
                 # produce the hint for the group
                 q.group = q.groupdesc = None
                 order = q.order

Modified: bloodhound/trunk/bloodhound_multiproduct/setup.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/setup.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/setup.py (original)
+++ bloodhound/trunk/bloodhound_multiproduct/setup.py Thu Aug  8 13:19:01 2013
@@ -17,10 +17,22 @@
 #  under the License.
 
 """setup for multi product plugin"""
-import sys
+import sys, codecs
 from pkg_resources import parse_version
 from setuptools import setup
 
+
+# Force UTF-8 for stdout/err if --utf8 option is specified.
+# For some reason python doesn't obey LANG/LC_CTYPE settings
+# if output is not a terminal (e.g. pipes don't work).
+if __name__ == '__main__':
+    for ac,av in enumerate(sys.argv):
+        if av == '--utf8':
+            sys.stdout = codecs.getwriter('utf8')(sys.stdout)
+            sys.stderr = codecs.getwriter('utf8')(sys.stderr)
+            del sys.argv[ac]
+            break
+
 setup(
     name = 'BloodhoundMultiProduct',
     version = '0.7.0',

Modified: 
bloodhound/trunk/bloodhound_multiproduct/tests/product-query-link-tests.txt
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/tests/product-query-link-tests.txt?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/tests/product-query-link-tests.txt 
(original)
+++ bloodhound/trunk/bloodhound_multiproduct/tests/product-query-link-tests.txt 
Thu Aug  8 13:19:01 2013
@@ -52,7 +52,7 @@
 ============================== %(tc_title_prefix)s - ProductTicketQuery macro: 
no results, list form
 Reopened tickets: [[ProductTicketQuery(status=reopened)]]
 
-Reopened tickets: [[ProductTicketQuery(status=reopened, 
product=%(setup_product_name)s)]]
+Reopened tickets: [[ProductTicketQuery(status=reopened, 
product=%(setup_product)s)]]
 ------------------------------
 <p>
 Reopened tickets: <span class="query_no_results">No results</span>
@@ -64,19 +64,19 @@ Reopened tickets: <span class="query_no_
 ============================== %(tc_title_prefix)s - ProductTicketQuery macro: 
no results, count 0
 Reopened tickets: [[ProductTicketQuery(status=reopened, format=count)]]
 
-Reopened tickets: [[ProductTicketQuery(status=reopened, format=count, 
product=%(setup_product_name)s)]]
+Reopened tickets: [[ProductTicketQuery(status=reopened, format=count, 
product=%(setup_product)s)]]
 ------------------------------
 <p>
 Reopened tickets: <span class="query_count" title="0 tickets for which 
status=reopened&amp;max=0&amp;order=id">0</span>
 </p>
 <p>
-Reopened tickets: <span class="query_count" title="0 tickets for which 
status=reopened&amp;product=%(setup_product_name)s&amp;max=0&amp;order=id">0</span>
+Reopened tickets: <span class="query_count" title="0 tickets for which 
status=reopened&amp;max=0&amp;product=%(setup_product)s&amp;order=id">0</span>
 </p>
 ------------------------------
 ============================== %(tc_title_prefix)s - ProductTicketQuery macro: 
no results, compact form
 Reopened tickets: [[ProductTicketQuery(status=reopened, format=compact)]]
 
-Reopened tickets: [[ProductTicketQuery(status=reopened, format=compact, 
product=%(setup_product_name)s)]]
+Reopened tickets: [[ProductTicketQuery(status=reopened, format=compact, 
product=%(setup_product)s)]]
 ------------------------------
 <p>
 Reopened tickets: <span class="query_no_results">No results</span>
@@ -88,7 +88,7 @@ Reopened tickets: <span class="query_no_
 ============================== %(tc_title_prefix)s - ProductTicketQuery macro: 
one result, list form
 New tickets: [[ProductTicketQuery(status=new)]]
 
-New tickets: [[ProductTicketQuery(status=new, product=%(setup_product_name)s)]]
+New tickets: [[ProductTicketQuery(status=new, product=%(setup_product)s)]]
 ------------------------------
 <p>
 New tickets: </p><div><dl class="wiki compact"><dt><a class="new" 
href="%(path_prefix)s/ticket/1" title="This is the summary">#1</a></dt><dd>This 
is the summary</dd></dl></div><p>
@@ -100,19 +100,19 @@ New tickets: </p><div><dl class="wiki co
 ============================== %(tc_title_prefix)s - ProductTicketQuery macro: 
one result, count 1
 New tickets: [[ProductTicketQuery(status=new, format=count)]]
 
-New tickets: [[ProductTicketQuery(status=new, format=count, 
product=%(setup_product_name)s)]]
+New tickets: [[ProductTicketQuery(status=new, format=count, 
product=%(setup_product)s)]]
 ------------------------------
 <p>
 New tickets: <span class="query_count" title="1 tickets for which 
status=new&amp;max=0&amp;order=id">1</span>
 </p>
 <p>
-New tickets: <span class="query_count" title="1 tickets for which 
status=new&amp;product=%(setup_product_name)s&amp;max=0&amp;order=id">1</span>
+New tickets: <span class="query_count" title="1 tickets for which 
status=new&amp;max=0&amp;product=%(setup_product)s&amp;order=id">1</span>
 </p>
 ------------------------------
 ============================== %(tc_title_prefix)s - ProductTicketQuery macro: 
one result, compact form
 New tickets: [[ProductTicketQuery(status=new, format=compact)]]
 
-New tickets: [[ProductTicketQuery(status=new, format=compact, 
product=%(setup_product_name)s)]]
+New tickets: [[ProductTicketQuery(status=new, format=compact, 
product=%(setup_product)s)]]
 ------------------------------
 <p>
 New tickets: <span><a class="new" href="%(path_prefix)s/ticket/1" title="This 
is the summary">#1</a></span>

Modified: bloodhound/trunk/bloodhound_multiproduct/tests/wikisyntax.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_multiproduct/tests/wikisyntax.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_multiproduct/tests/wikisyntax.py (original)
+++ bloodhound/trunk/bloodhound_multiproduct/tests/wikisyntax.py Thu Aug  8 
13:19:01 2013
@@ -65,7 +65,7 @@ def ticket_setup(tc):
                           'status': 'new'})
 
     # FIXME : UGLY ! Should not be explicit for product environments
-    ticket['product'] = (tc.env.product.name
+    ticket['product'] = (tc.env.product.prefix
                          if isinstance(tc.env, ProductEnvironment)
                          else '')
 

Modified: bloodhound/trunk/bloodhound_theme/bhtheme/theme.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_theme/bhtheme/theme.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_theme/bhtheme/theme.py (original)
+++ bloodhound/trunk/bloodhound_theme/bhtheme/theme.py Thu Aug  8 13:19:01 2013
@@ -430,7 +430,8 @@ class BloodhoundTheme(ThemeBase):
 
     def _add_products_general_breadcrumb(self, req, template, data,
                                          content_type, is_active):
-        data['resourcepath_template'] = 'bh_path_general.html'
+        if isinstance(req.perm.env, ProductEnvironment):
+            data['resourcepath_template'] = 'bh_path_general.html'
 
     # INavigationContributor methods
 

Modified: bloodhound/trunk/trac/trac/ticket/tests/query.py
URL: 
http://svn.apache.org/viewvc/bloodhound/trunk/trac/trac/ticket/tests/query.py?rev=1511771&r1=1511770&r2=1511771&view=diff
==============================================================================
--- bloodhound/trunk/trac/trac/ticket/tests/query.py (original)
+++ bloodhound/trunk/trac/trac/ticket/tests/query.py Thu Aug  8 13:19:01 2013
@@ -110,7 +110,7 @@ ORDER BY COALESCE(priority.value,'')='' 
         query = Query(self.env, order='version')
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS 
type,t.status AS status,t.priority AS priority,t.version AS version,t.time AS 
time,t.changetime AS changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.version AS 
version,t.time AS time,t.changetime AS changetime,priority.value AS 
priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
   LEFT OUTER JOIN version ON (version.name=version)
@@ -122,7 +122,7 @@ ORDER BY COALESCE(t.version,'')='',COALE
         query = Query(self.env, order='version', desc=1)
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS 
type,t.status AS status,t.priority AS priority,t.version AS version,t.time AS 
time,t.changetime AS changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.version AS 
version,t.time AS time,t.changetime AS changetime,priority.value AS 
priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
   LEFT OUTER JOIN version ON (version.name=version)
@@ -134,7 +134,7 @@ ORDER BY COALESCE(t.version,'')='' DESC,
         query = Query.from_string(self.env, 'milestone=milestone1', order='id')
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS 
type,t.status AS status,t.priority AS priority,t.component AS component,t.time 
AS time,t.changetime AS changetime,t.milestone AS milestone,priority.value AS 
priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.component AS 
component,t.time AS time,t.changetime AS changetime,t.milestone AS 
milestone,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE ((COALESCE(t.milestone,'')=%s))
@@ -146,7 +146,7 @@ ORDER BY COALESCE(t.id,0)=0,t.id""")
         query = Query(self.env, order='id', group='milestone')
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS 
type,t.status AS status,t.priority AS priority,t.component AS 
component,t.milestone AS milestone,t.time AS time,t.changetime AS 
changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.component AS 
component,t.milestone AS milestone,t.time AS time,t.changetime AS 
changetime,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
   LEFT OUTER JOIN milestone ON (milestone.name=milestone)
@@ -158,7 +158,7 @@ ORDER BY COALESCE(t.milestone,'')='',COA
         query = Query(self.env, order='id', group='milestone', groupdesc=1)
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS 
type,t.status AS status,t.priority AS priority,t.component AS 
component,t.milestone AS milestone,t.time AS time,t.changetime AS 
changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.component AS 
component,t.milestone AS milestone,t.time AS time,t.changetime AS 
changetime,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
   LEFT OUTER JOIN milestone ON (milestone.name=milestone)
@@ -170,7 +170,7 @@ ORDER BY COALESCE(t.milestone,'')='' DES
         query = Query(self.env, group='priority')
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS 
type,t.status AS status,t.milestone AS milestone,t.component AS 
component,t.priority AS priority,t.time AS time,t.changetime AS 
changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.owner AS 
owner,t.type AS type,t.status AS status,t.milestone AS milestone,t.component AS 
component,t.priority AS priority,t.time AS time,t.changetime AS 
changetime,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 ORDER BY COALESCE(priority.value,'')='',%(cast_priority)s,t.id""" % {
@@ -182,7 +182,7 @@ ORDER BY COALESCE(priority.value,'')='',
         query = Query.from_string(self.env, 'milestone!=milestone1', 
order='id')
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.milestone AS milestone,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.time AS 
time,t.changetime AS changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.milestone AS 
milestone,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS 
priority,t.time AS time,t.changetime AS changetime,priority.value AS 
priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE ((COALESCE(t.milestone,'')!=%s))
@@ -357,7 +357,7 @@ ORDER BY COALESCE(t.id,0)=0,t.id""")
         query = Query.from_string(self.env, 'created=2008-08-01..2008-09-01', 
order='id')
         sql, args = query.get_sql(self.req)
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.time AS time,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.changetime AS 
changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.time AS 
time,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS 
priority,t.changetime AS changetime,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE (((%(cast_time)s>=%%s AND %(cast_time)s<%%s)))
@@ -370,7 +370,7 @@ ORDER BY COALESCE(t.id,0)=0,t.id""" % {
         query = Query.from_string(self.env, 'created!=2008-08-01..2008-09-01', 
order='id')
         sql, args = query.get_sql(self.req)
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.time AS time,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.changetime AS 
changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.time AS 
time,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS 
priority,t.changetime AS changetime,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE ((NOT (%(cast_time)s>=%%s AND %(cast_time)s<%%s)))
@@ -383,7 +383,7 @@ ORDER BY COALESCE(t.id,0)=0,t.id""" % {
         query = Query.from_string(self.env, 'created=2008-08-01..', order='id')
         sql, args = query.get_sql(self.req)
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.time AS time,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.changetime AS 
changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.time AS 
time,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS 
priority,t.changetime AS changetime,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE ((%(cast_time)s>=%%s))
@@ -396,7 +396,7 @@ ORDER BY COALESCE(t.id,0)=0,t.id""" % {
         query = Query.from_string(self.env, 'created=..2008-09-01', order='id')
         sql, args = query.get_sql(self.req)
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.time AS time,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.changetime AS 
changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.time AS 
time,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS 
priority,t.changetime AS changetime,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE ((%(cast_time)s<%%s))
@@ -409,7 +409,7 @@ ORDER BY COALESCE(t.id,0)=0,t.id""" % {
         query = Query.from_string(self.env, 'modified=2008-08-01..2008-09-01', 
order='id')
         sql, args = query.get_sql(self.req)
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.changetime AS changetime,t.owner 
AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.time AS 
time,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.changetime AS 
changetime,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS 
priority,t.time AS time,priority.value AS priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE (((%(cast_changetime)s>=%%s AND %(cast_changetime)s<%%s)))
@@ -423,7 +423,7 @@ ORDER BY COALESCE(t.id,0)=0,t.id""" % {
                                   order='id')
         sql, args = query.get_sql()
         self.assertEqualSQL(sql,
-"""SELECT t.id AS id,t.summary AS summary,t.keywords AS keywords,t.owner AS 
owner,t.type AS type,t.status AS status,t.priority AS priority,t.time AS 
time,t.changetime AS changetime,priority.value AS priority_value
+"""SELECT t.product AS product,t.id AS id,t.summary AS summary,t.keywords AS 
keywords,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS 
priority,t.time AS time,t.changetime AS changetime,priority.value AS 
priority_value
 FROM ticket AS t
   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND 
priority.name=priority)
 WHERE (((COALESCE(t.keywords,'') %(like)s AND COALESCE(t.keywords,'') NOT 
%(like)s AND COALESCE(t.keywords,'') %(like)s)))


Reply via email to