Modified: bloodhound/vendor/trac/current/trac/ticket/tests/report.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/report.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/report.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/report.py Fri Nov 14 
11:06:23 2014
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (C) 2006-2013 Edgewall Software
+# Copyright (C) 2006-2014 Edgewall Software
 # All rights reserved.
 #
 # This software is licensed as described in the file COPYING, which
@@ -11,16 +11,24 @@
 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://trac.edgewall.org/log/.
 
+from __future__ import with_statement
+
 import doctest
+from datetime import datetime, timedelta
 
+import unittest
+from StringIO import StringIO
+
+import trac.tests.compat
 from trac.db.mysql_backend import MySQLConnection
+from trac.ticket.model import Ticket
 from trac.ticket.report import ReportModule
-from trac.test import EnvironmentStub, Mock
+from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.util.datefmt import utc
 from trac.web.api import Request, RequestDone
+from trac.web.href import Href
 import trac
 
-import unittest
-from StringIO import StringIO
 
 class MockMySQLConnection(MySQLConnection):
     def __init__(self):
@@ -110,11 +118,409 @@ class ReportTestCase(unittest.TestCase):
                          'type=r%C3%A9sum%C3%A9&report=' + str(id),
                          headers_sent['Location'])
 
+    def test_quoted_id_with_var(self):
+        req = Mock(base_path='', chrome={}, args={}, session={},
+                   abs_href=Href('/'), href=Href('/'), locale='',
+                   perm=MockPerm(), authname=None, tz=None)
+        db = self.env.get_read_db()
+        name = """%s"`'%%%?"""
+        sql = 'SELECT 1 AS %s, $USER AS user' % db.quote(name)
+        rv = self.report_module.execute_paginated_report(req, db, 1, sql,
+                                                         {'USER': 'joe'})
+        self.assertEqual(5, len(rv), repr(rv))
+        cols, results, num_items, missing_args, limit_offset = rv
+        self.assertEqual([name, 'user'], cols)
+        self.assertEqual([(1, 'joe')], results)
+        self.assertEqual([], missing_args)
+        self.assertEqual(None, limit_offset)
+
+
+class ExecuteReportTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.env = EnvironmentStub(default_data=True)
+        self.req = Mock(base_path='', chrome={}, args={}, session={},
+                        abs_href=Href('/'), href=Href('/'), locale='',
+                        perm=MockPerm(), authname=None, tz=None)
+        self.report_module = ReportModule(self.env)
+
+    def tearDown(self):
+        self.env.reset_db()
+
+    def _insert_ticket(self, when=None, **kwargs):
+        ticket = Ticket(self.env)
+        for name, value in kwargs.iteritems():
+            ticket[name] = value
+        ticket['status'] = 'new'
+        ticket.insert(when=when)
+        return ticket
+
+    def _save_ticket(self, ticket, author=None, comment=None, when=None,
+                     **kwargs):
+        if when is None:
+            when = ticket['changetime'] + timedelta(microseconds=1)
+        for name, value in kwargs.iteritems():
+            ticket[name] = value
+        return ticket.save_changes(author=author, comment=comment, when=when)
+
+    def _execute_report(self, id, args=None):
+        mod = self.report_module
+        req = self.req
+        with self.env.db_query as db:
+            title, description, sql = mod.get_report(id)
+            return mod.execute_paginated_report(req, db, id, sql, args or {})
+
+    def _generate_tickets(self, columns, data, attrs):
+        with self.env.db_transaction as db:
+            tickets = []
+            when = datetime(2014, 1, 1, 0, 0, 0, 0, utc)
+            for idx, line in enumerate(data.splitlines()):
+                line = line.strip()
+                if not line or line.startswith('#'):
+                    continue
+                values = line.split()
+                assert len(columns) == len(values), 'Line %d' % (idx + 1)
+                summary = ' '.join(values)
+                values = map(lambda v: None if v == 'None' else v, values)
+                d = attrs.copy()
+                d['summary'] = summary
+                d.update(zip(columns, values))
+
+                status = None
+                if 'status' in d:
+                    status = d.pop('status')
+                ticket = self._insert_ticket(when=when, status='new', **d)
+                if status != 'new':
+                    self._save_ticket(ticket, status=status,
+                                      when=when + timedelta(microseconds=1))
+                tickets.append(ticket)
+                when += timedelta(seconds=1)
+            return tickets
+
+    REPORT_1_DATA = """\
+        # status    priority
+        new         minor
+        new         major
+        new         critical
+        closed      minor
+        closed      major
+        closed      critical"""
+
+    def test_report_1_active_tickets(self):
+        attrs = dict(reporter='joe', component='component1', version='1.0',
+                     milestone='milestone1', type='defect', owner='joe')
+        self._generate_tickets(('status', 'priority'), self.REPORT_1_DATA,
+                               attrs)
+
+        rv = self._execute_report(1)
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        idx_summary = cols.index('summary')
+        self.assertEqual(['new critical',
+                          'new major',
+                          'new minor'],
+                         [r[idx_summary] for r in results])
+        idx_color = cols.index('__color__')
+        self.assertEqual(set(('2', '3', '4')),
+                         set(r[idx_color] for r in results))
+
+    REPORT_2_DATA = """\
+        # status    version     priority
+        new         2.0         minor
+        new         2.0         critical
+        new         1.0         minor
+        new         1.0         critical
+        new         None        minor
+        new         None        critical
+        closed      2.0         minor
+        closed      2.0         critical
+        closed      1.0         minor
+        closed      1.0         critical
+        closed      None        minor
+        closed      None        critical"""
+
+    def test_report_2_active_tickets_by_version(self):
+        attrs = dict(reporter='joe', component='component1',
+                     milestone='milestone1', type='defect', owner='joe')
+        self._generate_tickets(('status', 'version', 'priority'),
+                                self.REPORT_2_DATA, attrs)
+
+        rv = self._execute_report(2)
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        idx_summary = cols.index('summary')
+        self.assertEqual(['new 1.0 critical',
+                          'new 1.0 minor',
+                          'new 2.0 critical',
+                          'new 2.0 minor',
+                          'new None critical',
+                          'new None minor'],
+                         [r[idx_summary] for r in results])
+        idx_color = cols.index('__color__')
+        self.assertEqual(set(('2', '4')),
+                         set(r[idx_color] for r in results))
+        idx_group = cols.index('__group__')
+        self.assertEqual(set(('1.0', '2.0', None)),
+                         set(r[idx_group] for r in results))
+
+    REPORT_3_DATA = """\
+        # status    milestone   priority
+        new         milestone3  minor
+        new         milestone3  major
+        new         milestone1  minor
+        new         milestone1  major
+        new         None        minor
+        new         None        major
+        closed      milestone3  minor
+        closed      milestone3  major
+        closed      milestone1  minor
+        closed      milestone1  major
+        closed      None        minor
+        closed      None        major"""
+
+    def test_report_3_active_tickets_by_milestone(self):
+        attrs = dict(reporter='joe', component='component1', version='1.0',
+                     type='defect', owner='joe')
+        self._generate_tickets(('status', 'milestone', 'priority'),
+                                self.REPORT_3_DATA, attrs)
+
+        rv = self._execute_report(3)
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        idx_summary = cols.index('summary')
+        self.assertEqual(['new milestone1 major',
+                          'new milestone1 minor',
+                          'new milestone3 major',
+                          'new milestone3 minor',
+                          'new None major',
+                          'new None minor'],
+                         [r[idx_summary] for r in results])
+        idx_color = cols.index('__color__')
+        self.assertEqual(set(('3', '4')),
+                         set(r[idx_color] for r in results))
+        idx_group = cols.index('__group__')
+        self.assertEqual(set(('Milestone milestone1', 'Milestone milestone3',
+                              None)),
+                         set(r[idx_group] for r in results))
+
+    REPORT_4_DATA = """\
+        # status    owner   priority
+        new         john    trivial
+        new         john    blocker
+        new         jack    trivial
+        new         jack    blocker
+        new         foo     trivial
+        new         foo     blocker
+        accepted    john    trivial
+        accepted    john    blocker
+        accepted    jack    trivial
+        accepted    jack    blocker
+        accepted    foo     trivial
+        accepted    foo     blocker
+        closed      john    trivial
+        closed      john    blocker
+        closed      jack    trivial
+        closed      jack    blocker
+        closed      foo     trivial
+        closed      foo     blocker"""
+
+    def _test_active_tickets_by_owner(self, report_id, full_description=False):
+        attrs = dict(reporter='joe', component='component1',
+                     milestone='milestone1', version='1.0', type='defect')
+        self._generate_tickets(('status', 'owner', 'priority'),
+                                self.REPORT_4_DATA, attrs)
+
+        rv = self._execute_report(report_id)
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        idx_summary = cols.index('summary')
+        self.assertEqual(['accepted foo blocker',
+                          'accepted foo trivial',
+                          'accepted jack blocker',
+                          'accepted jack trivial',
+                          'accepted john blocker',
+                          'accepted john trivial'],
+                         [r[idx_summary] for r in results])
+        idx_color = cols.index('__color__')
+        self.assertEqual(set(('1', '5')),
+                         set(r[idx_color] for r in results))
+        idx_group = cols.index('__group__')
+        self.assertEqual(set(('jack', 'john', 'foo')),
+                         set(r[idx_group] for r in results))
+        if full_description:
+            self.assertNotIn('_description', cols)
+            self.assertIn('_description_', cols)
+        else:
+            self.assertNotIn('_description_', cols)
+            self.assertIn('_description', cols)
+
+    def test_report_4_active_tickets_by_owner(self):
+        self._test_active_tickets_by_owner(4, full_description=False)
+
+    def test_report_5_active_tickets_by_owner_full_description(self):
+        self._test_active_tickets_by_owner(5, full_description=True)
+
+    REPORT_6_DATA = """\
+        # status    milestone  priority owner
+        new         milestone4 trivial  john
+        new         milestone4 critical jack
+        new         milestone2 trivial  jack
+        new         milestone2 critical john
+        new         None       trivial  john
+        new         None       critical jack
+        closed      milestone4 trivial  jack
+        closed      milestone4 critical john
+        closed      milestone2 trivial  john
+        closed      milestone2 critical jack
+        closed      None       trivial  jack
+        closed      None       critical john"""
+
+    def test_report_6_all_tickets_by_milestone(self):
+        attrs = dict(reporter='joe', component='component1', version='1.0',
+                     type='defect')
+        self._generate_tickets(('status', 'milestone', 'priority', 'owner'),
+                                self.REPORT_6_DATA, attrs)
+
+        rv = self._execute_report(6, {'USER': 'john'})
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        idx_summary = cols.index('summary')
+        self.assertEqual(['new milestone4 critical jack',
+                          'new milestone4 trivial john',
+                          'closed milestone4 critical john',
+                          'closed milestone4 trivial jack',
+                          'new milestone2 critical john',
+                          'new milestone2 trivial jack',
+                          'closed milestone2 critical jack',
+                          'closed milestone2 trivial john',
+                          'new None critical jack',
+                          'new None trivial john',
+                          'closed None critical john',
+                          'closed None trivial jack'],
+                         [r[idx_summary] for r in results])
+        idx_style = cols.index('__style__')
+        self.assertEqual('color: #777; background: #ddd; border-color: #ccc;',
+                         results[2][idx_style])  # closed and owned
+        self.assertEqual('color: #777; background: #ddd; border-color: #ccc;',
+                         results[3][idx_style])  # closed and not owned
+        self.assertEqual('font-weight: bold',
+                         results[1][idx_style])  # not closed and owned
+        self.assertEqual(None,
+                         results[0][idx_style])  # not closed and not owned
+        idx_color = cols.index('__color__')
+        self.assertEqual(set(('2', '5')),
+                         set(r[idx_color] for r in results))
+        idx_group = cols.index('__group__')
+        self.assertEqual(set(('milestone2', 'milestone4', None)),
+                         set(r[idx_group] for r in results))
+
+    REPORT_7_DATA = """\
+        # status    owner   reporter    priority
+        accepted    john    foo         minor
+        accepted    john    foo         critical
+        accepted    foo     foo         major
+        new         john    foo         minor
+        new         john    foo         blocker
+        new         foo     foo         major
+        closed      john    foo         major
+        closed      foo     foo         major
+        new         foo     foo         major
+        new         foo     john        trivial
+        new         foo     john        major
+        closed      foo     foo         major
+        closed      foo     john        major
+        new         foo     bar         major
+        new         bar     foo         major"""
+
+    def test_report_7_my_tickets(self):
+        attrs = dict(component='component1', milestone='milestone1',
+                     version='1.0', type='defect')
+        tickets = self._generate_tickets(
+            ('status', 'owner', 'reporter', 'priority'), self.REPORT_7_DATA,
+            attrs)
+
+        rv = self._execute_report(7, {'USER': 'john'})
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        idx_summary = cols.index('summary')
+        self.assertEqual(['accepted john foo critical',
+                          'accepted john foo minor',
+                          'new john foo blocker',
+                          'new john foo minor',
+                          'new foo john major',
+                          'new foo john trivial'],
+                         [r[idx_summary] for r in results])
+        idx_group = cols.index('__group__')
+        self.assertEqual(set(('Accepted', 'Owned', 'Reported')),
+                         set(r[idx_group] for r in results))
+
+        self._save_ticket(tickets[-1], author='john', comment='commented')
+        rv = self._execute_report(7, {'USER': 'john'})
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        self.assertEqual(7, len(results))
+        self.assertEqual('new bar foo major', results[-1][idx_summary])
+        self.assertEqual(set(('Accepted', 'Owned', 'Reported', 'Commented')),
+                         set(r[idx_group] for r in results))
+
+        rv = self._execute_report(7, {'USER': 'blah <b...@example.org>'})
+        cols, results, num_items, missing_args, limit_offset = rv
+        self.assertEqual(0, len(results))
+
+        self._save_ticket(tickets[-1], author='blah <b...@example.org>',
+                          comment='from anonymous')
+        rv = self._execute_report(7, {'USER': 'blah <b...@example.org>'})
+        cols, results, num_items, missing_args, limit_offset = rv
+        self.assertEqual(1, len(results))
+        self.assertEqual('new bar foo major', results[0][idx_summary])
+        self.assertEqual('Commented', results[0][idx_group])
+
+    REPORT_8_DATA = """\
+        # status    owner   priority
+        new         foo     minor
+        new         foo     critical
+        new         john    minor
+        new         john    critical
+        closed      john    major
+        closed      foo     major"""
+
+    def test_report_8_active_tickets_mine_first(self):
+        attrs = dict(component='component1', milestone='milestone1',
+                     version='1.0', type='defect')
+        tickets = self._generate_tickets(('status', 'owner', 'priority'),
+                                         self.REPORT_8_DATA, attrs)
+
+        rv = self._execute_report(8, {'USER': 'john'})
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        idx_summary = cols.index('summary')
+        self.assertEqual(['new john critical',
+                          'new john minor',
+                          'new foo critical',
+                          'new foo minor'],
+                         [r[idx_summary] for r in results])
+        idx_group = cols.index('__group__')
+        self.assertEqual('My Tickets', results[1][idx_group])
+        self.assertEqual('Active Tickets', results[2][idx_group])
+
+        rv = self._execute_report(8, {'USER': 'anonymous'})
+        cols, results, num_items, missing_args, limit_offset = rv
+
+        self.assertEqual(['new foo critical',
+                          'new john critical',
+                          'new foo minor',
+                          'new john minor'],
+                         [r[idx_summary] for r in results])
+        idx_group = cols.index('__group__')
+        self.assertEqual(['Active Tickets'],
+                         sorted(set(r[idx_group] for r in results)))
+
 
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocTestSuite(trac.ticket.report))
     suite.addTest(unittest.makeSuite(ReportTestCase))
