You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by rj...@apache.org on 2014/11/14 12:06:31 UTC

svn commit: r1639602 [11/14] - in /bloodhound/vendor/trac/current: ./ contrib/workflow/ doc/dev/ sample-plugins/ sample-plugins/permissions/ sample-plugins/workflow/ trac/ trac/admin/ trac/admin/templates/ trac/admin/tests/ trac/db/ trac/db/tests/ trac...

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/templates/repository_index.html
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/repository_index.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/templates/repository_index.html (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/templates/repository_index.html Fri Nov 14 11:06:23 2014
@@ -1,4 +1,15 @@
-<!--! Template snippet for a table of repositories -->
+<!--!  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 table of repositories
+-->
 <html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:py="http://genshi.edgewall.org/"
     xmlns:xi="http://www.w3.org/2001/XInclude" py:strip="">
@@ -11,11 +22,11 @@
         <tr class="${'odd' if idx % 2 else 'even'}">
           <td class="name">
             <em py:strip="not err">
-              <b py:strip="repoinfo.alias != ''">
+              <strong py:strip="repoinfo.alias != ''">
                 <a class="dir" title="View Root Directory"
                    href="${href.browser(repos.reponame if repos else reponame,
                                         order=order if order != 'name' else None, desc=desc)}">$reponame</a>
