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> ------------------------------ &#1; &#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&max=0&order=id">1</span> +New tickets: <span class="query_count" title="1 ticket for which status=new&max=0&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.