+    suite.addTest(unittest.makeSuite(ExecuteReportTestCase))
     return suite
 
 if __name__ == '__main__':

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/roadmap.py Fri Nov 14 
11:06:23 2014
@@ -11,11 +11,14 @@
 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://trac.edgewall.org/log/.
 
-from trac.test import EnvironmentStub
-from trac.ticket.roadmap import *
+import unittest
+
 from trac.core import ComponentManager
+from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.tests.contentgen import random_sentence
+from trac.ticket.roadmap import *
+from trac.web.tests.api import RequestHandlerPermissionsTestCaseBase
 
-import unittest
 
 class TicketGroupStatsTestCase(unittest.TestCase):
 
@@ -137,14 +140,88 @@ class DefaultTicketGroupStatsProviderTes
         self.assertEqual(67, open['percent'], 'open percent incorrect')
 
 
+class MilestoneModuleTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.env = EnvironmentStub()
+        self.req = Mock(href=self.env.href, perm=MockPerm())
+        self.mmodule = MilestoneModule(self.env)
+        self.terms = ['MilestoneAlpha', 'MilestoneBeta', 'MilestoneGamma']
+        for term in self.terms + [' '.join(self.terms)]:
+            m = Milestone(self.env)
+            m.name = term
+            m.due = datetime.now(utc)
+            m.description = random_sentence()
+            m.insert()
+
+    def tearDown(self):
+        self.env.reset_db()
+
+    def test_get_search_filters(self):
+        filters = self.mmodule.get_search_filters(self.req)
+        filters = list(filters)
+        self.assertEqual(1, len(filters))
+        self.assertEqual(2, len(filters[0]))
+        self.assertEqual('milestone', filters[0][0])
+        self.assertEqual('Milestones', filters[0][1])
+
+    def test_get_search_results_milestone_not_in_filters(self):
+        results = self.mmodule.get_search_results(self.req, self.terms, [])
+        self.assertEqual([], list(results))
+
+    def test_get_search_results_matches_all_terms(self):
+        milestone = Milestone(self.env, ' '.join(self.terms))
+        results = self.mmodule.get_search_results(self.req, self.terms,
+                                                  ['milestone'])
+        results = list(results)
+        self.assertEqual(1, len(results))
+        self.assertEqual(5, len(results[0]))
+        self.assertEqual('/trac.cgi/milestone/' +
+                         milestone.name.replace(' ', '%20'),
+                         results[0][0])
+        self.assertEqual('Milestone ' + milestone.name, results[0][1])
+        self.assertEqual(milestone.due, results[0][2])
+        self.assertEqual('', results[0][3])
+        self.assertEqual(milestone.description, results[0][4])
+
+
+class 
MilestoneModulePermissionsTestCase(RequestHandlerPermissionsTestCaseBase):
+
+    def setUp(self):
+        super(MilestoneModulePermissionsTestCase, self).setUp(MilestoneModule)
+
+    def test_milestone_notfound_with_milestone_create(self):
+        self.grant_perm('anonymous', 'MILESTONE_VIEW')
+        self.grant_perm('anonymous', 'MILESTONE_CREATE')
+
+        req = self.create_request(path_info='/milestone/milestone5')
+        res = self.process_request(req)
+
+        self.assertEqual('milestone_edit.html', res[0])
+        self.assertEqual('milestone5', res[1]['milestone'].name)
+        self.assertEqual("Milestone milestone5 does not exist. You can"
+                         " create it here.", req.chrome['notices'][0])
+
+    def test_milestone_notfound_without_milestone_create(self):
+        self.grant_perm('anonymous', 'MILESTONE_VIEW')
+
+        req = self.create_request(path_info='/milestone/milestone5')
+
+        self.assertRaises(ResourceNotFound, self.process_request, req)
+
+
 def in_tlist(ticket, list):
     return len([t for t in list if t['id'] == ticket.id]) > 0
 
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(TicketGroupStatsTestCase))
     suite.addTest(unittest.makeSuite(DefaultTicketGroupStatsProviderTestCase))