-              </b>
+              </strong>
             </em>
           </td>
           <td class="size">

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/templates/revisionlog.html
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/revisionlog.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/templates/revisionlog.html (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/templates/revisionlog.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/sortable_th.html
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/templates/sortable_th.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/templates/sortable_th.html (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/templates/sortable_th.html Fri Nov 14 11:06:23 2014
@@ -1,4 +1,14 @@
-<!--! Snippet for a <th> corresponding to a sortable column.
+<!--!  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/.
+
+Snippet for a <th> corresponding to a sortable column.
 
     Expects the following variables to be set specifically:
 

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/tests/api.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/tests/api.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/tests/api.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/tests/api.py Fri Nov 14 11:06:23 2014
@@ -111,6 +111,12 @@ class ResourceManagerTestCase(unittest.T
         self.assertEqual('/trac.cgi/browser/testrepo',
                          get_resource_url(self.env, res, self.env.href))
 
+        res = Resource('repository', '')  # default repository
+        self.assertEqual('Default repository',
+                         get_resource_description(self.env, res))
+        self.assertEqual('/trac.cgi/browser',
+                         get_resource_url(self.env, res, self.env.href))
+
 
 def suite():
     suite = unittest.TestSuite()

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/tests/cache.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/tests/cache.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/tests/cache.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/tests/cache.py Fri Nov 14 11:06:23 2014
@@ -18,8 +18,8 @@ from __future__ import with_statement
 
 from datetime import datetime
 
+import trac.tests.compat
 from trac.test import EnvironmentStub, Mock
-from trac.tests import compat
 from trac.util.datefmt import to_utimestamp, utc
 from trac.versioncontrol import Repository, Changeset, Node, NoSuchChangeset
 from trac.versioncontrol.cache import CachedRepository
@@ -283,6 +283,40 @@ class CacheTestCase(unittest.TestCase):
                          rows[2])
         self.assertEqual((to_utimestamp(t[3]), 'joe', 'Add COPYING'), rows[3])
 
+    def test_sync_changeset_with_string_rev(self):  # ticket:11660
+
+        class MockCachedRepository(CachedRepository):
+            def db_rev(self, rev):
+                return '%010d' % rev
+
+        t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
+        t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc)
+        repos = self.get_repos(get_changeset=lambda x: changesets[int(x)],
+                               youngest_rev=1)
+        changesets = [
+            Mock(Changeset, repos, 0, 'empty', 'joe', t1,
+                 get_changes=lambda: []),
+            Mock(Changeset, repos, 1, 'first', 'joe', t2,
+                 get_changes=lambda: []),
+            ]
+        cache = MockCachedRepository(self.env, repos, self.log)
+
+        cache.sync_changeset('0')   # not cached yet
+        cache.sync_changeset(u'1')  # not cached yet
+        rows = self.env.db_query(
+            "SELECT rev,author FROM revision ORDER BY rev")
+        self.assertEqual(2, len(rows))
+        self.assertEquals(('0000000000', 'joe'), rows[0])
+        self.assertEquals(('0000000001', 'joe'), rows[1])
+
+        cache.sync_changeset(u'0')  # cached
+        cache.sync_changeset('1')   # cached
+        rows = self.env.db_query(
+            "SELECT rev,author FROM revision ORDER BY rev")
+        self.assertEqual(2, len(rows))
+        self.assertEquals(('0000000000', 'joe'), rows[0])
+        self.assertEquals(('0000000001', 'joe'), rows[1])
+
     def test_get_changes(self):
         t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
         t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc)

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/tests/functional.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/tests/functional.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/tests/functional.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/tests/functional.py Fri Nov 14 11:06:23 2014
@@ -291,6 +291,51 @@ class RegressionTestTicket11438(Function
         tc.notfind('@%d' % (rev - 2))
 
 
+class RegressionTestTicket11584(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11584
+        don't raise NoSuchChangeset for empty repository if no "rev" parameter
+        """
+        repo_path = self._testenv.svnadmin_create('repo-t11584')
+
+        self._tester.go_to_admin()
+        tc.follow("\\bRepositories\\b")
+        tc.url(self._tester.url + '/admin/versioncontrol/repository')
+
+        tc.formvalue('trac-addrepos', 'name', 't11584')
+        tc.formvalue('trac-addrepos', 'dir', repo_path)
+        tc.submit()
+        tc.notfind(internal_error)
+        self._testenv._tracadmin('repository', 'sync', 't11584')
+
+        browser_url = self._tester.url + '/browser/t11584'
+        tc.go(browser_url)
+        tc.url(browser_url)
+        tc.notfind('Error: No such changeset')
+
+
+class RegressionTestTicket11618(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11618
+        fix for malformed `readonly="True"` attribute in repository admin.
+        """
+        env = self._testenv.get_trac_environment()
+        env.config.set('repositories', 't11618.dir',
+                       self._testenv.repo_path_for_initenv())
+        env.config.save()
+        try:
+            self._tester.go_to_admin()
+            tc.follow(r'\bRepositories\b')
+            tc.url(self._tester.url + '/admin/versioncontrol/repository')
+            tc.follow(r'\bt11618\b')
+            tc.url(self._tester.url + '/admin/versioncontrol/repository/t11618')
+            tc.notfind(' readonly="True"')
+            tc.find(' readonly="readonly"')
+        finally:
+            env.config.remove('repositories', 't11618.dir')
+            env.config.save()
+
+
 def functionalSuite(suite=None):
     if not suite:
         import trac.tests.functional
@@ -308,6 +353,8 @@ def functionalSuite(suite=None):
         suite.addTest(RegressionTestTicket11194())
         suite.addTest(RegressionTestTicket11346())
         suite.addTest(RegressionTestTicket11438())
+        suite.addTest(RegressionTestTicket11584())
+        suite.addTest(RegressionTestTicket11618())
         suite.addTest(RegressionTestRev5877())
     else:
         print "SKIP: versioncontrol/tests/functional.py (no svn bindings)"

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/browser.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/browser.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/browser.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/browser.py Fri Nov 14 11:06:23 2014
@@ -24,15 +24,14 @@ from genshi.builder import tag
 from trac.config import ListOption, BoolOption, Option
 from trac.core import *
 from trac.mimeview.api import IHTMLPreviewAnnotator, Mimeview, is_binary
-from trac.perm import IPermissionRequestor
+from trac.perm import IPermissionRequestor, PermissionError
 from trac.resource import Resource, ResourceNotFound
 from trac.util import as_bool, embedded_numbers
-from trac.util.compat import cleandoc
 from trac.util.datefmt import http_date, to_datetime, utc
 from trac.util.html import escape, Markup
 from trac.util.text import exception_to_unicode, shorten_line
 from trac.util.translation import _, cleandoc_
-from trac.web.api import IRequestHandler, Request, RequestDone
+from trac.web.api import IRequestHandler, RequestDone
 from trac.web.chrome import (INavigationContributor, add_ctxtnav, add_link,
                              add_script, add_stylesheet, prevnext_nav,
                              web_context)
@@ -296,7 +295,8 @@ class BrowserModule(Component):
 
     def get_navigation_items(self, req):
         rm = RepositoryManager(self.env)
-        if 'BROWSER_VIEW' in req.perm and rm.get_real_repositories():
+        if any(repos.is_viewable(req.perm) for repos
+                                           in rm.get_real_repositories()):
             yield ('mainnav', 'browser',
                    tag.a(_('Browse Source'), href=req.href.browser()))
 
@@ -327,8 +327,6 @@ class BrowserModule(Component):
             return True
 
     def process_request(self, req):
-        req.perm.require('BROWSER_VIEW')
-
         presel = req.args.get('preselected')
         if presel and (presel + '/').startswith(req.href.browser() + '/'):
             req.redirect(presel)
@@ -366,6 +364,7 @@ class BrowserModule(Component):
         # Find node for the requested path/rev
         context = web_context(req)
         node = None
+        changeset = None
         display_rev = lambda rev: rev
         if repos:
             try:
@@ -378,6 +377,12 @@ class BrowserModule(Component):
             except NoSuchChangeset, e:
                 raise ResourceNotFound(e.message,
                                        _('Invalid changeset number'))
+            if node:
+                try:
+                    # use changeset instance to retrieve branches and tags
+                    changeset = repos.get_changeset(node.rev)
+                except NoSuchChangeset:
+                    pass
 
             context = context.child(repos.resource.child('source', path,
                                                    version=rev_or_latest))
@@ -392,6 +397,9 @@ class BrowserModule(Component):
             repo_data = self._render_repository_index(
                                         context, all_repositories, order, desc)
         if node:
+            if not node.is_viewable(req.perm):
+                raise PermissionError('BROWSER_VIEW' if node.isdir else
+                                      'FILE_VIEW', node.resource, self.env)
             if node.isdir:
                 if format in ('zip',): # extension point here...
                     self._render_zip(req, context, repos, node, rev)
@@ -401,7 +409,13 @@ class BrowserModule(Component):
                 file_data = self._render_file(req, context, repos, node, rev)
 
         if not repos and not (repo_data and repo_data['repositories']):
-            raise ResourceNotFound(_("No node %(path)s", path=path))
+            # If no viewable repositories, check permission instead of
+            # repos.is_viewable()
+            req.perm.require('BROWSER_VIEW')
+            if show_index:
+                raise ResourceNotFound(_("No viewable repositories"))
+            else:
+                raise ResourceNotFound(_("No node %(path)s", path=path))
 
         quickjump_data = properties_data = None
         if node and not xhr:
@@ -413,7 +427,7 @@ class BrowserModule(Component):
             'context': context, 'reponame': reponame, 'repos': repos,
             'repoinfo': all_repositories.get(reponame or ''),
             'path': path, 'rev': node and node.rev, 'stickyrev': rev,
-            'display_rev': display_rev,
+            'display_rev': display_rev, 'changeset': changeset,
             'created_path': node and node.created_path,
             'created_rev': node and node.created_rev,
             'properties': properties_data,
@@ -504,6 +518,10 @@ class BrowserModule(Component):
                 continue
             try:
                 repos = rm.get_repository(reponame)
+            except TracError, err:
+                entry = (reponame, repoinfo, None, None,
+                         exception_to_unicode(err), None)
+            else:
                 if repos:
                     if not repos.is_viewable(context.perm):
                         continue
@@ -522,10 +540,7 @@ class BrowserModule(Component):
                              raw_href)
                 else:
                     entry = (reponame, repoinfo, None, None, u"\u2013", None)
-            except TracError, err:
-                entry = (reponame, repoinfo, None, None,
-                         exception_to_unicode(err), None)
-            if entry[-1] is not None:   # Check permission in case of error
+            if entry[4] is not None:  # Check permission in case of error
                 root = Resource('repository', reponame).child('source', '/')
                 if 'BROWSER_VIEW' not in context.perm(root):
                     continue

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/changeset.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/changeset.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/changeset.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/changeset.py Fri Nov 14 11:06:23 2014
@@ -40,7 +40,7 @@ from trac.util import as_bool, content_d
 from trac.util.datefmt import from_utimestamp, pretty_timedelta
 from trac.util.text import exception_to_unicode, to_unicode, \
                            unicode_urlencode, shorten_line, CRLF
-from trac.util.translation import _, ngettext
+from trac.util.translation import _, ngettext, tag_
 from trac.versioncontrol.api import RepositoryManager, Changeset, Node, \
                                     NoSuchChangeset
 from trac.versioncontrol.diff import get_diff_options, diff_blocks, \
@@ -102,7 +102,7 @@ class DefaultPropertyDiffRenderer(Compon
         unidiff = '--- \n+++ \n' + \
                   '\n'.join(unified_diff(old.splitlines(), new.splitlines(),
                                          options.get('contextlines', 3)))
-        return tag.li('Property ', tag.strong(name),
+        return tag.li(tag_("Property %(name)s", name=tag.strong(name)),
                       Mimeview(self.env).render(old_context, 'text/x-diff',
                                                 unidiff))
 
@@ -215,9 +215,9 @@ class ChangesetModule(Component):
         req.perm.require('CHANGESET_VIEW')
 
         # -- retrieve arguments
-        full_new_path = new_path = req.args.get('new_path')
+        new_path = req.args.get('new_path')
         new = req.args.get('new')
-        full_old_path = old_path = req.args.get('old_path')
+        old_path = req.args.get('old_path')
         old = req.args.get('old')
         reponame = req.args.get('reponame')
 
@@ -254,14 +254,14 @@ class ChangesetModule(Component):
 
         # -- normalize and check for special case
         try:
-            new_path = repos.normalize_path(new_path)
             new = repos.normalize_rev(new)
-            full_new_path = '/' + pathjoin(repos.reponame, new_path)
-            old_path = repos.normalize_path(old_path or new_path)
             old = repos.normalize_rev(old or new)
-            full_old_path = '/' + pathjoin(repos.reponame, old_path)
         except NoSuchChangeset, e:
-            raise ResourceNotFound(e.message, _('Invalid Changeset Number'))
+            raise ResourceNotFound(e.message, _("Invalid Changeset Number"))
+        new_path = repos.normalize_path(new_path)
+        old_path = repos.normalize_path(old_path or new_path)
+        full_new_path = '/' + pathjoin(repos.reponame, new_path)
+        full_old_path = '/' + pathjoin(repos.reponame, old_path)
 
         if old_path == new_path and old == new: # revert to Changeset
             old_path = old = None
@@ -1175,7 +1175,7 @@ class AnyDiffModule(Component):
                                if repos.is_viewable(req.perm))
 
             elem = tag.ul(
-                [tag.li(tag.b(path) if isdir else path)
+                [tag.li(tag.strong(path) if isdir else path)
                  for (isdir, name, path) in sorted(entries, key=kind_order)
                  if name.lower().startswith(prefix)])
 

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/log.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/log.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/log.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/log.py Fri Nov 14 11:06:23 2014
@@ -28,8 +28,7 @@ from trac.resource import ResourceNotFou
 from trac.util import Ranges
 from trac.util.text import to_unicode, wrap
 from trac.util.translation import _
-from trac.versioncontrol.api import (RepositoryManager, Changeset,
-                                     NoSuchChangeset)
+from trac.versioncontrol.api import Changeset, RepositoryManager
 from trac.versioncontrol.web_ui.changeset import ChangesetModule
 from trac.versioncontrol.web_ui.util import *
 from trac.web import IRequestHandler
@@ -90,8 +89,12 @@ class LogModule(Component):
         reponame, repos, path = rm.get_repository_by_path(path)
 
         if not repos:
-            raise ResourceNotFound(_("Repository '%(repo)s' not found",
-                                   repo=reponame))
+            if path == '/':
+                raise TracError(_("No repository specified and no default"
+                                  " repository configured."))
+            else:
+                raise ResourceNotFound(_("Repository '%(repo)s' not found",
+                                         repo=reponame or path.strip('/')))
 
         if reponame != repos.reponame:  # Redirect alias
             qs = req.query_string
@@ -110,7 +113,7 @@ class LogModule(Component):
                 rev = revranges.b
             except ValueError:
                 pass
-        rev = unicode(repos.normalize_rev(rev))
+        rev = repos.normalize_rev(rev)
         display_rev = repos.display_rev
 
         # The `history()` method depends on the mode:
@@ -139,12 +142,12 @@ class LogModule(Component):
                         node_history = list(node.get_history(2))
                         p, rev, chg = node_history[0]
                         if repos.rev_older_than(rev, a):
-                            break # simply skip, no separator
+                            break  # simply skip, no separator
                         if 'CHANGESET_VIEW' in req.perm(cset_resource(id=rev)):
                             if expected_next_item:
                                 # check whether we're continuing previous range
                                 np, nrev, nchg = expected_next_item
-                                if rev != nrev: # no, we need a separator
+                                if rev != nrev:  # no, we need a separator
                                     yield (np, nrev, None)
                             yield node_history[0]
                         if len(node_history) > 1:
@@ -159,6 +162,7 @@ class LogModule(Component):
         else:
             show_graph = path == '/' and not verbose \
                          and not repos.has_linear_changesets
+
             def history():
                 node = get_existing_node(req, repos, path, rev)
                 for h in node.get_history():
@@ -193,7 +197,7 @@ class LogModule(Component):
                     break
                 elif mode == 'path_history':
                     depth -= 1
-            if old_chg is None: # separator entry
+            if old_chg is None:  # separator entry
                 stop_limit = limit
             else:
                 count += 1
@@ -201,13 +205,15 @@ class LogModule(Component):
             if count >= stop_limit:
                 break
             previous_path = old_path
-        if info == []:
+        if not info:
             node = get_existing_node(req, repos, path, rev)
             if repos.rev_older_than(stop_rev, node.created_rev):
                 # FIXME: we should send a 404 error here
                 raise TracError(_("The file or directory '%(path)s' doesn't "
-                    "exist at revision %(rev)s or at any previous revision.",
-                    path=path, rev=display_rev(rev)), _('Nonexistent path'))
+                                  "exist at revision %(rev)s or at any "
+                                  "previous revision.", path=path,
+                                  rev=display_rev(rev)),
+                                _('Nonexistent path'))
 
         # Generate graph data
         graph = {}
@@ -232,7 +238,7 @@ class LogModule(Component):
             return req.href.log(repos.reponame or None, path, **params)
 
         if format in ('rss', 'changelog'):
-            info = [i for i in info if i['change']] # drop separators
+            info = [i for i in info if i['change']]  # drop separators
             if info and count > limit:
                 del info[-1]
         elif info and count >= limit:
@@ -246,8 +252,9 @@ class LogModule(Component):
                 older_revisions_href = make_log_href(next_path, rev=next_rev,
                                                      revs=next_revranges)
                 add_link(req, 'next', older_revisions_href,
-                    _('Revision Log (restarting at %(path)s, rev. %(rev)s)',
-                    path=next_path, rev=display_rev(next_rev)))
+                         _('Revision Log (restarting at %(path)s, rev. '
+                           '%(rev)s)', path=next_path,
+                           rev=display_rev(next_rev)))
             # only show fully 'limit' results, use `change == None` as a marker
             info[-1]['change'] = None
 