+    suite.addTest(unittest.makeSuite(MilestoneModuleTestCase))
+    suite.addTest(unittest.makeSuite(MilestoneModulePermissionsTestCase))
     return suite
 
+
 if __name__ == '__main__':
     unittest.main(defaultTest='suite')

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/wikisyntax.py Fri Nov 14 
11:06:23 2014
@@ -169,6 +169,14 @@ REPORT_TEST_CASES = u"""
 </p>
 ------------------------------
 &amp;#1; &amp;#23;
+============================== report link with non-digits
+report:blah
+------------------------------
+<p>
+<a class="missing report" title="report does not exist">report:blah</a>
+</p>
+------------------------------
+<a class="missing report" title="report does not exist">report:blah</a>
 ============================== InterTrac for reports
 trac:report:1
 [trac:report:1 Trac r1]
@@ -347,7 +355,7 @@ New tickets: </p><div><dl class="wiki co
 New tickets: [[TicketQuery(status=new, format=count)]]
 ------------------------------
 <p>
-New tickets: <span class="query_count" title="1 tickets for which 
status=new&amp;max=0&amp;order=id">1</span>
+New tickets: <span class="query_count" title="1 ticket for which 
status=new&amp;max=0&amp;order=id">1</span>
 </p>
 ------------------------------
 ============================== TicketQuery macro: one result, compact form

Modified: bloodhound/vendor/trac/current/trac/ticket/web_ui.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/web_ui.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/web_ui.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/web_ui.py Fri Nov 14 11:06:23 
2014
@@ -38,21 +38,18 @@ from trac.ticket.api import TicketSystem
 from trac.ticket.model import Milestone, Ticket, group_milestones
 from trac.ticket.notification import TicketNotifyEmail
 from trac.timeline.api import ITimelineEventProvider
-from trac.util import as_bool, as_int, get_reporter_id
+from trac.util import as_bool, as_int, get_reporter_id, lazy
 from trac.util.datefmt import (
     format_datetime, from_utimestamp, to_utimestamp, utc
 )
 from trac.util.html import to_fragment
 from trac.util.text import (
-    exception_to_unicode, empty, obfuscate_email_address, shorten_line,
-    to_unicode
+    exception_to_unicode, empty, obfuscate_email_address, shorten_line
 )
 from trac.util.presentation import separated
-from trac.util.translation import _, tag_, tagn_, N_, gettext, ngettext
+from trac.util.translation import _, tag_, tagn_, N_, ngettext
 from trac.versioncontrol.diff import get_diff_options, diff_blocks
-from trac.web import (
-    IRequestHandler, RequestDone, arg_list_to_args, parse_arg_list
-)
+from trac.web import IRequestHandler, arg_list_to_args, parse_arg_list
 from trac.web.chrome import (
     Chrome, INavigationContributor, ITemplateProvider,
     add_ctxtnav, add_link, add_notice, add_script, add_script_data,
@@ -126,11 +123,11 @@ class TicketModule(Component):
             return getattr(TicketSystem(self.env), name)
         raise AttributeError("TicketModule has no attribute '%s'" % name)
 
-    @property
+    @lazy
     def must_preserve_newlines(self):
         preserve_newlines = self.preserve_newlines
         if preserve_newlines == 'default':
-            preserve_newlines = self.env.get_version(initial=True) >= 21 # 0.11
+            preserve_newlines = self.env.database_initial_version >= 21 # 0.11
         return as_bool(preserve_newlines)
 
     # IContentConverter methods
@@ -1133,7 +1130,7 @@ class TicketModule(Component):
         for f in fields:
             name = f['name']
             value = ticket.values.get(name, '')
-            if name in ('cc', 'reporter'):
+            if name in ('cc', 'owner', 'reporter'):
                 value = Chrome(self.env).format_emails(context, value, ' ')
             elif name in ticket.time_fields:
                 value = format_datetime(value, '%Y-%m-%d %H:%M:%S',
@@ -1244,8 +1241,9 @@ class TicketModule(Component):
                 value = ticket[name]
                 if value:
                     if value not in field['options']:
-                        add_warning(req, '"%s" is not a valid value for '
-                                    'the %s field.' % (value, name))
+                        add_warning(req, _('"%(value)s" is not a valid value '
+                                           'for the %(name)s field.',
+                                           value=value, name=name))
                         valid = False
                 elif not field.get('optional', False):
                     add_warning(req, _("field %(name)s must be set",
@@ -1288,9 +1286,9 @@ class TicketModule(Component):
             for field, message in manipulator.validate_ticket(req, ticket):
                 valid = False
                 if field:
-                    add_warning(req, _("The ticket field '%(field)s' is "
-                                       "invalid: %(message)s",
-                                       field=field, message=message))
+                    add_warning(req, tag_("The ticket field '%(field)s'"
+                                          " is invalid: %(message)s",
+                                          field=field, message=message))
                 else:
                     add_warning(req, message)
         return valid

Modified: bloodhound/vendor/trac/current/trac/timeline/templates/timeline.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/templates/timeline.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/templates/timeline.html 
(original)
+++ bloodhound/vendor/trac/current/trac/timeline/templates/timeline.html Fri 
Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: bloodhound/vendor/trac/current/trac/timeline/web_ui.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/timeline/web_ui.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/timeline/web_ui.py (original)
+++ bloodhound/vendor/trac/current/trac/timeline/web_ui.py Fri Nov 14 11:06:23 
2014
@@ -395,7 +395,7 @@ class TimelineModule(Component):
                        name=tag.tt(ep.__class__.__name__),
                        kinds=', '.join('"%s"' % ep_kinds[f] for f in
                                        current_filters & ep_filters)),
-                  tag.b(exception_to_unicode(exc)), class_='message'),
+                  tag.strong(exception_to_unicode(exc)), class_='message'),
             tag.p(tag_("You may want to see the %(other_events)s from the "
                        "Timeline or notify your Trac administrator about the "
                        "error (detailed information was written to the log).",

Modified: bloodhound/vendor/trac/current/trac/util/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/__init__.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/__init__.py (original)
+++ bloodhound/vendor/trac/current/trac/util/__init__.py Fri Nov 14 11:06:23 
2014
@@ -20,6 +20,7 @@
 from __future__ import with_statement
 
 import errno
+import functools
 import inspect
 from itertools import izip, tee
 import locale
@@ -708,11 +709,33 @@ def get_pkginfo(dist):
     """
     import types
     if isinstance(dist, types.ModuleType):
+        def has_resource(dist, resource_name):
+            if dist.location.endswith('.egg'):  # installed by easy_install
+                return dist.has_resource(resource_name)
+            if dist.has_metadata('installed-files.txt'):  # installed by pip
+                resource_name = os.path.normpath('../' + resource_name)
+                return any(resource_name == os.path.normpath(name)
+                           for name
+                           in dist.get_metadata_lines('installed-files.txt'))
+            if dist.has_metadata('SOURCES.txt'):
+                resource_name = os.path.normpath(resource_name)
+                return any(resource_name == os.path.normpath(name)
+                           for name in dist.get_metadata_lines('SOURCES.txt'))
+            toplevel = resource_name.split('/')[0]
+            if dist.has_metadata('top_level.txt'):
+                return toplevel in dist.get_metadata_lines('top_level.txt')
+            return dist.key == toplevel.lower()
         module = dist
         module_path = get_module_path(module)
+        resource_name = module.__name__.replace('.', '/')
+        if os.path.basename(module.__file__) in ('__init__.py', '__init__.pyc',
+                                                 '__init__.pyo'):
+            resource_name += '/__init__.py'
+        else:
+            resource_name += '.py'
         for dist in find_distributions(module_path, only=True):
             if os.path.isfile(module_path) or \
-                   dist.key == module.__name__.lower():
+                    has_resource(dist, resource_name):
                 break
         else:
             return {}
@@ -736,6 +759,20 @@ def get_pkginfo(dist):
             info[normalize(attr)] = err
     return info
 
+
+def warn_setuptools_issue(out=None):
+    if not out:
+        out = sys.stderr
+    import setuptools
+    from pkg_resources import parse_version as parse
+    if parse('5.4') <= parse(setuptools.__version__) < parse('5.7') and \
+            not os.environ.get('PKG_RESOURCES_CACHE_ZIP_MANIFESTS'):
+        out.write("Warning: Detected setuptools version %s. The environment "
+                  "variable 'PKG_RESOURCES_CACHE_ZIP_MANIFESTS' must be set "
+                  "to avoid significant performance degradation.\n"
+                  % setuptools.__version__)
+
+
 # -- crypto utils
 
 try:
@@ -1080,18 +1117,30 @@ def to_ranges(revs):
 
 
 class lazy(object):
-    """A lazily-evaluated attribute"""
+    """A lazily-evaluated attribute.
+
+    :since: 1.0
+    """
 
     def __init__(self, fn):
         self.fn = fn
+        functools.update_wrapper(self, fn)
 
     def __get__(self, instance, owner):
         if instance is None:
             return self
+        if self.fn.__name__ in instance.__dict__:
+            return instance.__dict__[self.fn.__name__]
         result = self.fn(instance)
-        setattr(instance, self.fn.__name__, result)
+        instance.__dict__[self.fn.__name__] = result
         return result
 
+    def __set__(self, instance, value):
+        instance.__dict__[self.fn.__name__] = value
+
+    def __delete__(self, instance):
+        del instance.__dict__[self.fn.__name__]
+
 
 # -- algorithmic utilities
 

Modified: bloodhound/vendor/trac/current/trac/util/daemon.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/daemon.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/daemon.py (original)
+++ bloodhound/vendor/trac/current/trac/util/daemon.py Fri Nov 14 11:06:23 2014
@@ -19,6 +19,7 @@ import os
 import signal
 import sys
 
+
 def daemonize(pidfile=None, progname=None, stdin='/dev/null',
               stdout='/dev/null', stderr='/dev/null', umask=022):
     """Fork a daemon process."""

Modified: bloodhound/vendor/trac/current/trac/util/datefmt.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/datefmt.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/datefmt.py (original)
+++ bloodhound/vendor/trac/current/trac/util/datefmt.py Fri Nov 14 11:06:23 2014
@@ -55,12 +55,14 @@ def to_datetime(t, tzinfo=None):
 
     ``t`` is converted using the following rules:
 
-     - If ``t`` is already a `datetime` object,
-       - if it is timezone-"naive", it is localized to ``tzinfo``
-       - if it is already timezone-aware, ``t`` is mapped to the given
-         timezone (`datetime.datetime.astimezone`)
-     - If ``t`` is None, the current time will be used.
-     - If ``t`` is a number, it is interpreted as a timestamp.
+    * If ``t`` is already a `datetime` object,
+
+     * if it is timezone-"naive", it is localized to ``tzinfo``
+     * if it is already timezone-aware, ``t`` is mapped to the given
+       timezone (`datetime.datetime.astimezone`)
+
+    * If ``t`` is None, the current time will be used.
+    * If ``t`` is a number, it is interpreted as a timestamp.
 
     Any other input will trigger a `TypeError`.
 
@@ -804,67 +806,103 @@ class LocalTimezone(tzinfo):
 
     @classmethod
     def _initialize(cls):
-        cls._std_tz = cls(False)
         cls._std_offset = timedelta(seconds=-time.timezone)
+        cls._std_tz = cls(cls._std_offset)
         if time.daylight:
-            cls._dst_tz = cls(True)
             cls._dst_offset = timedelta(seconds=-time.altzone)
+            cls._dst_tz = cls(cls._dst_offset)
         else:
-            cls._dst_tz = cls._std_tz
             cls._dst_offset = cls._std_offset
+            cls._dst_tz = cls._std_tz
         cls._dst_diff = cls._dst_offset - cls._std_offset
 
-    def __init__(self, is_dst=None):
-        self.is_dst = is_dst
+    def __init__(self, offset=None):
+        self._offset = offset
 
     def __str__(self):
-        offset = self.utcoffset(datetime.now())
-        secs = offset.days * 3600 * 24 + offset.seconds
-        hours, rem = divmod(abs(secs), 3600)
-        return 'UTC%c%02d:%02d' % ('-' if secs < 0 else '+', hours, rem / 60)
+        return self._tzname_offset(self.utcoffset(datetime.now()))
 
     def __repr__(self):
-        if self.is_dst is None:
+        if self._offset is None:
             return '<LocalTimezone "%s" %s "%s" %s>' % \
                    (time.tzname[False], self._std_offset,
                     time.tzname[True], self._dst_offset)
-        if self.is_dst:
-            offset = self._dst_offset
+        return '<LocalTimezone "%s" %s>' % (self._tzname(), self._offset)
+
+    def _tzname(self):
+        if self is self._std_tz:
+            return time.tzname[False]
+        elif self is self._dst_tz:
+            return time.tzname[True]
+        elif self._offset is not None:
+            return self._tzname_offset(self._offset)
         else:
-            offset = self._std_offset
-        return '<LocalTimezone "%s" %s>' % (time.tzname[self.is_dst], offset)
+            return '%s, %s' % time.tzname
 
-    def _is_dst(self, dt, is_dst=False):
-        if self.is_dst is not None:
-            return self.is_dst
+    def _tzname_offset(self, offset):
+        secs = offset.days * 3600 * 24 + offset.seconds
+        hours, rem = divmod(abs(secs), 3600)
+        return 'UTC%c%02d:%02d' % ('+-'[secs < 0], hours, rem / 60)
 
-        tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
-              dt.weekday(), 0)
-        try:
-            std_tt = time.localtime(time.mktime(tt + (0,)))
-            dst_tt = time.localtime(time.mktime(tt + (1,)))
-        except (ValueError, OverflowError):
-            return False
-
-        std_correct = std_tt.tm_isdst == 0
-        dst_correct = dst_tt.tm_isdst == 1
-        if std_correct is dst_correct:
-            if is_dst is None:
-                if std_correct is True:
-                    raise ValueError('Ambiguous time "%s"' % dt)
-                if std_correct is False:
-                    raise ValueError('Non existent time "%s"' % dt)
-            return is_dst
-        if std_correct:
-            return False
-        if dst_correct:
+    def _tzinfo(self, dt, is_dst=False):
+        tzinfo = dt.tzinfo
+        if isinstance(tzinfo, LocalTimezone) and tzinfo._offset is not None:
+            return tzinfo
+
+        base_tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
+                   dt.weekday(), 0)
+        local_tt = [None, None]
+        for idx in (0, 1):
+            try:
+                local_tt[idx] = time.localtime(time.mktime(base_tt + (idx,)))
+            except (ValueError, OverflowError):
+                pass
+        if local_tt[0] is local_tt[1] is None:
+            return self._std_tz
+
+        std_correct = local_tt[0] and local_tt[0].tm_isdst == 0
+        dst_correct = local_tt[1] and local_tt[1].tm_isdst == 1
+        if is_dst is None and std_correct is dst_correct:
+            if std_correct:
+                raise ValueError('Ambiguous time "%s"' % dt)
+            if not std_correct:
+                raise ValueError('Non existent time "%s"' % dt)
+        tt = None
+        if std_correct is dst_correct is True:
+            tt = local_tt[bool(is_dst)]
+        elif std_correct is True:
+            tt = local_tt[0]
+        elif dst_correct is True:
+            tt = local_tt[1]
+        if tt:
+            utc_ts = to_timestamp(datetime(tzinfo=utc, *tt[:6]))
+            tz_offset = timedelta(seconds=utc_ts - time.mktime(tt))
+        else:
+            dt = dt.replace(tzinfo=utc)
+            utc_ts = to_timestamp(dt)
+            dt -= timedelta(hours=6)
+            tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
+                  dt.weekday(), 0, -1)
+            tz_offset = timedelta(seconds=utc_ts - time.mktime(tt) - 6 * 3600)
+
+        # if UTC offset doesn't match timezone offset, create a
+        # LocalTimezone instance with the UTC offset (#11563)
+        if tz_offset == self._std_offset:
+            tz = self._std_tz
+        elif tz_offset == self._dst_offset:
+            tz = self._dst_tz
+        else:
+            tz = LocalTimezone(tz_offset)
+        return tz
+
+    def _is_dst(self, dt, is_dst=False):
+        tz = self._tzinfo(dt, is_dst)
+        if tz is self._dst_tz:
             return True