@@ -276,11 +283,11 @@ class LogModule(Component):
             'reponame': repos.reponame or None, 'repos': repos,
             'path': path, 'rev': rev, 'stop_rev': stop_rev,
             'display_rev': display_rev, 'revranges': revranges,
-            'mode': mode, 'verbose': verbose, 'limit' : limit,
+            'mode': mode, 'verbose': verbose, 'limit': limit,
             'items': info, 'changes': changes, 'extra_changes': extra_changes,
             'graph': graph,
-            'wiki_format_messages':
-            self.config['changeset'].getbool('wiki_format_messages')
+            'wiki_format_messages': self.config['changeset']
+                                    .getbool('wiki_format_messages')
         }
 
         if format == 'changelog':
@@ -295,8 +302,8 @@ class LogModule(Component):
         item_ranges = []
         range = []
         for item in info:
-            if item['change'] is None: # separator
-                if range: # start new range
+            if item['change'] is None:  # separator
+                if range:  # start new range
                     range.append(item)
                     item_ranges.append(range)
                     range = []
@@ -321,7 +328,8 @@ class LogModule(Component):
                  'application/rss+xml', 'rss')
         changelog_href = make_log_href(path, format='changelog', revs=revs,
                                        stop_rev=stop_rev)
-        add_link(req, 'alternate', changelog_href, _('ChangeLog'), 'text/plain')
+        add_link(req, 'alternate', changelog_href, _('ChangeLog'),
+                 'text/plain')
 
         add_ctxtnav(req, _('View Latest Revision'),
                     href=req.href.browser(repos.reponame or None, path))

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/__init__.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/__init__.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/__init__.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/__init__.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
@@ -13,10 +13,15 @@
 
 import unittest
 
-from trac.versioncontrol.web_ui.tests import wikisyntax
+from trac.versioncontrol.web_ui.tests import browser, changeset, log, \
+                                             wikisyntax
+
 
 def suite():
     suite = unittest.TestSuite()
+    suite.addTest(browser.suite())
+    suite.addTest(changeset.suite())
+    suite.addTest(log.suite())
     suite.addTest(wikisyntax.suite())
     return suite
 