+        return False
 
     def utcoffset(self, dt):
-        if self._is_dst(dt):
-            return self._dst_offset
-        else:
-            return self._std_offset
+        return self._tzinfo(dt)._offset
 
     def dst(self, dt):
         if self._is_dst(dt):
@@ -873,16 +911,12 @@ class LocalTimezone(tzinfo):
             return _zero
 
     def tzname(self, dt):
-        return time.tzname[self._is_dst(dt)]
+        return self._tzinfo(dt)._tzname()
 
     def localize(self, dt, is_dst=False):
         if dt.tzinfo is not None:
             raise ValueError('Not naive datetime (tzinfo is already set)')
-        if self._is_dst(dt, is_dst):
-            tz = self._dst_tz
-        else:
-            tz = self._std_tz
-        return dt.replace(tzinfo=tz)
+        return dt.replace(tzinfo=self._tzinfo(dt, is_dst))
 
     def normalize(self, dt, is_dst=False):
         if dt.tzinfo is None:
@@ -894,15 +928,22 @@ class LocalTimezone(tzinfo):
     def fromutc(self, dt):
         if dt.tzinfo is None or dt.tzinfo is not self:
             raise ValueError('fromutc: dt.tzinfo is not self')
+        dt = dt.replace(tzinfo=utc)
         try:
-            tt = time.localtime(to_timestamp(dt.replace(tzinfo=utc)))
+            tt = time.localtime(to_timestamp(dt))
         except ValueError:
             return dt.replace(tzinfo=self._std_tz) + self._std_offset
-        if tt.tm_isdst > 0:
+        # if UTC offset from localtime() doesn't match timezone offset,
+        # create a LocalTimezone instance with the UTC offset (#11563)
+        new_dt = datetime(*(tt[:6] + (dt.microsecond, utc)))
+        tz_offset = new_dt - dt
+        if tz_offset == self._std_offset:
+            tz = self._std_tz
+        elif tz_offset == self._dst_offset:
             tz = self._dst_tz
         else:
-            tz = self._std_tz
-        return datetime(*(tt[:6] + (dt.microsecond, tz)))
+            tz = LocalTimezone(tz_offset)
+        return new_dt.replace(tzinfo=tz)
 
 
 utc = FixedOffset(0, 'UTC')

Modified: bloodhound/vendor/trac/current/trac/util/tests/__init__.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/tests/__init__.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/tests/__init__.py (original)
+++ bloodhound/vendor/trac/current/trac/util/tests/__init__.py Fri Nov 14 
11:06:23 2014
@@ -15,11 +15,15 @@ from __future__ import with_statement
 
 import doctest
 import os.path
+import pkg_resources
 import random
 import re
+import sys
 import tempfile
 import unittest
 
+import trac
+import trac.tests.compat
 from trac import util
 from trac.util.tests import concurrency, datefmt, presentation, text, \
                             translation, html
@@ -169,6 +173,102 @@ class SafeReprTestCase(unittest.TestCase
                          "type(s) for +: 'int' and 'str')>", sr)
 
 
+class SetuptoolsUtilsTestCase(unittest.TestCase):
+
+    def test_get_module_path(self):
+        self.assertEqual(util.get_module_path(trac),
+                         util.get_module_path(util))
+
+    def test_get_pkginfo_trac(self):
+        pkginfo = util.get_pkginfo(trac)
+        self.assertEqual(trac.__version__, pkginfo.get('version'))
+        self.assertNotEqual({}, pkginfo)
+
+    def test_get_pkginfo_non_toplevel(self):
+        from trac import core
+        import tracopt
+        pkginfo = util.get_pkginfo(trac)
+        self.assertEqual(pkginfo, util.get_pkginfo(util))
+        self.assertEqual(pkginfo, util.get_pkginfo(core))
+        self.assertEqual(pkginfo, util.get_pkginfo(tracopt))
+
+    def test_get_pkginfo_genshi(self):
+        try:
+            import genshi
+            import genshi.core
+            dist = pkg_resources.get_distribution('Genshi')
+        except:
+            pass
+        else:
+            pkginfo = util.get_pkginfo(genshi)
+            self.assertNotEqual({}, pkginfo)
+            self.assertEqual(pkginfo, util.get_pkginfo(genshi.core))
+
+    def test_get_pkginfo_babel(self):
+        try:
+            import babel
+            import babel.core
+            dist = pkg_resources.get_distribution('Babel')
+        except:
+            pass
+        else:
+            pkginfo = util.get_pkginfo(babel)
+            self.assertNotEqual({}, pkginfo)
+            self.assertEqual(pkginfo, util.get_pkginfo(babel.core))
+
+    def test_get_pkginfo_mysqldb(self):
+        # MySQLdb's package name is "MySQL-Python"
+        try:
+            import MySQLdb
+            import MySQLdb.cursors
+            dist = pkg_resources.get_distribution('MySQL-Python')
+            dist.get_metadata('top_level.txt')
+        except:
+            pass
+        else:
+            pkginfo = util.get_pkginfo(MySQLdb)
+            self.assertNotEqual({}, pkginfo)
+            self.assertEqual(pkginfo, util.get_pkginfo(MySQLdb.cursors))
+
+    def test_get_pkginfo_psycopg2(self):
+        # python-psycopg2 deb package doesn't provide SOURCES.txt and
+        # top_level.txt
+        try:
+            import psycopg2
+            import psycopg2.extensions
+            dist = pkg_resources.get_distribution('psycopg2')
+        except:
+            pass
+        else:
+            pkginfo = util.get_pkginfo(psycopg2)
+            self.assertNotEqual({}, pkginfo)
+            self.assertEqual(pkginfo, util.get_pkginfo(psycopg2.extensions))
+
+
+class LazyClass(object):
+    @util.lazy
+    def f(self):
+        return object()
+
+
+class LazyTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.obj = LazyClass()
+
+    def test_lazy_get(self):
+        f = self.obj.f
+        self.assertTrue(self.obj.f is f)
+
+    def test_lazy_set(self):
+        self.obj.f = 2
+        self.assertEqual(2, self.obj.f)
+
+    def test_lazy_del(self):
+        f = self.obj.f
+        del self.obj.f
+        self.assertFalse(self.obj.f is f)
+
 
 def suite():
     suite = unittest.TestSuite()
@@ -177,6 +277,8 @@ def suite():
     suite.addTest(unittest.makeSuite(RandomTestCase))
     suite.addTest(unittest.makeSuite(ContentDispositionTestCase))
     suite.addTest(unittest.makeSuite(SafeReprTestCase))
+    suite.addTest(unittest.makeSuite(SetuptoolsUtilsTestCase))
+    suite.addTest(unittest.makeSuite(LazyTestCase))
     suite.addTest(concurrency.suite())
     suite.addTest(datefmt.suite())
     suite.addTest(presentation.suite())

Modified: bloodhound/vendor/trac/current/trac/util/tests/datefmt.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/tests/datefmt.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/tests/datefmt.py (original)
+++ bloodhound/vendor/trac/current/trac/util/tests/datefmt.py Fri Nov 14 
11:06:23 2014
@@ -20,20 +20,16 @@ import os
 import time
 import unittest
 
-from trac.tests import compat
+import trac.tests.compat
 from trac.core import TracError
 from trac.util import datefmt
 
 try:
-    import pytz
-except ImportError:
-    pytz = None
-try:
     from babel import Locale
 except ImportError:
     Locale = None
 
-if pytz is None:
+if datefmt.pytz is None:
     PytzTestCase = None
 else:
     class PytzTestCase(unittest.TestCase):