Added: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/browser.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/browser.py?rev=1639602&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/browser.py (added)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/browser.py Fri Nov 14 11:06:23 2014
@@ -0,0 +1,379 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2014 Edgewall Software
+# All rights reserved.
+#
+# 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/.
+
+import unittest
+from datetime import datetime
+from cStringIO import StringIO
+
+import trac.tests.compat
+from trac.core import Component, TracError, implements
+from trac.perm import PermissionError
+from trac.resource import ResourceNotFound
+from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.util.datefmt import utc
+from trac.versioncontrol.api import (
+    Changeset, DbRepositoryProvider, IRepositoryConnector, Node, NoSuchNode,
+    Repository, RepositoryManager)
+from trac.versioncontrol.web_ui.browser import BrowserModule
+from trac.web.tests.api import RequestHandlerPermissionsTestCaseBase
+from tracopt.perm.authz_policy import ConfigObj
+
+
+class MockRepositoryConnector(Component):
+
+    implements(IRepositoryConnector)
+
+    def get_supported_types(self):
+        yield 'mock', 8
+
+    def get_repository(self, repos_type, repos_dir, params):
+        def get_changeset(rev):
+            return Mock(Changeset, repos, rev, 'message', 'author',
+                        datetime(2001, 1, 1, tzinfo=utc))
+
+        def get_node(path, rev):
+            if 'missing' in path:
+                raise NoSuchNode(path, rev)
+            kind = Node.FILE if 'file' in path else Node.DIRECTORY
+            node = Mock(Node, repos, path, rev, kind,
+                        created_path=path, created_rev=rev,
+                        get_entries=lambda: iter([]),
+                        get_properties=lambda: {},
+                        get_content=lambda: StringIO('content'),
+                        get_content_length=lambda: 7,
+                        get_content_type=lambda: 'application/octet-stream')
+            return node
+
+        if params['name'] == 'raise':
+            raise TracError("")
+        else:
+            repos = Mock(Repository, params['name'], params, self.log,
+                         get_youngest_rev=lambda: 1,
+                         get_changeset=get_changeset,
+                         get_node=get_node,
+                         previous_rev=lambda rev, path='': None,
+                         next_rev=lambda rev, path='': None)
+        return repos
+
+
+class BrowserModulePermissionsTestCase(RequestHandlerPermissionsTestCaseBase):
+
+    authz_policy = """\
+[repository:*allow*@*/source:*deny*]
+anonymous = !BROWSER_VIEW, !FILE_VIEW
+
+[repository:*deny*@*/source:*allow*]
+anonymous = BROWSER_VIEW, FILE_VIEW
+
+[repository:*allow*@*]
+anonymous = BROWSER_VIEW, FILE_VIEW
+
+[repository:*deny*@*]
+anonymous = !BROWSER_VIEW, !FILE_VIEW
+
+"""
+
+    def setUp(self):
+        super(BrowserModulePermissionsTestCase, self).setUp(BrowserModule)
+        provider = DbRepositoryProvider(self.env)
+        provider.add_repository('(default)', '/', 'mock')
+        provider.add_repository('allow', '/', 'mock')
+        provider.add_repository('deny', '/', 'mock')
+        provider.add_repository('raise', '/', 'mock')
+
+    def tearDown(self):
+        RepositoryManager(self.env).reload_repositories()
+        super(BrowserModulePermissionsTestCase, self).tearDown()
+
+    def test_get_navigation_items_with_browser_view(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW')
+        provider = DbRepositoryProvider(self.env)
+        req = self.create_request(path_info='/')
+        self.assertEqual('browser', self.get_navigation_items(req).next()[1])
+
+        provider.remove_repository('allow')
+        self.assertEqual('browser', self.get_navigation_items(req).next()[1])
+
+        provider.remove_repository('deny')
+        self.assertEqual('browser', self.get_navigation_items(req).next()[1])
+
+        provider.remove_repository('(default)')
+        self.assertEqual([], list(self.get_navigation_items(req)))
+
+    def test_get_navigation_items_without_browser_view(self):
+        provider = DbRepositoryProvider(self.env)
+        req = self.create_request(path_info='/')
+        self.assertEqual('browser', self.get_navigation_items(req).next()[1])
+
+        provider.remove_repository('(default)')
+        self.assertEqual('browser', self.get_navigation_items(req).next()[1])
+
+        provider.remove_repository('deny')
+        self.assertEqual('browser', self.get_navigation_items(req).next()[1])
+
+        provider.remove_repository('allow')
+        self.assertEqual([], list(self.get_navigation_items(req)))
+
+    def test_repository_with_browser_view(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW')
+
+        req = self.create_request(path_info='/browser/')
+        rv = self.process_request(req)
+        self.assertEqual('', rv[1]['repos'].name)
+
+        req = self.create_request(path_info='/browser/allow')
+        rv = self.process_request(req)
+        self.assertEqual('allow', rv[1]['repos'].name)
+
+        req = self.create_request(path_info='/browser/deny')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('BROWSER_VIEW', e.action)
+            self.assertEqual('source', e.resource.realm)
+            self.assertEqual('/', e.resource.id)
+            self.assertEqual('repository', e.resource.parent.realm)
+            self.assertEqual('deny', e.resource.parent.id)
+
+        DbRepositoryProvider(self.env).remove_repository('(default)')
+        req = self.create_request(path_info='/browser/')
+        rv = self.process_request(req)
+        self.assertEqual(None, rv[1]['repos'])
+
+        req = self.create_request(path_info='/browser/blah-blah-file')
+        try:
+            self.process_request(req)
+            self.fail('ResourceNotFound not raised')
+        except ResourceNotFound, e:
+            self.assertEqual('No node blah-blah-file', unicode(e))
+
+    def test_repository_without_browser_view(self):
+        req = self.create_request(path_info='/browser/')
+        rv = self.process_request(req)
+        # cannot view default repository but don't raise PermissionError
+        self.assertEqual(None, rv[1]['repos'])
+
+        req = self.create_request(path_info='/browser/allow')
+        rv = self.process_request(req)
+        self.assertEqual('allow', rv[1]['repos'].name)
+
+        req = self.create_request(path_info='/browser/deny')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('BROWSER_VIEW', e.action)
+            self.assertEqual('source', e.resource.realm)
+            self.assertEqual('/', e.resource.id)
+            self.assertEqual('repository', e.resource.parent.realm)
+            self.assertEqual('deny', e.resource.parent.id)
+
+        DbRepositoryProvider(self.env).remove_repository('(default)')
+        req = self.create_request(path_info='/browser/')
+        rv = self.process_request(req)
+        self.assertEqual(None, rv[1]['repos'])
+
+        req = self.create_request(path_info='/browser/blah-blah-file')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('BROWSER_VIEW', e.action)
+            self.assertEqual(None, e.resource)
+
+    def test_node_with_file_view(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW', 'FILE_VIEW')
+
+        req = self.create_request(path_info='/browser/file')
+        rv = self.process_request(req)
+        self.assertEqual('', rv[1]['repos'].name)
+        self.assertEqual('file', rv[1]['path'])
+
+        req = self.create_request(path_info='/browser/allow-file')
+        rv = self.process_request(req)
+        self.assertEqual('', rv[1]['repos'].name)
+        self.assertEqual('allow-file', rv[1]['path'])
+
+        req = self.create_request(path_info='/browser/deny-file')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('FILE_VIEW', e.action)
+            self.assertEqual('source', e.resource.realm)
+            self.assertEqual('deny-file', e.resource.id)
+            self.assertEqual('repository', e.resource.parent.realm)
+            self.assertEqual('', e.resource.parent.id)
+
+    def test_node_in_allowed_repos_with_file_view(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW', 'FILE_VIEW')
+
+        req = self.create_request(path_info='/browser/allow/file')
+        rv = self.process_request(req)
+        self.assertEqual('allow', rv[1]['repos'].name)
+        self.assertEqual('file', rv[1]['path'])
+
+        req = self.create_request(path_info='/browser/allow/allow-file')
+        rv = self.process_request(req)
+        self.assertEqual('allow', rv[1]['repos'].name)
+        self.assertEqual('allow-file', rv[1]['path'])
+
+        req = self.create_request(path_info='/browser/allow/deny-file')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('FILE_VIEW', e.action)
+            self.assertEqual('source', e.resource.realm)
+            self.assertEqual('deny-file', e.resource.id)
+            self.assertEqual('repository', e.resource.parent.realm)
+            self.assertEqual('allow', e.resource.parent.id)
+
+    def test_node_in_denied_repos_with_file_view(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW', 'FILE_VIEW')
+
+        req = self.create_request(path_info='/browser/deny/allow-file')
+        rv = self.process_request(req)
+        self.assertEqual('deny', rv[1]['repos'].name)
+        self.assertEqual('allow-file', rv[1]['path'])
+
+        for path in ('file', 'deny-file'):
+            req = self.create_request(path_info='/browser/deny/' + path)
+            try:
+                self.process_request(req)
+                self.fail('PermissionError not raised (path: %r)' % path)
+            except PermissionError, e:
+                self.assertEqual('FILE_VIEW', e.action)
+                self.assertEqual('source', e.resource.realm)
+                self.assertEqual(path, e.resource.id)
+                self.assertEqual('repository', e.resource.parent.realm)
+                self.assertEqual('deny', e.resource.parent.id)
+
+    def test_missing_node_with_browser_view(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW')
+        req = self.create_request(path_info='/browser/allow/missing')
+        self.assertRaises(ResourceNotFound, self.process_request, req)
+        req = self.create_request(path_info='/browser/deny/missing')
+        self.assertRaises(ResourceNotFound, self.process_request, req)
+        req = self.create_request(path_info='/browser/missing')
+        self.assertRaises(ResourceNotFound, self.process_request, req)
+
+    def test_missing_node_without_browser_view(self):
+        req = self.create_request(path_info='/browser/allow/missing')
+        self.assertRaises(ResourceNotFound, self.process_request, req)
+        req = self.create_request(path_info='/browser/deny/missing')
+        self.assertRaises(ResourceNotFound, self.process_request, req)
+        req = self.create_request(path_info='/browser/missing')
+        self.assertRaises(ResourceNotFound, self.process_request, req)
+
+    def test_repository_index_with_hidden_default_repos(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW', 'FILE_VIEW')
+        provider = DbRepositoryProvider(self.env)
+        provider.modify_repository('(default)', {'hidden': 'enabled'})
+        req = self.create_request(path_info='/browser/')
+        template, data, content_type = self.process_request(req)
+        self.assertEqual(None, data['repos'])
+        repo_data = data['repo']  # for repository index
+        self.assertEqual('allow', repo_data['repositories'][0][0])
+        self.assertEqual('raise', repo_data['repositories'][1][0])
+        self.assertEqual(2, len(repo_data['repositories']))
+
+    def test_node_in_hidden_default_repos(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW', 'FILE_VIEW')
+        provider = DbRepositoryProvider(self.env)
+        provider.modify_repository('(default)', {'hidden': 'enabled'})
+        req = self.create_request(path_info='/browser/blah-blah-file')
+        template, data, content_type = self.process_request(req)
+        self.assertEqual('', data['reponame'])
+        self.assertEqual('blah-blah-file', data['path'])
+
+    def test_no_viewable_repositories_with_browser_view(self):
+        self.grant_perm('anonymous', 'BROWSER_VIEW')
+        provider = DbRepositoryProvider(self.env)
+
+        provider.remove_repository('allow')
+        provider.remove_repository('(default)')
+        provider.remove_repository('raise')
+
+        req = self.create_request(path_info='/browser/')
+        try:
+            self.process_request(req)
+            self.fail('ResourceNotFound not raised')
+        except ResourceNotFound, e:
+            self.assertEqual('No viewable repositories', unicode(e))
+        req = self.create_request(path_info='/browser/allow/')
+        try:
+            self.process_request(req)
+            self.fail('ResourceNotFound not raised')
+        except ResourceNotFound, e:
+            self.assertEqual('No node allow', unicode(e))
+        req = self.create_request(path_info='/browser/deny/')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('BROWSER_VIEW', e.action)
+            self.assertEqual('source', e.resource.realm)
+            self.assertEqual('/', e.resource.id)
+            self.assertEqual('repository', e.resource.parent.realm)
+            self.assertEqual('deny', e.resource.parent.id)
+
+        provider.remove_repository('deny')
+        req = self.create_request(path_info='/browser/')
+        try:
+            self.process_request(req)
+            self.fail('ResourceNotFound not raised')
+        except ResourceNotFound, e:
+            self.assertEqual('No viewable repositories', unicode(e))
+        req = self.create_request(path_info='/browser/deny/')
+        try:
+            self.process_request(req)
+            self.fail('ResourceNotFound not raised')
+        except ResourceNotFound, e:
+            self.assertEqual('No node deny', unicode(e))
+
+    def test_no_viewable_repositories_without_browser_view(self):
+        provider = DbRepositoryProvider(self.env)
+        provider.remove_repository('allow')
+        req = self.create_request(path_info='/browser/')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('BROWSER_VIEW', e.action)
+            self.assertEqual(None, e.resource)
+        provider.remove_repository('deny')
+        provider.remove_repository('(default)')
+        req = self.create_request(path_info='/browser/')
+        try:
+            self.process_request(req)
+            self.fail('PermissionError not raised')
+        except PermissionError, e:
+            self.assertEqual('BROWSER_VIEW', e.action)
+            self.assertEqual(None, e.resource)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    if ConfigObj:
+        suite.addTest(unittest.makeSuite(BrowserModulePermissionsTestCase))
+    else:
+        print("SKIP: %s.%s (no configobj installed)" %
+              (BrowserModulePermissionsTestCase.__module__,
+               BrowserModulePermissionsTestCase.__name__))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')

Added: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/changeset.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/changeset.py?rev=1639602&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/changeset.py (added)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/changeset.py Fri Nov 14 11:06:23 2014
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2014 Edgewall Software
+# All rights reserved.
+#
+# 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/.
+
+import unittest
+
+from trac.core import TracError
+from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.versioncontrol.web_ui.changeset import ChangesetModule
+
+
+class ChangesetModuleTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.env = EnvironmentStub()
+        self.cm = ChangesetModule(self.env)
+
+    def test_default_repository_not_configured(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11599."""
+        req = Mock(perm=MockPerm(), args={'new_path': '/'},
+                   get_header=lambda self: None)
+        self.assertRaises(TracError, self.cm.process_request, req)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(ChangesetModuleTestCase))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')

Added: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/log.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/log.py?rev=1639602&view=auto
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/log.py (added)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/log.py Fri Nov 14 11:06:23 2014
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2014 Edgewall Software
+# All rights reserved.
+#
+# 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/.
+
+import unittest
+
+from trac.core import TracError
+from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.versioncontrol.web_ui.log import LogModule
+
+
+class LogModuleTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.env = EnvironmentStub()
+        self.lm = LogModule(self.env)
+
+    def test_default_repository_not_configured(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11599."""
+        req = Mock(perm=MockPerm(), args={'new_path': '/'})
+        self.assertRaises(TracError, self.lm.process_request, req)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(LogModuleTestCase))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')

Modified: bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/wikisyntax.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/wikisyntax.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/wikisyntax.py (original)
+++ bloodhound/vendor/trac/current/trac/versioncontrol/web_ui/tests/wikisyntax.py Fri Nov 14 11:06:23 2014
@@ -14,7 +14,6 @@
 import unittest
 
 from trac.test import Mock
-from trac.versioncontrol import NoSuchChangeset, NoSuchNode
 from trac.versioncontrol.api import *
 from trac.versioncontrol.web_ui import *
 from trac.wiki.tests import formatter

Modified: bloodhound/vendor/trac/current/trac/web/api.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/api.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/api.py (original)
+++ bloodhound/vendor/trac/current/trac/web/api.py Fri Nov 14 11:06:23 2014
@@ -29,10 +29,12 @@ from StringIO import StringIO
 import sys
 import urlparse
 
+from genshi.builder import Fragment
 from trac.core import Interface, TracError
+from trac.perm import PermissionError
 from trac.util import get_last_traceback, unquote
 from trac.util.datefmt import http_date, localtz
-from trac.util.text import empty, to_unicode
+from trac.util.text import empty, exception_to_unicode, to_unicode
 from trac.util.translation import _
 from trac.web.href import Href
 from trac.web.wsgi import _FileWrapper
@@ -131,7 +133,7 @@ HTTP_STATUS = dict([(code, reason.title(
 class HTTPException(Exception):
 
     def __init__(self, detail, *args):
-        if isinstance(detail, TracError):
+        if isinstance(detail, (TracError, PermissionError)):
             self.detail = detail.message
             self.reason = detail.title
         else:
@@ -141,6 +143,35 @@ class HTTPException(Exception):
         Exception.__init__(self, '%s %s (%s)' % (self.code, self.reason,
                                                  self.detail))
 
+    @property
+    def message(self):
+        # The message is based on the e.detail, which can be an Exception
+        # object, but not a TracError one: when creating HTTPException,
+        # a TracError.message is directly assigned to e.detail
+        if isinstance(self.detail, Exception): # not a TracError or PermissionError
+            message = exception_to_unicode(self.detail)
+        elif isinstance(self.detail, Fragment): # TracError or PermissionError markup
+            message = self.detail
+        else:
+            message = to_unicode(self.detail)
+        return message
+
+    @property
+    def title(self):
+        try:
+            # We first try to get localized error messages here, but we
+            # should ignore secondary errors if the main error was also
+            # due to i18n issues
+            title = _("Error")
+            if self.reason:
+                if title.lower() in self.reason.lower():
+                    title = self.reason
+                else:
+                    title = _("Error: %(message)s", message=self.reason)
+        except Exception:
+            title = "Error"
+        return title
+
     @classmethod
     def subclass(cls, name, code):
         """Create a new Exception class representing a HTTP status code."""

Modified: bloodhound/vendor/trac/current/trac/web/chrome.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/chrome.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/chrome.py (original)
+++ bloodhound/vendor/trac/current/trac/web/chrome.py Fri Nov 14 11:06:23 2014
@@ -178,7 +178,7 @@ def add_script_data(req, data={}, **kwar
     script_data.update(kwargs)
 
 def add_javascript(req, filename):
-    """:deprecated: use `add_script` instead."""
+    """:deprecated: since 0.10, use `add_script` instead."""
     add_script(req, filename, mimetype='text/javascript')
 
 def add_warning(req, msg, *args):
@@ -274,6 +274,8 @@ def web_context(req, resource=None, id=F
                      name)
     :return: a new rendering context
     :rtype: `RenderingContext`
+
+    :since: version 1.0
     """
     if req:
         href = req.abs_href if absurls else req.href
@@ -313,9 +315,7 @@ def _chrome_resource_path(req, filename)
     elif filename.startswith('common/') and 'htdocs_location' in req.chrome:
         return Href(req.chrome['htdocs_location'])(filename[7:])
     else:
-        href = req.href
-        if not filename.startswith('/'):
-            href = href.chrome
+        href = req.href if filename.startswith('/') else req.href.chrome
         return href(filename)
 
 
@@ -503,6 +503,9 @@ class Chrome(Component):
 
     templates = None
 
+    # DocType for 'text/html' output
+    html_doctype = DocType.XHTML_STRICT
+
     # A dictionary of default context data for templates
     _default_context_data = {
         '_': translation.gettext,
@@ -970,9 +973,10 @@ class Chrome(Component):
         """Render the `filename` using the `data` for the context.
 
         The `content_type` argument is used to choose the kind of template
-        used (`NewTextTemplate` if `'text/plain'`, `MarkupTemplate` otherwise),
-        and tweak the rendering process (use of XHTML Strict doctype if
-        `'text/html'` is given).
+        used (`NewTextTemplate` if `'text/plain'`, `MarkupTemplate`
+        otherwise), and tweak the rendering process. Doctype for `'text/html'`
+        can be specified by setting the `html_doctype` attribute (default
+        is `XHTML_STRICT`)
 
         When `fragment` is specified, the (filtered) Genshi stream is
         returned.
@@ -1012,8 +1016,9 @@ class Chrome(Component):
             stream.render('text', out=buffer, encoding='utf-8')
             return buffer.getvalue()
 
-        doctype = {'text/html': DocType.XHTML_STRICT}.get(content_type)
-        if doctype:
+        doctype = None
+        if content_type == 'text/html':
+            doctype = self.html_doctype
             if req.form_token:
                 stream |= self._add_form_token(req.form_token)
             if not int(req.session.get('accesskeys', 0)):

Modified: bloodhound/vendor/trac/current/trac/web/main.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/main.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/main.py (original)
+++ bloodhound/vendor/trac/current/trac/web/main.py Fri Nov 14 11:06:23 2014
@@ -28,7 +28,7 @@ from pprint import pformat, pprint
 import re
 import sys
 
-from genshi.builder import Fragment, tag
+from genshi.builder import tag
 from genshi.output import DocType
 from genshi.template import TemplateLoader
 
@@ -41,14 +41,14 @@ from trac.loader import get_plugin_info,
 from trac.perm import PermissionCache, PermissionError
 from trac.resource import ResourceNotFound
 from trac.util import arity, get_frame_info, get_last_traceback, hex_entropy, \
-                      read_file, safe_repr, translation
+                      read_file, safe_repr, translation, warn_setuptools_issue
 from trac.util.concurrency import threading
 from trac.util.datefmt import format_datetime, localtz, timezone, user_time
 from trac.util.text import exception_to_unicode, shorten_line, to_unicode
 from trac.util.translation import _, get_negotiated_locale, has_babel, \
                                   safefmt, tag_
 from trac.web.api import *
-from trac.web.chrome import Chrome, add_warning
+from trac.web.chrome import Chrome, add_notice, add_warning
 from trac.web.href import Href
 from trac.web.session import Session
 
@@ -256,7 +256,7 @@ class RequestDispatcher(Component):
                                    exception_to_unicode(e, traceback=True))
                 raise err[0], err[1], err[2]
         except PermissionError, e:
-            raise HTTPForbidden(to_unicode(e))
+            raise HTTPForbidden(e)
         except ResourceNotFound, e:
             raise HTTPNotFound(e)
         except TracError, e:
@@ -349,6 +349,7 @@ class RequestDispatcher(Component):
         return resp
 
 
+_warn_setuptools = False
 _slashes_re = re.compile(r'/+')
 
 def dispatch_request(environ, start_response):
@@ -358,6 +359,11 @@ def dispatch_request(environ, start_resp
     :param start_response: the WSGI callback for starting the response
     """
 
+    global _warn_setuptools
+    if _warn_setuptools is False:
+        _warn_setuptools = True
+        warn_setuptools_issue(out=environ.get('wsgi.errors'))
+
     # SCRIPT_URL is an Apache var containing the URL before URL rewriting
     # has been applied, so we can use it to reconstruct logical SCRIPT_NAME
     script_url = environ.get('SCRIPT_URL')
@@ -518,35 +524,13 @@ def _send_user_error(req, env, e):
     # See trac/web/api.py for the definition of HTTPException subclasses.
     if env:
         env.log.warn('[%s] %s' % (req.remote_addr, exception_to_unicode(e)))
-    try:
-        # We first try to get localized error messages here, but we
-        # should ignore secondary errors if the main error was also
-        # due to i18n issues
-        title = _('Error')
-        if e.reason:
-            if title.lower() in e.reason.lower():
-                title = e.reason
-            else:
-                title = _('Error: %(message)s', message=e.reason)
-    except Exception:
-        title = 'Error'
-    # The message is based on the e.detail, which can be an Exception
-    # object, but not a TracError one: when creating HTTPException,
-    # a TracError.message is directly assigned to e.detail
-    if isinstance(e.detail, Exception): # not a TracError
-        message = exception_to_unicode(e.detail)
-    elif isinstance(e.detail, Fragment): # markup coming from a TracError
-        message = e.detail
-    else:
-        message = to_unicode(e.detail)
-    data = {'title': title, 'type': 'TracError', 'message': message,
+    data = {'title': e.title, 'type': 'TracError', 'message': e.message,
             'frames': [], 'traceback': None}
     if e.code == 403 and req.authname == 'anonymous':
         # TRANSLATOR: ... not logged in, you may want to 'do so' now (link)
         do_so = tag.a(_("do so"), href=req.href.login())
-        req.chrome['notices'].append(
-            tag_("You are currently not logged in. You may want to "
-                 "%(do_so)s now.", do_so=do_so))
+        add_notice(req, tag_("You are currently not logged in. You may want "
+                             "to %(do_so)s now.", do_so=do_so))
     try:
         req.send_error(sys.exc_info(), status=e.code, env=env, data=data)
     except RequestDone:
@@ -598,7 +582,9 @@ def send_internal_error(env, req, exc_in
             sys_info = "".join("|| '''`%s`''' || `%s` ||\n"
                                % (k, v.replace('\n', '` [[br]] `'))
                                for k, v in env.get_systeminfo())
-            sys_info += "|| '''`jQuery`''' || `#JQUERY#` ||\n"
+            sys_info += "|| '''`jQuery`''' || `#JQUERY#` ||\n" \
+                        "|| '''`jQuery UI`''' || `#JQUERYUI#` ||\n" \
+                        "|| '''`jQuery Timepicker`''' || `#JQUERYTP#` ||\n"
             enabled_plugins = "".join("|| '''`%s`''' || `%s` ||\n"
                                       % (p['name'], p['version'] or _('N/A'))
                                       for p in plugins)
@@ -645,6 +631,7 @@ User agent: `#USER_AGENT#`
             'tracker': tracker, 'tracker_args': tracker_args,
             'description': description, 'description_en': description_en}
 
+    Chrome(env).add_jquery_ui(req)
     try:
         req.send_error(exc_info, status=500, env=env, data=data)
     except RequestDone:

Modified: bloodhound/vendor/trac/current/trac/web/session.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/session.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/session.py (original)
+++ bloodhound/vendor/trac/current/trac/web/session.py Fri Nov 14 11:06:23 2014
@@ -358,8 +358,7 @@ class SessionAdmin(Component):
                self._complete_delete, self._do_delete)
 
         yield ('session purge', '<age>',
-               """Purge all anonymous sessions older than the given age or
-               date
+               """Purge anonymous sessions older than the given age or date
 
                Age may be specified as a relative time like "90 days ago", or
                as a date in the "%(datetime)s" or "%(iso8601)s" (ISO 8601)

Modified: bloodhound/vendor/trac/current/trac/web/standalone.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/standalone.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/standalone.py (original)
+++ bloodhound/vendor/trac/current/trac/web/standalone.py Fri Nov 14 11:06:23 2014
@@ -28,7 +28,6 @@ from SocketServer import ThreadingMixIn
 
 from trac import __version__ as VERSION
 from trac.util import autoreload, daemon
-from trac.web.api import Request
 from trac.web.auth import BasicAuthentication, DigestAuthentication
 from trac.web.main import dispatch_request
 from trac.web.wsgi import WSGIServer, WSGIRequestHandler

Modified: bloodhound/vendor/trac/current/trac/web/tests/api.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/tests/api.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/tests/api.py (original)
+++ bloodhound/vendor/trac/current/trac/web/tests/api.py Fri Nov 14 11:06:23 2014
@@ -11,23 +11,100 @@
 # individuals. For the exact contribution history, see the revision
 # history and logs, available at http://trac.edgewall.org/log/.
 
-from trac.test import Mock
+import os.path
+import shutil
+import sys
+import tempfile
+import unittest
+from StringIO import StringIO
+
+import trac.tests.compat
+from trac import perm
+from trac.core import TracError
+from trac.test import EnvironmentStub, Mock, MockPerm, locale_en
+from trac.util import create_file
+from trac.util.datefmt import utc
+from trac.util.text import shorten_line
 from trac.web.api import Request, RequestDone, parse_arg_list
+from tracopt.perm.authz_policy import AuthzPolicy
 
-from StringIO import StringIO
-import unittest
+
+class RequestHandlerPermissionsTestCaseBase(unittest.TestCase):
+
+    authz_policy = None
+
+    def setUp(self, module_class):
+        self.path = tempfile.mkdtemp(prefix='trac-')
+        if self.authz_policy is not None:
+            self.authz_file = os.path.join(self.path, 'authz_policy.conf')
+            create_file(self.authz_file, self.authz_policy)
+            self.env = EnvironmentStub(enable=['trac.*', AuthzPolicy],
+                                       path=self.path)
+            self.env.config.set('authz_policy', 'authz_file', self.authz_file)
+            self.env.config.set('trac', 'permission_policies',
+                                'AuthzPolicy, DefaultPermissionPolicy')
+        else:
+            self.env = EnvironmentStub(path=self.path)
+        self.req_handler = module_class(self.env)
+
+    def tearDown(self):
+        self.env.reset_db()
+        shutil.rmtree(self.path)
+
+    def create_request(self, authname='anonymous', **kwargs):
+        kw = {'perm': perm.PermissionCache(self.env, authname), 'args': {},
+              'href': self.env.href, 'abs_href': self.env.abs_href,
+              'tz': utc, 'locale': None, 'lc_time': locale_en,
+              'chrome': {'notices': [], 'warnings': []},
+              'method': None, 'get_header': lambda v: None}
+        kw.update(kwargs)
+        return Mock(**kw)
+
+    def get_navigation_items(self, req):
+        return self.req_handler.get_navigation_items(req)
+
+    def grant_perm(self, username, *actions):
+        permsys = perm.PermissionSystem(self.env)
+        for action in actions:
+            permsys.grant_permission(username, action)
+
+    def process_request(self, req):
+        self.assertTrue(self.req_handler.match_request(req))
+        return self.req_handler.process_request(req)
+
+
+def _make_environ(scheme='http', server_name='example.org',
+                  server_port=80, method='GET', script_name='/trac',
+                  **kwargs):
+    environ = {'wsgi.url_scheme': scheme, 'wsgi.input': StringIO(''),
+               'REQUEST_METHOD': method, 'SERVER_NAME': server_name,
+               'SERVER_PORT': server_port, 'SCRIPT_NAME': script_name}
+    environ.update(kwargs)
+    return environ
+
+
+def _make_req(environ, start_response, args={}, arg_list=(), authname='admin',
+              form_token='A' * 40, chrome={'links': {}, 'scripts': []},
+              perm=MockPerm(), session={}, tz=utc, locale=None, **kwargs):
+    req = Request(environ, start_response)
+    req.args = args
+    req.arg_list = arg_list
+    req.authname = authname
+    req.form_token = form_token
+    req.chrome = chrome
+    req.perm = perm
+    req.session = session
+    req.tz = tz
+    req.locale = locale
+    for name, value in kwargs.iteritems():
+        setattr(req, name, value)
+    return req
 
 
 class RequestTestCase(unittest.TestCase):
 
-    def _make_environ(self, scheme='http', server_name='example.org',
-                      server_port=80, method='GET', script_name='/trac',
-                      **kwargs):
-        environ = {'wsgi.url_scheme': scheme, 'wsgi.input': StringIO(''),
-                   'REQUEST_METHOD': method, 'SERVER_NAME': server_name,
-                   'SERVER_PORT': server_port, 'SCRIPT_NAME': script_name}
-        environ.update(kwargs)
-        return environ
+    def _make_environ(self, *args, **kwargs):
+        return _make_environ(*args, **kwargs)
 
     def test_base_url(self):
         environ = self._make_environ()
@@ -153,6 +230,118 @@ class RequestTestCase(unittest.TestCase)
         self.assertEqual('bar', req.args['action'])
 
 
+class SendErrorTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.env = EnvironmentStub()
+
+    def tearDown(self):
+        self.env.reset_db()
+
+    def test_trac_error(self):
+        content = self._send_error(error_klass=TracError)
+        self.assertIn('<p class="message">Oops!</p>', content)
+        self.assertNotIn('<strong>Trac detected an internal error:</strong>',
+                         content)
+        self.assertNotIn('There was an internal error in Trac.', content)
+
+    def test_internal_error_for_non_admin(self):
+        content = self._send_error(perm={})
+        self.assertIn('There was an internal error in Trac.', content)
+        self.assertIn('<p>To that end, you could', content)
+        self.assertNotIn('This is probably a local installation issue.',
+                         content)
+        self.assertNotIn('<h2>Found a bug in Trac?</h2>', content)
+
+    def test_internal_error_with_admin_trac_for_non_admin(self):
+        content = self._send_error(perm={},
+                                   admin_trac_url='http://example.org/admin')
+        self.assertIn('There was an internal error in Trac.', content)
+        self.assertIn('<p>To that end, you could', content)
+        self.assertIn(' action="http://example.org/admin/newticket#"', content)
+        self.assertNotIn('This is probably a local installation issue.',
+                         content)
+        self.assertNotIn('<h2>Found a bug in Trac?</h2>', content)
+
+    def test_internal_error_without_admin_trac_for_non_admin(self):
+        content = self._send_error(perm={}, admin_trac_url='')
+        self.assertIn('There was an internal error in Trac.', content)
+        self.assertNotIn('<p>To that end, you could', content)
+        self.assertNotIn('This is probably a local installation issue.',
+                         content)
+        self.assertNotIn('<h2>Found a bug in Trac?</h2>', content)
+
+    def test_internal_error_for_admin(self):
+        content = self._send_error()
+        self.assertNotIn('There was an internal error in Trac.', content)
+        self.assertIn('This is probably a local installation issue.', content)
+        self.assertNotIn('a ticket at the admin Trac to report', content)
+        self.assertIn('<h2>Found a bug in Trac?</h2>', content)
+        self.assertIn('<p>Otherwise, please', content)
+        self.assertIn(' action="http://example.org/tracker/newticket"',
+                      content)
+
+    def test_internal_error_with_admin_trac_for_admin(self):
+        content = self._send_error(admin_trac_url='http://example.org/admin')
+        self.assertNotIn('There was an internal error in Trac.', content)
+        self.assertIn('This is probably a local installation issue.', content)
+        self.assertIn('a ticket at the admin Trac to report', content)
+        self.assertIn(' action="http://example.org/admin/newticket#"', content)
+        self.assertIn('<h2>Found a bug in Trac?</h2>', content)
+        self.assertIn('<p>Otherwise, please', content)
+        self.assertIn(' action="http://example.org/tracker/newticket"',
+                      content)
+
+    def test_internal_error_without_admin_trac_for_admin(self):
+        content = self._send_error(admin_trac_url='')
+        self.assertNotIn('There was an internal error in Trac.', content)
+        self.assertIn('This is probably a local installation issue.', content)
+        self.assertNotIn('a ticket at the admin Trac to report', content)
+        self.assertIn('<h2>Found a bug in Trac?</h2>', content)
+        self.assertIn('<p>Otherwise, please', content)
+        self.assertIn(' action="http://example.org/tracker/newticket"',
+                      content)
+
+    def _send_error(self, admin_trac_url='.', perm=None,
+                    error_klass=ValueError):
+        self.env.config.set('project', 'admin_trac_url', admin_trac_url)
+        self.assertEquals(admin_trac_url, self.env.project_admin_trac_url)
+
+        content = StringIO()
+        result = {'status': None, 'headers': []}
+        def write(data):
+            content.write(data)
+        def start_response(status, headers, exc_info=None):
+            result['status'] = status
+            result['headers'].extend(headers)
+            return write
+        environ = _make_environ()
+        req = _make_req(environ, start_response)
+        try:
+            raise error_klass('Oops!')
+        except:
+            exc_info = sys.exc_info()
+        data = {'title': 'Internal Error',
+                'type': ('internal', 'TracError')[error_klass is TracError],
+                'message': 'Oops!', 'traceback': None, 'frames': [],
+                'shorten_line': shorten_line,
+                'plugins': [], 'faulty_plugins': [],
+                'tracker': 'http://example.org/tracker', 'tracker_args': {},
+                'description': '', 'description_en': '',
+                'get_systeminfo': lambda: ()}
+        if perm is not None:
+            data['perm'] = perm
+
+        self.assertRaises(RequestDone, req.send_error, exc_info, env=self.env,
+                          data=data)
+        content = content.getvalue().decode('utf-8')
+        self.assertIn('<!DOCTYPE ', content)
+        self.assertEquals('500', result['status'].split()[0])
+        self.assertIn(('Content-Type', 'text/html;charset=utf-8'),
+                      result['headers'])
+        return content
+
+
 class ParseArgListTestCase(unittest.TestCase):
 
     def test_qs_str(self):
@@ -177,6 +366,7 @@ class ParseArgListTestCase(unittest.Test
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(RequestTestCase))
+    suite.addTest(unittest.makeSuite(SendErrorTestCase))
     suite.addTest(unittest.makeSuite(ParseArgListTestCase))
     return suite
 

Modified: bloodhound/vendor/trac/current/trac/web/tests/auth.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/tests/auth.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/tests/auth.py (original)
+++ bloodhound/vendor/trac/current/trac/web/tests/auth.py Fri Nov 14 11:06:23 2014
@@ -13,9 +13,9 @@
 
 import os
 
+import trac.tests.compat
 from trac.core import TracError
 from trac.test import EnvironmentStub, Mock
-from trac.tests import compat
 from trac.web.auth import BasicAuthentication, LoginModule
 from trac.web.href import Href
 

Modified: bloodhound/vendor/trac/current/trac/web/tests/chrome.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/tests/chrome.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/tests/chrome.py (original)
+++ bloodhound/vendor/trac/current/trac/web/tests/chrome.py Fri Nov 14 11:06:23 2014
@@ -16,9 +16,9 @@ import shutil
 import tempfile
 import unittest
 
+import trac.tests.compat
 from trac.core import Component, TracError, implements
 from trac.test import EnvironmentStub, locale_en
-from trac.tests import compat
 from trac.tests.contentgen import random_sentence
 from trac.util import create_file
 from trac.web.chrome import (
@@ -85,8 +85,10 @@ class ChromeTestCase(unittest.TestCase):
         add_script(req, 'common/js/trac.js')
         add_script(req, 'http://example.com/trac.js')
         add_script(req, '//example.com/trac.js')
+        add_script(req, '/dynamic.js')
+        add_script(req, 'plugin/js/plugin.js')
         scripts = req.chrome['scripts']
-        self.assertEqual(3, len(scripts))
+        self.assertEqual(5, len(scripts))
         self.assertEqual('text/javascript', scripts[0]['type'])
         self.assertEqual('/trac.cgi/chrome/common/js/trac.js',
                          scripts[0]['href'])
@@ -96,6 +98,10 @@ class ChromeTestCase(unittest.TestCase):
         self.assertEqual('text/javascript', scripts[2]['type'])
         self.assertEqual('//example.com/trac.js',
                          scripts[2]['href'])
+        self.assertEqual('/trac.cgi/dynamic.js',
+                         scripts[3]['href'])
+        self.assertEqual('/trac.cgi/chrome/plugin/js/plugin.js',
+                         scripts[4]['href'])
 
     def test_add_script_data(self):
         req = Request(href=Href('/trac.cgi'))
@@ -110,8 +116,10 @@ class ChromeTestCase(unittest.TestCase):
         add_stylesheet(req, 'common/css/trac.css')
         add_stylesheet(req, 'https://example.com/trac.css')
         add_stylesheet(req, '//example.com/trac.css')
+        add_stylesheet(req, '/dynamic.css')
+        add_stylesheet(req, 'plugin/css/plugin.css')
         links = req.chrome['links']['stylesheet']
-        self.assertEqual(3, len(links))
+        self.assertEqual(5, len(links))
         self.assertEqual('text/css', links[0]['type'])
         self.assertEqual('/trac.cgi/chrome/common/css/trac.css',
                          links[0]['href'])
@@ -121,6 +129,10 @@ class ChromeTestCase(unittest.TestCase):
         self.assertEqual('text/css', links[2]['type'])
         self.assertEqual('//example.com/trac.css',
                          links[2]['href'])
+        self.assertEqual('/trac.cgi/dynamic.css',
+                         links[3]['href'])
+        self.assertEqual('/trac.cgi/chrome/plugin/css/plugin.css',
+                         links[4]['href'])
 
     def test_add_stylesheet_media(self):
         req = Request(base_path='/trac.cgi', href=Href('/trac.cgi'))

Modified: bloodhound/vendor/trac/current/trac/web/tests/href.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/tests/href.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/tests/href.py (original)
+++ bloodhound/vendor/trac/current/trac/web/tests/href.py Fri Nov 14 11:06:23 2014
@@ -15,8 +15,8 @@
 import doctest
 import unittest
 
+import trac.tests.compat
 import trac.web.href
-from trac.tests import compat
 
 
 class HrefTestCase(unittest.TestCase):

Modified: bloodhound/vendor/trac/current/trac/web/tests/session.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/tests/session.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/tests/session.py (original)
+++ bloodhound/vendor/trac/current/trac/web/tests/session.py Fri Nov 14 11:06:23 2014
@@ -18,8 +18,8 @@ import time
 from datetime import datetime
 import unittest
 
+import trac.tests.compat
 from trac.test import EnvironmentStub, Mock
-from trac.tests import compat
 from trac.web.session import DetachedSession, Session, PURGE_AGE, \
                              UPDATE_INTERVAL, SessionAdmin
 from trac.core import TracError

Modified: bloodhound/vendor/trac/current/trac/web/wsgi.py
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/web/wsgi.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/web/wsgi.py (original)
+++ bloodhound/vendor/trac/current/trac/web/wsgi.py Fri Nov 14 11:06:23 2014
@@ -194,9 +194,15 @@ class WSGIRequestHandler(BaseHTTPRequest
 
     def finish(self):
         """We need to help the garbage collector a little."""
-        BaseHTTPRequestHandler.finish(self)
-        self.wfile = None
-        self.rfile = None
+        try:
+            BaseHTTPRequestHandler.finish(self)
+        except (IOError, socket.error), e:
+            # ignore an exception if client disconnects
+            if e.args[0] not in (errno.EPIPE, errno.ECONNRESET, 10053, 10054):
+                raise
+        finally:
+            self.wfile = None
+            self.rfile = None
 
 
 class WSGIServerGateway(WSGIGateway):

Modified: bloodhound/vendor/trac/current/trac/wiki/default-pages/InterMapTxt
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/wiki/default-pages/InterMapTxt?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/wiki/default-pages/InterMapTxt (original)
+++ bloodhound/vendor/trac/current/trac/wiki/default-pages/InterMapTxt Fri Nov 14 11:06:23 2014
@@ -38,22 +38,53 @@ Also note that !InterWiki prefixes are c
 == Prefix Definitions ==
 
 {{{
-PEP     http://www.python.org/peps/pep-$1.html    # Python Enhancement Proposal 
+PEP     http://www.python.org/dev/peps/pep-$1/    # Python Enhancement Proposal 
 PythonBug    http://bugs.python.org/issue$1       # Python Issue #$1
 Python-issue http://bugs.python.org/issue$1       # Python Issue #$1
 
 Trac-ML  http://thread.gmane.org/gmane.comp.version-control.subversion.trac.general/ # Message $1 in Trac Mailing List
 trac-dev http://thread.gmane.org/gmane.comp.version-control.subversion.trac.devel/   # Message $1 in Trac Development Mailing List
 
+apidoc http://www.edgewall.org/docs/trac-trunk/html/$1.html # $1 in the API documentation for Trac
+apiref http://www.edgewall.org/docs/trac-trunk/epydoc/$1.html # $1 in the Epydoc API reference for Trac
+
+bitten   http://bitten.edgewall.org/intertrac/    # Bitten's Trac
+
 Mercurial http://www.selenic.com/mercurial/wiki/index.cgi/ # the wiki for the Mercurial distributed SCM
+hg        http://www.selenic.com/hg/rev/$1?rev=$2          # Changeset $1 $2 in Mercurial repository
+hg-issue  http://mercurial.selenic.com/bts/issue           # Issue $1 in Mercurial BTS
 
 RFC       http://tools.ietf.org/html/rfc$1          # IETF's RFC $1
 ISO       http://en.wikipedia.org/wiki/ISO_         # ISO Standard $1 in Wikipedia
 kb        http://support.microsoft.com/kb/$1/en-us/ # Article $1 in Microsoft's Knowledge Base
 
+pypi        http://pypi.python.org/pypi/   # $1 package in the Python Package Index
+CheeseShop  http://pypi.python.org/pypi/           # $1 package in the Python Package Index
+peak        http://peak.telecommunity.com/DevCenter/     # $1 in Python Enterprise Application Kit's Wiki
+setuptools-issue http://bugs.python.org/setuptools/issue # issue$1 in legacy Setuptools tracker
+pypa-setuptools-issue https://bitbucket.org/pypa/setuptools/issue/ # issue #$1 in BitBucket Setuptools tracker
+
+SQLite      http://www.sqlite.org/cvstrac/wiki?p=$1     # $1 page in the CvsTrac for SQLite
+SQLiteTkt   http://www.sqlite.org/cvstrac/tktview?tn=$1 # Ticket $1 in the CvsTrac for SQLite
+
+mysql-bugs  http://bugs.mysql.com/bug.php?id=  # Bug #$1 in MySQL's bug database
+mysql-issue http://bugs.mysql.com/bug.php?id=  # Bug #$1 in MySQL's bug database
+
+MODPYTHON          http://issues.apache.org/jira/browse/MODPYTHON- # Issue $1 in mod_python's JIRA instance
+mod-python-issue   http://issues.apache.org/jira/browse/MODPYTHON- # Issue $1 in mod_python's JIRA instance
+
+SvnWiki     http://www.orcaware.com/svn/wiki/                        # Subversion Wiki
+svnissue    http://subversion.tigris.org/issues/show_bug.cgi?id=     # Subversion issue #$1
+svn-issue   http://subversion.tigris.org/issues/show_bug.cgi?id=     # Subversion issue #$1
+svncset     http://svn.collab.net/viewvc/svn?view=revision&revision= # Subversion [$1]
+
+mod-wsgi    http://code.google.com/p/modwsgi/wiki/                 # mod_wsgi Wiki on Google Code
+mod-wsgi-issue  http://code.google.com/p/modwsgi/issues/detail?id= # mod_wsgi Issue Tracker on Google Code
+
 chromium-issue  http://code.google.com/p/chromium/issues/detail?id=
 
 Django      http://code.djangoproject.com/intertrac/ # Django's Trac
+AgileTrac   http://www.agile-trac.org/intertrac/     # Plugin adding Iterations to Trac
 
 CreoleWiki   http://wikicreole.org/wiki/
 Creole1Wiki  http://wikicreole.org/wiki/

Modified: bloodhound/vendor/trac/current/trac/wiki/default-pages/InterTrac
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/wiki/default-pages/InterTrac?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/wiki/default-pages/InterTrac (original)
+++ bloodhound/vendor/trac/current/trac/wiki/default-pages/InterTrac Fri Nov 14 11:06:23 2014
@@ -42,8 +42,7 @@ It is necessary to setup a configuration
 This configuration has to be done in the TracIni file, `[intertrac]` section.
 
 Example configuration:
-{{{
-...
+{{{#!ini
 [intertrac]
 # -- Example of setting up an alias:
 t = trac
@@ -66,7 +65,7 @@ a ''compatibility'' mode:
    ([trac:r3526 r3526] to be precise), then it doesn't know how to dispatch an InterTrac 
    link, and it's up to the local Trac to prepare the correct link. 
    Not all links will work that way, but the most common do. 
-   This is called the compatibility mode, and is `true` by default. 
+   This is called the compatibility mode, and is `false` by default. 
  * If you know that the remote Trac knows how to dispatch InterTrac links, 
    you can explicitly disable this compatibility mode and then ''any'' 
    TracLinks can become an InterTrac link.

Modified: bloodhound/vendor/trac/current/trac/wiki/default-pages/PageTemplates
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/wiki/default-pages/PageTemplates?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/wiki/default-pages/PageTemplates (original)
+++ bloodhound/vendor/trac/current/trac/wiki/default-pages/PageTemplates Fri Nov 14 11:06:23 2014
@@ -1,6 +1,6 @@
 = Wiki Page Templates = 
 
-  ''(since [http://trac.edgewall.org/milestone/0.11 0.11])''
+  ''(since [trac:milestone:0.11 0.11])''
 
 The default content for a new wiki page can be chosen from a list of page templates. 
 

Modified: bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAccessibility
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAccessibility?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAccessibility (original)
+++ bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAccessibility Fri Nov 14 11:06:23 2014
@@ -4,9 +4,9 @@ Not every user has a graphic environment
 
 The keyboard shortcuts must be enabled for a session through the [/prefs/keybindings Keyboard Shortcuts] preferences panel.
 
-Trac supports accessibility keys for the most common operations.
+Trac supports accessibility keys for the most common operations. The access keys differ by browser and the following work for several browsers, but see [http://en.wikipedia.org/wiki/Access_key#Access_in_different_browsers access in different browsers] for more details.
  - on Linux platforms, press any of the keys listed below in combination with the `<Alt>` key 
- - on a Mac, use the `<ctrl>` key instead
+ - on a Mac, use the `<Ctrl>` + `<Opt>` key instead
  - on Windows, you need to hit `<Shift> + <Alt> + <Key>`. This works for most browsers (Firefox, Chrome, Safari and Internet Explorer)
 
 == Global Access Keys ==

Modified: bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAdmin
URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAdmin?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAdmin (original)
+++ bloodhound/vendor/trac/current/trac/wiki/default-pages/TracAdmin Fri Nov 14 11:06:23 2014
@@ -32,7 +32,7 @@ This subcommand is very important as it'
 
 [[TracAdminHelp(initenv)]]
 
-It supports an extra `--inherit` option, which can be used to specify a global configuration file which can be used share settings between several environments. You can also inherit from a shared configuration afterwards, by setting the `[inherit] file` option in the `conf/trac.ini` file in your newly created environment, but the advantage of specifying the inherited configuration file at environment creation time is that only the options ''not'' already specified in the global configuration file will be written in the created environment's `conf/trac.ini` file.
+It supports an extra `--inherit` option, which can be used to specify a global configuration file which can be used to share settings between several environments. You can also inherit from a shared configuration afterwards, by setting the `[inherit] file` option in the `conf/trac.ini` file in your newly created environment, but the advantage of specifying the inherited configuration file at environment creation time is that only the options ''not'' already specified in the global configuration file will be written in the created environment's `conf/trac.ini` file.
 See TracIni#GlobalConfiguration.
 
 Note that in version 0.11 of Trac, `initenv` lost an extra last argument `<templatepath>`, which was used in previous versions to point to the `templates` folder. If you are using the one-liner '`trac-admin /path/to/trac/ initenv <projectname> <db> <repostype> <repospath>`' in the above and getting an error that reads ''''`Wrong number of arguments to initenv: 4`'''', then this is because you're using a `trac-admin` script from an '''older''' version of Trac.