@@ -210,7 +206,7 @@ class ParseISO8601TestCase(unittest.Test
         self.assertEqual(datetime.timedelta(hours=-9, minutes=-30),
                          dt.utcoffset())
 
-    if pytz:
+    if datefmt.pytz:
         def test_iso8601_naive_tz_normalize_non_existent_time(self):
             t = datetime.datetime(2012, 3, 25, 1, 15, 57, 0, datefmt.utc)
             tz = datefmt.timezone('Europe/Paris')
@@ -528,7 +524,7 @@ class ParseRelativeDateTestCase(unittest
         self.assertEqual(datetime.datetime(2012, 3, 25, 3, 14, 59, tzinfo=tz),
                          datefmt._parse_relative_time('last second', tz, now))
 
-    if pytz:
+    if datefmt.pytz:
         def test_time_interval_across_dst(self):
             tz = datefmt.timezone('Europe/Paris')
             now = datefmt.to_datetime(datetime.datetime(2012, 3, 25, 3, 0, 41),
@@ -1457,6 +1453,235 @@ class LocalTimezoneTestCase(unittest.Tes
         self.assertEqual(datetime.timedelta(hours=1), t3 - t2)
         self.assertEqual(datetime.timedelta(hours=1), t4 - t3)
 
+    def test_london_between_1968_and_1971(self):
+        self._tzset('Europe/London')
+        # -1:00 (DST end) at 1967-10-29 03:00
+        ts = datefmt.to_timestamp(datetime.datetime(1967, 10, 30,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('1967-10-30T00:00:00+00:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # +1:00 (DST start) at 1968-02-18 02:00
+        ts = datefmt.to_timestamp(datetime.datetime(1968, 2, 19,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('1968-02-19T01:00:00+01:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # No DST between 1968-02-18 02:00 and 1971-10-31 03:00
+        ts = datefmt.to_timestamp(datetime.datetime(1970, 1, 1, 0, 0, 23,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('1970-01-01T01:00:23+01:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # -1:00 (TZ change) at 1971-10-31 03:00
+        t = datefmt.to_datetime(datetime.datetime(1971, 10, 31, 1, 30),
+                                datefmt.localtz)
+        delta = datetime.timedelta(hours=1)
+        self.assertEqual('1971-10-31T01:30:00+01:00', t.isoformat())
+        t = datefmt.to_datetime(t + delta, datefmt.localtz)
+        self.assertEqual('1971-10-31T02:30:00+01:00', t.isoformat())
+        t = datefmt.to_datetime(t + delta, datefmt.localtz)
+        self.assertEqual('1971-10-31T02:30:00+00:00', t.isoformat())
+        t = datefmt.to_datetime(t + delta, datefmt.localtz)
+        self.assertEqual('1971-10-31T03:30:00+00:00', t.isoformat())
+
+        ts = datefmt.to_timestamp(datetime.datetime(1971, 11, 1,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('1971-11-01T00:00:00+00:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+
+    def test_guatemala_dst_in_2006(self):
+        self._tzset('America/Guatemala')
+        # No DST before 2006-04-30 00:00
+        ts = datefmt.to_timestamp(datetime.datetime(2006, 4, 29,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('2006-04-28T18:00:00-06:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # +1:00 (DST start) at 2006-04-30 00:00
+        ts = datefmt.to_timestamp(datetime.datetime(2006, 8, 1,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('2006-07-31T19:00:00-05:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # -1:00 (DST end) at 2006-10-01 00:00
+        ts = datefmt.to_timestamp(datetime.datetime(2006, 10, 2,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('2006-10-01T18:00:00-06:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # No DST after 2006-10-01 00:00
+
+    def test_venezuela_in_2007(self):
+        self._tzset('America/Caracas')
+        ts = datefmt.to_timestamp(datetime.datetime(2007, 12, 8,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('2007-12-07T20:00:00-04:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # -0:30 (TZ change) at 2007-12-09 03:00
+        ts = datefmt.to_timestamp(datetime.datetime(2007, 12, 10,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('2007-12-09T19:30:00-04:30',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+
+    def test_lord_howe_island_in_198x(self):
+        self._tzset('Australia/Lord_Howe')
+        ts = datefmt.to_timestamp(datetime.datetime(1985, 3, 1,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('1985-03-01T11:30:00+11:30',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        # -1:00 (DST end) at 1985-03-03 02:00
+        ts = datefmt.to_timestamp(datetime.datetime(1985, 8, 1,
+                                                    tzinfo=datefmt.utc))
+        self.assertEqual('1985-08-01T10:30:00+10:30',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+        ts = datefmt.to_timestamp(datetime.datetime(1985, 11, 1,
+                                                    tzinfo=datefmt.utc))
+        # +0:30 (DST start) at 1985-10-27 02:00
+        self.assertEqual('1985-11-01T11:00:00+11:00',
+                         datefmt.to_datetime(ts, datefmt.localtz).isoformat())
+
+    def _compare_pytz_arithmetic(self, tz, dt_naive):
+        """Compare arithmetic timezone-aware datetime between localtz and
+        pytz's timezone"""
+        localtz = datefmt.localtz
+        delta = datetime.timedelta(minutes=20)
+        n = datetime.timedelta(hours=3).seconds / delta.seconds
+        # create timezone-aware datetime instances
+        dt_localtz = datefmt.to_datetime(dt_naive - delta * n, localtz)
+        dt_tz = datefmt.to_datetime(dt_naive - delta * n, tz)
+        # compare datetime instances between -3 hours and +3 hours
+        for i in xrange(n * 2 + 1):
+            self.assertEqual(dt_tz, dt_localtz)
+            self.assertEqual(dt_tz.isoformat(), dt_localtz.isoformat())
+            dt_localtz = datefmt.to_datetime(dt_localtz + delta, localtz)
+            dt_tz = datefmt.to_datetime(dt_tz + delta, tz)
+
+    def _compare_pytz_localize_and_normalize(self, tz, dt_naive):
+        """Compare localize() and normalize() of LocalTimezone and pytz's
+        timezone"""
+        localtz = datefmt.localtz
+        delta = datetime.timedelta(minutes=20)
+        n = datetime.timedelta(hours=3).seconds / delta.seconds
+        dt_naive -= delta * n
+        # compare localize and normalize with naive datetime
+        # between -3 hours and +3 hours
+        for i in xrange(n * 2 + 1):
+            dt_localtz = localtz.localize(dt_naive)
+            dt_tz = tz.localize(dt_naive)
+            self.assertEqual(dt_tz, dt_localtz,
+                             '%r != %r (%r)' % (dt_tz, dt_localtz, dt_naive))
+            self.assertEqual(dt_tz.isoformat(), dt_localtz.isoformat(),
+                             '%r != %r (%r)' % (dt_tz.isoformat(),
+                                                dt_localtz.isoformat(),
+                                                dt_naive))
+            dt_localtz = localtz.normalize(localtz.localize(dt_naive))
+            dt_tz = tz.normalize(tz.localize(dt_naive))
+            self.assertEqual(dt_tz, dt_localtz,
+                             '%r != %r (%r)' % (dt_tz, dt_localtz, dt_naive))
+            self.assertEqual(dt_tz.isoformat(), dt_localtz.isoformat(),
+                             '%r != %r (%r)' % (dt_tz.isoformat(),
+                                                dt_localtz.isoformat(),
+                                                dt_naive))
+            dt_naive += delta
+
+    def _compare_pytz(self, tz, value, localize=True):
+        if isinstance(value, basestring):
+            value = datefmt.parse_date(value + 'Z', datefmt.utc)
+        dt_naive = value.replace(tzinfo=None)
+        self._compare_pytz_arithmetic(tz, dt_naive)
+        # `localize()` differs one of pytz's timezone when backward timezone
+        # change
+        if localize:
+            self._compare_pytz_localize_and_normalize(tz, dt_naive)
+
+    if datefmt.pytz:
+        def test_pytz_choibalsan(self):
+            tz = datefmt.timezone('Asia/Choibalsan')
+            self._tzset('Asia/Choibalsan')
+            self._compare_pytz(tz, '1977-01-01T00:00')  # No DST
+            self._compare_pytz(tz, '1978-01-01T01:00')  # +1:00 (TZ change)
+            self._compare_pytz(tz, '1978-01-01T02:00')  #       (TZ change)
+            self._compare_pytz(tz, '1982-04-01T00:00')  # No DST
+            self._compare_pytz(tz, '1983-04-01T00:00')  # +2:00 (TZ change)
+            self._compare_pytz(tz, '1983-04-01T02:00')  #       (TZ change)
+            self._compare_pytz(tz, '1983-10-01T00:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '2006-03-25T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '2006-09-30T02:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '2007-07-01T00:00')  # No DST in 2007
+            self._compare_pytz(tz, '2008-03-30T23:00',  #       (TZ change)
+                               localize=False)
+            self._compare_pytz(tz, '2008-03-31T00:00',  # -1:00 (TZ change)
+                               localize=False)
+            self._compare_pytz(tz, '2009-07-01T00:00')  # No DST
+
+        def test_pytz_guatemala(self):
+            tz = datefmt.timezone('America/Guatemala')
+            self._tzset('America/Guatemala')
+            self._compare_pytz(tz, '2005-07-01T00:00')  # No DST
+            self._compare_pytz(tz, '2006-04-30T00:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '2006-10-01T00:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '2007-07-01T00:00')  # No DST
+
+        def test_pytz_london(self):
+            tz = datefmt.timezone('Europe/London')
+            self._tzset('Europe/London')
+            self._compare_pytz(tz, '1968-02-18T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '1971-10-31T02:00',  #       (TZ change)
+                               localize=False)
+            self._compare_pytz(tz, '1971-10-31T03:00',  # -1:00 (TZ change)
+                               localize=False)
+            self._compare_pytz(tz, '1972-03-19T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '1972-10-29T03:00')  # -1:00 (DST end)
+
+        def test_pytz_lord_howe_island(self):
+            tz = datefmt.timezone('Australia/Lord_Howe')
+            self._tzset('Australia/Lord_Howe')
+            self._compare_pytz(tz, '1980-07-01T00:00')  # No DST
+            self._compare_pytz(tz, '1981-03-01T00:00')  # +0:30 (TZ change)
+            self._compare_pytz(tz, '1981-03-01T00:30')  #       (TZ change)
+            self._compare_pytz(tz, '1981-10-25T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '1985-03-03T02:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '1985-10-27T02:00')  # +0:30 (DST start)
+            self._compare_pytz(tz, '1986-03-16T02:00')  # -0:30 (DST end)
+
+        def test_pytz_moscow(self):
+            tz = datefmt.timezone('Europe/Moscow')
+            self._tzset('Europe/Moscow')
+            self._compare_pytz(tz, '1991-09-29T03:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '1992-01-19T02:00')  # +1:00 (TZ change)
+            self._compare_pytz(tz, '1992-01-19T03:00')  #       (TZ change)
+            self._compare_pytz(tz, '1992-03-28T23:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '1992-09-26T23:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '2010-03-28T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '2010-10-31T03:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '2011-03-27T02:00')  # +1:00 (TZ change)
+            self._compare_pytz(tz, '2011-03-27T03:00')  #       (TZ change)
+            self._compare_pytz(tz, '2011-10-31T03:00')  # No DST
+
+        def test_pytz_paris(self):
+            tz = datefmt.timezone('Europe/Paris')
+            self._tzset('Europe/Paris')
+            self._compare_pytz(tz, '1975-07-01T01:00')  # No DST
+            self._compare_pytz(tz, '1976-03-28T01:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '1976-09-26T01:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '2009-03-29T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '2009-10-25T03:00')  # -1:00 (DST end)
+
+        def test_pytz_tokyo(self):
+            tz = datefmt.timezone('Asia/Tokyo')
+            self._tzset('Asia/Tokyo')
+            self._compare_pytz(tz, '1947-07-01T02:00')  # No DST
+            self._compare_pytz(tz, '1948-05-02T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '1948-09-11T02:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '1949-04-03T02:00')  # +1:00 (DST start)
+            self._compare_pytz(tz, '1949-09-10T02:00')  # -1:00 (DST end)
+            self._compare_pytz(tz, '1950-07-01T02:00')  # No DST
+
+        def test_pytz_venezuela(self):
+            tz = datefmt.timezone('America/Caracas')
+            self._tzset('America/Caracas')
+            self._compare_pytz(tz, '2006-07-01T00:00')  # No DST
+            self._compare_pytz(tz, '2007-12-09T02:30',  #       (TZ change)
+                               localize=False)
+            self._compare_pytz(tz, '2007-12-09T03:00',  # -0:30 (TZ change)
+                               localize=False)
+            self._compare_pytz(tz, '2008-07-01T00:00')  # No DST
+
 
 class LocalTimezoneStrTestCase(unittest.TestCase):
 

Modified: bloodhound/vendor/trac/current/trac/util/tests/html.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/tests/html.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/tests/html.py (original)
+++ bloodhound/vendor/trac/current/trac/util/tests/html.py Fri Nov 14 11:06:23 
2014
@@ -15,8 +15,8 @@ import unittest
 from genshi.builder import Element, Fragment, tag
 from genshi.input import HTML
 
+import trac.tests.compat
 from trac.core import TracError
-from trac.tests import compat
 from trac.util.html import TracHTMLSanitizer, find_element, to_fragment
 from trac.util.translation import gettext, tgettext
 

Modified: bloodhound/vendor/trac/current/trac/util/tests/text.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/tests/text.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/tests/text.py (original)
+++ bloodhound/vendor/trac/current/trac/util/tests/text.py Fri Nov 14 11:06:23 
2014
@@ -16,7 +16,7 @@ import socket
 import unittest
 from StringIO import StringIO
 
-from trac.tests import compat
+import trac.tests.compat
 from trac.util.text import empty, expandtabs, fix_eol, javascript_quote, \
                            levenshtein_distance, normalize_whitespace, \
                            print_table, quote_query_string, shorten_line, \

Modified: bloodhound/vendor/trac/current/trac/util/translation.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/util/translation.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/util/translation.py (original)
+++ bloodhound/vendor/trac/current/trac/util/translation.py Fri Nov 14 11:06:23 
2014
@@ -372,6 +372,7 @@ try:
     has_babel = True
 
 except ImportError: # fall back on 0.11 behavior, i18n functions are no-ops
+    Locale = None
     gettext = _ = gettext_noop
     dgettext = dgettext_noop
     ngettext = ngettext_noop

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/api.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/api.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/api.py Fri Nov 14 
11:06:23 2014
@@ -424,6 +424,8 @@ class RepositoryManager(Component):
             return _('%(kind)s %(id)s%(at_version)s%(in_repo)s',
                      kind=kind, id=id, at_version=version, in_repo=in_repo)
         elif resource.realm == 'repository':
+            if not resource.id:
+                return _("Default repository")
             return _("Repository %(repo)s", repo=resource.id)
 
     def get_resource_url(self, resource, href, **kwargs):

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/cache.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/cache.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/cache.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/cache.py Fri Nov 14 
11:06:23 2014
@@ -101,7 +101,7 @@ class CachedRepository(Repository):
                       """, (to_utimestamp(cset.date), cset.author,
                             cset.message, self.id, srev))
             else:
-                self._insert_changeset(db, rev, cset)
+                self._insert_changeset(db, cset.rev, cset)
         return old_cset
 
     @cached('_metadata_id')
@@ -115,62 +115,10 @@ class CachedRepository(Repository):
 
     def sync(self, feedback=None, clean=False):
         if clean:
-            self.log.info("Cleaning cache")
-            with self.env.db_transaction as db:
-                db("DELETE FROM revision WHERE repos=%s",
-                   (self.id,))
-                db("DELETE FROM node_change WHERE repos=%s",
-                   (self.id,))
-                db.executemany("DELETE FROM repository WHERE id=%s AND 
name=%s",
-                               [(self.id, k) for k in CACHE_METADATA_KEYS])
-                db.executemany("""
-                      INSERT INTO repository (id, name, value)
-                      VALUES (%s, %s, %s)
-                      """, [(self.id, k, '') for k in CACHE_METADATA_KEYS])
-                del self.metadata
+            self.remove_cache()
 
         metadata = self.metadata
-
-        with self.env.db_transaction as db:
-            invalidate = False
-
-            # -- check that we're populating the cache for the correct
-            #    repository
-            repository_dir = metadata.get(CACHE_REPOSITORY_DIR)
-            if repository_dir:
-                # directory part of the repo name can vary on case insensitive
-                # fs
-                if os.path.normcase(repository_dir) \
-                        != os.path.normcase(self.name):
-                    self.log.info("'repository_dir' has changed from %r to %r",
-                                  repository_dir, self.name)
-                    raise TracError(_("The repository directory has changed, "
-                                      "you should resynchronize the "
-                                      "repository with: trac-admin $ENV "
-                                      "repository resync '%(reponame)s'",
-                                      reponame=self.reponame or '(default)'))
-            elif repository_dir is None: #
-                self.log.info('Storing initial "repository_dir": %s',
-                              self.name)
-                db("""INSERT INTO repository (id, name, value)
-                      VALUES (%s, %s, %s)
-                      """, (self.id, CACHE_REPOSITORY_DIR, self.name))
-                invalidate = True
-            else: # 'repository_dir' cleared by a resync
-                self.log.info('Resetting "repository_dir": %s', self.name)
-                db("UPDATE repository SET value=%s WHERE id=%s AND name=%s",
-                   (self.name, self.id, CACHE_REPOSITORY_DIR))
-                invalidate = True
-
-            # -- insert a 'youngeset_rev' for the repository if necessary
-            if metadata.get(CACHE_YOUNGEST_REV) is None:
-                db("""INSERT INTO repository (id, name, value)
-                      VALUES (%s, %s, %s)
-                      """, (self.id, CACHE_YOUNGEST_REV, ''))
-                invalidate = True
-
-            if invalidate:
-                del self.metadata
+        self.save_metadata(metadata)
 
         # -- retrieve the youngest revision in the repository and the youngest
         #    revision cached so far
@@ -263,6 +211,65 @@ class CachedRepository(Repository):
                 if feedback:
                     feedback(youngest)
 
+    def remove_cache(self):
+        """Remove the repository cache."""
+        self.log.info("Cleaning cache")
+        with self.env.db_transaction as db:
+            db("DELETE FROM revision WHERE repos=%s",
+               (self.id,))
+            db("DELETE FROM node_change WHERE repos=%s",
+               (self.id,))
+            db.executemany("DELETE FROM repository WHERE id=%s AND name=%s",
+                           [(self.id, k) for k in CACHE_METADATA_KEYS])
+            db.executemany("""
+                  INSERT INTO repository (id, name, value)
+                  VALUES (%s, %s, %s)
+                  """, [(self.id, k, '') for k in CACHE_METADATA_KEYS])
+            del self.metadata
+
+    def save_metadata(self, metadata):
+        """Save the repository metadata."""
+        with self.env.db_transaction as db:
+            invalidate = False
+
+            # -- check that we're populating the cache for the correct
+            #    repository
+            repository_dir = metadata.get(CACHE_REPOSITORY_DIR)
+            if repository_dir:
+                # directory part of the repo name can vary on case insensitive
+                # fs
+                if os.path.normcase(repository_dir) \
+                        != os.path.normcase(self.name):
+                    self.log.info("'repository_dir' has changed from %r to %r",
+                                  repository_dir, self.name)
+                    raise TracError(_("The repository directory has changed, "
+                                      "you should resynchronize the "
+                                      "repository with: trac-admin $ENV "
+                                      "repository resync '%(reponame)s'",
+                                      reponame=self.reponame or '(default)'))
+            elif repository_dir is None: #
+                self.log.info('Storing initial "repository_dir": %s',
+                              self.name)
+                db("""INSERT INTO repository (id, name, value)
+                      VALUES (%s, %s, %s)
+                      """, (self.id, CACHE_REPOSITORY_DIR, self.name))
+                invalidate = True
+            else: # 'repository_dir' cleared by a resync
+                self.log.info('Resetting "repository_dir": %s', self.name)
+                db("UPDATE repository SET value=%s WHERE id=%s AND name=%s",
+                   (self.name, self.id, CACHE_REPOSITORY_DIR))
+                invalidate = True
+
+            # -- insert a 'youngeset_rev' for the repository if necessary
+            if metadata.get(CACHE_YOUNGEST_REV) is None:
+                db("""INSERT INTO repository (id, name, value)
+                      VALUES (%s, %s, %s)
+                      """, (self.id, CACHE_YOUNGEST_REV, ''))
+                invalidate = True
+
+            if invalidate:
+                del self.metadata
+
     def _insert_changeset(self, db, rev, cset):
         srev = self.db_rev(rev)
         # 1. Attempt to resync the 'revision' table.  In case of
@@ -310,9 +317,64 @@ class CachedRepository(Repository):
             return [int(rev) for rev, in db("""
                     SELECT DISTINCT rev FROM node_change
                     WHERE repos=%%s AND rev>=%%s AND rev<=%%s
-                      AND (path=%%s OR path %s)""" % db.like(),
+                      AND (path=%%s OR path %s)""" % db.prefix_match(),
                     (self.id, sfirst, slast, path,
-                     db.like_escape(path + '/') + '%'))]
+                     db.prefix_match_value(path + '/')))]
+
+    def _get_changed_revs(self, node_infos):
+        if not node_infos:
+            return {}
+
+        node_infos = [(node, self.normalize_rev(first)) for node, first
+                                                        in node_infos]
+        sfirst = self.db_rev(min(first for node, first in node_infos))
+        slast = self.db_rev(max(node.rev for node, first in node_infos))
+        path_infos = dict((node.path, (node, first)) for node, first
+                                                     in node_infos)
+        path_revs = dict((node.path, []) for node, first in node_infos)
+
+        db = self.env.get_read_db()
+        cursor = db.cursor()
+        prefix_match = db.prefix_match()
+
+        # Prevent "too many SQL variables" since max number of parameters is
+        # 999 on SQLite. No limitation on PostgreSQL and MySQL.
+        idx = 0
+        delta = (999 - 3) // 5
+        while idx < len(node_infos):
+            subset = node_infos[idx:idx + delta]
+            idx += delta
+            count = len(subset)
+
+            holders = ','.join(('%s',) * count)
+            query = """\
+                SELECT DISTINCT
+                  rev, (CASE WHEN path IN (%s) THEN path %s END) AS path
+                FROM node_change
+                WHERE repos=%%s AND rev>=%%s AND rev<=%%s AND (path IN (%s) %s)
+                """ % \
+                (holders,
+                 ' '.join(('WHEN path ' + prefix_match + ' THEN %s',) * count),
+                 holders,
+                 ' '.join(('OR path ' + prefix_match,) * count))
+            args = []
+            args.extend(node.path for node, first in subset)
+            for node, first in subset:
+                args.append(db.prefix_match_value(node.path + '/'))
+                args.append(node.path)
+            args.extend((self.id, sfirst, slast))
+            args.extend(node.path for node, first in subset)
+            args.extend(db.prefix_match_value(node.path + '/')
+                        for node, first in subset)
+            cursor.execute(query, args)
+
+            for srev, path in cursor:
+                rev = self.rev_db(srev)
+                node, first = path_infos[path]
+                if first <= rev <= node.rev:
+                    path_revs[path].append(rev)
+
+        return path_revs
 
     def has_node(self, path, rev=None):
         return self.repos.has_node(path, self.normalize_rev(rev))
@@ -352,8 +414,8 @@ class CachedRepository(Repository):
             if path:
                 path = path.lstrip('/')
                 # changes on path itself or its children
-                sql += " AND (path=%s OR path " + db.like()
-                args.extend((path, db.like_escape(path + '/') + '%'))
+                sql += " AND (path=%s OR path " + db.prefix_match()
+                args.extend((path, db.prefix_match_value(path + '/')))
                 # deletion of path ancestors
                 components = path.lstrip('/').split('/')
                 parents = ','.join(('%s',) * len(components))
@@ -367,6 +429,12 @@ class CachedRepository(Repository):
             for rev, in db(sql, args):
                 return int(rev)
 
+    def parent_revs(self, rev):
+        if self.has_linear_changesets:
+            return Repository.parent_revs(self, rev)
+        else:
+            return self.repos.parent_revs(rev)
+
     def rev_older_than(self, rev1, rev2):
         return self.repos.rev_older_than(self.normalize_rev(rev1),
                                          self.normalize_rev(rev2))

Modified: 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html
 (original)
+++ 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html
 Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2009-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -39,7 +49,8 @@
 
     <py:choose test="view">
       <form py:when="'detail'" py:with="info = repositories[reponame]" 
class="mod" id="trac-modrepos" method="post" action="">
-        <fieldset py:choose="" py:with="readonly = not info.editable or None">
+        <fieldset py:choose="" py:with="disabled = 'disabled' if not 
info.editable else None;
+                                        readonly = 'readonly' if not 
info.editable else None">
           <legend py:when="info.editable">Modify Repository:</legend>
           <legend py:otherwise="">View Repository:</legend>
           <p py:if="not info.editable" class="hint" 
i18n:msg=""><strong>Note:</strong>
@@ -65,17 +76,18 @@
             </py:otherwise>
           </py:choose>
           <div class="field">
-            <label><input type="checkbox" name="hidden" value="1" 
checked="${info.hidden or None}" disabled="$readonly"/>
+            <label><input type="checkbox" name="hidden" value="1" 
checked="${info.hidden or None}"
+                          disabled="${not info.editable or None}"/>
               Hide from repository index
             </label>
           </div>
           <div class="field">
             <fieldset>
               <label for="description" i18n:msg="">
-                Description (you may use <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here):
+                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"
+                <textarea id="description" name="description" class="wikitext 
trac-fullwidth trac-resizable"
                           rows="6" cols="60" readonly="$readonly">
 $info.description</textarea>
               </p>

Modified: 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html 
(original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html 
Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -96,7 +106,7 @@
           </form>
         </div>
 
-        <div class="trac-tags" py:with="changeset = 
repos.get_changeset(repos.normalize_rev(stickyrev))">
+        <div class="trac-tags" py:if="changeset">
           <span py:for="branch, head in changeset.get_branches()" 
py:if="branch not in ('default', 'master')"
                 class="branch${' head' if head else ''}"
                 title="${_('Branch head') if head else 
_('Branch')}">${branch}</span>

Modified: 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/changeset.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/changeset.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/templates/changeset.html 
(original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/templates/changeset.html 
Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/diff_form.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/diff_form.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/templates/diff_form.html 
(original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/templates/diff_form.html 
Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/dir_entries.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/dir_entries.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/dir_entries.html 
(original)
+++ 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/dir_entries.html 
Fri Nov 14 11:06:23 2014
@@ -1,4 +1,15 @@
-<!--! Template for generating rows corresponding to directory entries -->
+<!--!  Copyright (C) 2007-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
+Template for generating rows corresponding to directory entries
+-->
 <html xmlns="http://www.w3.org/1999/xhtml";
       xmlns:py="http://genshi.edgewall.org/";
       xmlns:xi="http://www.w3.org/2001/XInclude"; py:strip="">

Modified: 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/dirlist_thead.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/dirlist_thead.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/dirlist_thead.html 
(original)
+++ 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/dirlist_thead.html 
Fri Nov 14 11:06:23 2014
@@ -1,4 +1,15 @@
-<!--! Template snippet for a standard table header for a dirlist -->
+<!--!  Copyright (C) 2008-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
+Template snippet for a standard table header for a dirlist
+-->
 <html xmlns="http://www.w3.org/1999/xhtml";
     xmlns:py="http://genshi.edgewall.org/";
     xmlns:xi="http://www.w3.org/2001/XInclude"; py:strip="">

Modified: 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/path_links.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/path_links.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/path_links.html 
(original)
+++ 
bloodhound/vendor/trac/current/trac/versioncontrol/templates/path_links.html 
Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2008-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <div xmlns="http://www.w3.org/1999/xhtml";
      xmlns:py="http://genshi.edgewall.org/"; py:strip="">
   <!--!  Display a sequence of path components.


Reply via email to