Modified: bloodhound/vendor/trac/current/trac/templates/error.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/error.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/error.html (original) +++ bloodhound/vendor/trac/current/trac/templates/error.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"> @@ -39,7 +49,9 @@ $("#traceback pre").hide(); $("#tbtoggle").parent().show(); - $("#systeminfo").append("<tr><th>jQuery</th><td>" + $().jquery + "</td></tr>"); + $("#systeminfo").append("<tr><th>jQuery</th><td>"+$().jquery+"</td></tr>" + + "<tr><th>jQuery UI</th><td>"+$.ui.version+"</td></tr>" + + "<tr><th>jQuery Timepicker</th><td>"+$.timepicker.version+"</td></tr>"); $("#systeminfo").before("<p>User Agent: <tt>" + navigator.userAgent + "</tt></p>"); }); /*]]>*/</script> @@ -48,7 +60,9 @@ $("form.newticket textarea").each(function() { $(this).val($(this).val() .replace(/#USER_AGENT#/m, navigator.userAgent) - .replace(/#JQUERY#/m, $().jquery)); + .replace(/#JQUERY#/m, $().jquery) + .replace(/#JQUERYUI#/m, $.ui.version) + .replace(/#JQUERYTP#/m, $.timepicker.version)); }); }); /*]]>*/</script> @@ -98,13 +112,17 @@ ${description_en if url else description <p>The action that triggered the error was:</p> <pre>${req.method}: ${req.path_info}</pre> </py:when> - <py:otherwise> - <form class="newticket" method="get" action="${project.admin_href.newticket()}#"> + <py:otherwise py:choose=""> + <p py:when="not project.admin_href or project.admin_trac_url == '.'"> + This is probably a local installation issue. + </p> + <form py:otherwise="" + class="newticket" method="get" action="${project.admin_href.newticket()}#"> <p>This is probably a local installation issue. - <py:if test="project.admin_href and project.admin_trac_url != '.'"><i18n:msg params="create"> + <i18n:msg params="create"> You should ${create_ticket()} a ticket at the admin Trac to report the issue. - </i18n:msg></py:if> + </i18n:msg> </p> </form> <h2>Found a bug in Trac?</h2> @@ -140,7 +158,7 @@ ${description_en if url else description <li class="frame" py:for="idx, frame in enumerate(frames)"> <a href="#frame${idx}" id="frame${idx}"><span i18n:msg="file, line, function" py:strip=""> <span class="file">File "${frame.filename}", - line <b>${frame.lineno + 1}</b>, in</span> + line <strong>${frame.lineno + 1}</strong>, in</span> <var>${frame.function}</var></span> </a> <div py:if="frame.line" class="source" style="display: none">
Modified: bloodhound/vendor/trac/current/trac/templates/history_view.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/history_view.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/history_view.html (original) +++ bloodhound/vendor/trac/current/trac/templates/history_view.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"> @@ -15,7 +25,7 @@ <div id="content" class="ticket"> <h1 i18n:msg="name">Change History for <a href="${url or url_of(resource)}">${name or name_of(resource)}</a></h1> - <form py:if="history" class="printableform" method="get" action=""> + <form py:if="history" id="history" class="printableform" method="get" action=""> <div class="buttons"> <input type="hidden" name="action" value="${diff_action or 'diff'}" /> <input py:for="k, v in diff_args or []" type="hidden" name="$k" value="$v"/> Modified: bloodhound/vendor/trac/current/trac/templates/index.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/index.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/index.html (original) +++ bloodhound/vendor/trac/current/trac/templates/index.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/templates/layout.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/layout.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/layout.html (original) +++ bloodhound/vendor/trac/current/trac/templates/layout.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/templates/list_of_attachments.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/list_of_attachments.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/list_of_attachments.html (original) +++ bloodhound/vendor/trac/current/trac/templates/list_of_attachments.html Fri Nov 14 11:06:23 2014 @@ -1,4 +1,13 @@ -<!--! +<!--! Copyright (C) 2010-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/. + Display a list of attachments. Arguments: Modified: bloodhound/vendor/trac/current/trac/templates/macros.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/macros.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/macros.html (original) +++ bloodhound/vendor/trac/current/trac/templates/macros.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/. +--> <div xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/" xmlns:xi="http://www.w3.org/2001/XInclude" Modified: bloodhound/vendor/trac/current/trac/templates/page_index.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/page_index.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/page_index.html (original) +++ bloodhound/vendor/trac/current/trac/templates/page_index.html Fri Nov 14 11:06:23 2014 @@ -1,6 +1,17 @@ -<!--! Display a page index. +<!--! Copyright (C) 2008-2014 Edgewall Software - `paginator` must be a trac.util.presentation.Paginator instance + 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/. + +Display a page index. + +Arguments: + - `paginator` must be a trac.util.presentation.Paginator instance --> <div xmlns="http://www.w3.org/1999/xhtml" Modified: bloodhound/vendor/trac/current/trac/templates/preview_file.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/preview_file.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/preview_file.html (original) +++ bloodhound/vendor/trac/current/trac/templates/preview_file.html Fri Nov 14 11:06:23 2014 @@ -1,4 +1,13 @@ -<!--! +<!--! Copyright (C) 2010-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/. + Display a div for visualizing a preview of a file content. Arguments: Modified: bloodhound/vendor/trac/current/trac/templates/progress_bar.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/progress_bar.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/progress_bar.html (original) +++ bloodhound/vendor/trac/current/trac/templates/progress_bar.html Fri Nov 14 11:06:23 2014 @@ -1,4 +1,13 @@ -<!--! +<!--! Copyright (C) 2010-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/. + Display groups of tickets in a progress bar. Arguments: Modified: bloodhound/vendor/trac/current/trac/templates/progress_bar_grouped.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/progress_bar_grouped.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/progress_bar_grouped.html (original) +++ bloodhound/vendor/trac/current/trac/templates/progress_bar_grouped.html Fri Nov 14 11:06:23 2014 @@ -1,4 +1,13 @@ -<!--! +<!--! Copyright (C) 2011-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/. + Display a table of progress bars for ticket groups Arguments: Modified: bloodhound/vendor/trac/current/trac/templates/theme.html URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/templates/theme.html?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/templates/theme.html (original) +++ bloodhound/vendor/trac/current/trac/templates/theme.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/test.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/test.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/test.py (original) +++ bloodhound/vendor/trac/current/trac/test.py Fri Nov 14 11:06:23 2014 @@ -144,7 +144,8 @@ class TestSetup(unittest.TestSuite): return result def _wrapped_run(self, *args, **kwargs): - "Python 2.7 / unittest2 compatibility - there must be a better way..." + """Python 2.7 / unittest2 compatibility - there must be a better + way...""" self.setUp() if hasattr(self, 'fixture'): for test in self._tests: @@ -153,6 +154,7 @@ class TestSetup(unittest.TestSuite): unittest.TestSuite._wrapped_run(self, *args, **kwargs) self.tearDown() + class TestCaseSetup(unittest.TestCase): def setFixture(self, fixture): self.fixture = fixture @@ -164,7 +166,7 @@ def get_dburi(): dburi = os.environ.get('TRAC_TEST_DB_URI') if dburi: scheme, db_prop = _parse_db_str(dburi) - # Assume the schema 'tractest' for Postgres + # Assume the schema 'tractest' for PostgreSQL if scheme == 'postgres' and \ not db_prop.get('params', {}).get('schema'): if '?' in dburi: @@ -176,9 +178,8 @@ def get_dburi(): def reset_sqlite_db(env, db_prop): - dbname = os.path.basename(db_prop['path']) with env.db_transaction as db: - tables = db("SELECT name FROM sqlite_master WHERE type='table'") + tables = db.get_table_names() for table in tables: db("DELETE FROM %s" % table) return tables @@ -189,24 +190,24 @@ def reset_postgres_db(env, db_prop): dbname = db.schema if dbname: # reset sequences - # information_schema.sequences view is available in PostgreSQL 8.2+ - # however Trac supports PostgreSQL 8.0+, uses + # information_schema.sequences view is available in + # PostgreSQL 8.2+ however Trac supports PostgreSQL 8.0+, uses # pg_get_serial_sequence() - for seq in db(""" - SELECT sequence_name FROM ( - SELECT pg_get_serial_sequence(%s||table_name, - column_name) - AS sequence_name - FROM information_schema.columns - WHERE table_schema=%s) AS tab - WHERE sequence_name IS NOT NULL""", - (dbname + '.', dbname)): + seqs = [seq for seq, in db(""" + SELECT sequence_name + FROM ( + SELECT pg_get_serial_sequence( + quote_ident(table_schema) || '.' || + quote_ident(table_name), column_name) AS sequence_name + FROM information_schema.columns + WHERE table_schema=%s) AS tab + WHERE sequence_name IS NOT NULL""", (dbname,))] + for seq in seqs: db("ALTER SEQUENCE %s RESTART WITH 1" % seq) # clear tables - tables = db("""SELECT table_name FROM information_schema.tables - WHERE table_schema=%s""", (dbname,)) + tables = db.get_table_names() for table in tables: - db("DELETE FROM %s" % table) + db("DELETE FROM %s" % db.quote(table)) # PostgreSQL supports TRUNCATE TABLE as well # (see http://www.postgresql.org/docs/8.1/static/sql-truncate.html) # but on the small tables used here, DELETE is actually much faster @@ -217,12 +218,18 @@ def reset_mysql_db(env, db_prop): dbname = os.path.basename(db_prop['path']) if dbname: with env.db_transaction as db: - tables = db("""SELECT table_name FROM information_schema.tables + tables = db("""SELECT table_name, auto_increment + FROM information_schema.tables WHERE table_schema=%s""", (dbname,)) - for table in tables: - # TRUNCATE TABLE is prefered to DELETE FROM, as we need to reset - # the auto_increment in MySQL. - db("TRUNCATE TABLE %s" % table) + for table, auto_increment in tables: + if auto_increment is None or auto_increment == 1: + # DELETE FROM is preferred to TRUNCATE TABLE, as the + # auto_increment is not used or it is 1. + db("DELETE FROM %s" % table) + else: + # TRUNCATE TABLE is preferred to DELETE FROM, as we + # need to reset the auto_increment in MySQL. + db("TRUNCATE TABLE %s" % table) return tables @@ -245,6 +252,12 @@ class EnvironmentStub(Environment): activate in the stub environment. :param disable: A list of component classes or name globs to deactivate in the stub environment. + :param path: The location of the environment in the file system. + No files or directories are created when specifying + this parameter. + :param destroying: If True, the database will not be reset. This is + useful for cases when the object is being + constructed in order to call `destroy_db`. """ if enable is not None and not isinstance(enable, (list, tuple)): raise TypeError('Keyword argument "enable" must be a list') @@ -323,28 +336,26 @@ class EnvironmentStub(Environment): remove_sqlite_db = False try: with self.db_transaction as db: - db.rollback() # make sure there's no transaction in progress + db.rollback() # make sure there's no transaction in progress # check the database version - database_version = db( - "SELECT value FROM system WHERE name='database_version'") - if database_version: - database_version = int(database_version[0][0]) - if database_version == db_default.db_version: - # same version, simply clear the tables (faster) - m = sys.modules[__name__] - reset_fn = 'reset_%s_db' % scheme - if hasattr(m, reset_fn): - tables = getattr(m, reset_fn)(self, db_prop) - else: - # different version or version unknown, drop the tables - remove_sqlite_db = True - self.destroy_db(scheme, db_prop) - except Exception, e: + database_version = self.get_version() + except Exception: # "Database not found ...", # "OperationalError: no such table: system" or the like pass + else: + if database_version == db_default.db_version: + # same version, simply clear the tables (faster) + m = sys.modules[__name__] + reset_fn = 'reset_%s_db' % scheme + if hasattr(m, reset_fn): + tables = getattr(m, reset_fn)(self, db_prop) + else: + # different version or version unknown, drop the tables + remove_sqlite_db = True + self.destroy_db(scheme, db_prop) - db = None # as we might shutdown the pool FIXME no longer needed! + db = None # as we might shutdown the pool FIXME no longer needed! if scheme == 'sqlite' and remove_sqlite_db: path = db_prop['path'] @@ -362,12 +373,14 @@ class EnvironmentStub(Environment): self.global_databasemanager.shutdown() with self.db_transaction as db: + if scheme == 'sqlite': + # Speed-up tests with SQLite database + db("PRAGMA synchronous = OFF") if default_data: for table, cols, vals in db_default.get_data(db): db.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), - ','.join(['%s' for c in cols])), - vals) + ','.join(['%s'] * len(cols))), vals) else: db("INSERT INTO system (name, value) VALUES (%s, %s)", ('database_version', str(db_default.db_version))) @@ -378,12 +391,9 @@ class EnvironmentStub(Environment): try: with self.db_transaction as db: if scheme == 'postgres' and db.schema: - db('DROP SCHEMA "%s" CASCADE' % db.schema) + db('DROP SCHEMA %s CASCADE' % db.quote(db.schema)) elif scheme == 'mysql': - dbname = os.path.basename(db_prop['path']) - for table in db(""" - SELECT table_name FROM information_schema.tables - WHERE table_schema=%s""", (dbname,)): + for table in db.get_table_names(): db("DROP TABLE IF EXISTS `%s`" % table) except Exception: # "TracError: Database not found...", @@ -391,7 +401,7 @@ class EnvironmentStub(Environment): pass return False - # overriden + # overridden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): @@ -418,6 +428,7 @@ def locate(fn): INCLUDE_FUNCTIONAL_TESTS = True + def suite(): import trac.tests import trac.admin.tests Modified: bloodhound/vendor/trac/current/trac/tests/config.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/config.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/config.py (original) +++ bloodhound/vendor/trac/current/trac/tests/config.py Fri Nov 14 11:06:23 2014 @@ -19,10 +19,10 @@ import tempfile import time import unittest +import trac.tests.compat from trac.config import * from trac.core import Component, Interface, implements from trac.test import Configuration, EnvironmentStub -from trac.tests import compat from trac.util import create_file Modified: bloodhound/vendor/trac/current/trac/tests/core.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/core.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/core.py (original) +++ bloodhound/vendor/trac/current/trac/tests/core.py Fri Nov 14 11:06:23 2014 @@ -14,9 +14,9 @@ # # Author: Christopher Lenz <cml...@gmx.de> +import trac.tests.compat from trac.core import * from trac.core import ComponentManager -from trac.tests import compat import unittest Modified: bloodhound/vendor/trac/current/trac/tests/env.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/env.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/env.py (original) +++ bloodhound/vendor/trac/current/trac/tests/env.py Fri Nov 14 11:06:23 2014 @@ -13,11 +13,12 @@ from __future__ import with_statement +from ConfigParser import RawConfigParser import shutil import tempfile import unittest -from trac.tests import compat +import trac.tests.compat from trac import db_default from trac.core import ComponentManager from trac.env import Environment @@ -85,6 +86,8 @@ class EnvironmentTestCase(unittest.TestC def test_get_version(self): """Testing env.get_version""" self.assertEqual(db_default.db_version, self.env.get_version()) + self.assertEqual(db_default.db_version, self.env.database_version) + self.assertEqual(db_default.db_version, self.env.database_initial_version) def test_get_known_users(self): """Testing env.get_known_users""" @@ -112,6 +115,24 @@ class EnvironmentTestCase(unittest.TestC self.assertEqual(False, EnvironmentStub.required) self.assertEqual(None, self.env.is_component_enabled(EnvironmentStub)) + def test_dumped_values_in_tracini(self): + parser = RawConfigParser() + filename = self.env.config.filename + self.assertEqual([filename], parser.read(filename)) + self.assertEqual('#cc0,#0c0,#0cc,#00c,#c0c,#c00', + parser.get('revisionlog', 'graph_colors')) + self.assertEqual('disabled', parser.get('trac', 'secure_cookies')) + + def test_dumped_values_in_tracini_sample(self): + parser = RawConfigParser() + filename = self.env.config.filename + '.sample' + self.assertEqual([filename], parser.read(filename)) + self.assertEqual('#cc0,#0c0,#0cc,#00c,#c0c,#c00', + parser.get('revisionlog', 'graph_colors')) + self.assertEqual('disabled', parser.get('trac', 'secure_cookies')) + self.assertTrue(parser.has_option('logging', 'log_format')) + self.assertEqual('', parser.get('logging', 'log_format')) + def suite(): suite = unittest.TestSuite() Modified: bloodhound/vendor/trac/current/trac/tests/functional/__init__.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/functional/__init__.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/functional/__init__.py (original) +++ bloodhound/vendor/trac/current/trac/tests/functional/__init__.py Fri Nov 14 11:06:23 2014 @@ -123,22 +123,33 @@ if twill: else: env_class = FunctionalTestEnvironment + tester_class = FunctionalTester + def setUp(self, port=None): """If no port is specified, use a semi-random port and subdirectory 'testenv'; but if a port is specified, use that port and subdirectory 'testenv<portnum>'. """ if port is None: - port = 8000 + os.getpid() % 1000 - dirname = "testenv" + try: + port = int(os.getenv('TRAC_TEST_PORT')) + except (TypeError, ValueError): + pass + + env_path = os.getenv('TRAC_TEST_ENV_PATH') + if not env_path: + env_name = 'testenv%s' % (port or '') + env_path = os.path.join(trac_source_tree, env_name) else: - dirname = "testenv%s" % port - dirname = os.path.join(trac_source_tree, dirname) + env_path += str(port or '') + + if port is None: + port = 8000 + os.getpid() % 1000 baseurl = "http://127.0.0.1:%s" % port - self._testenv = self.env_class(dirname, port, baseurl) + self._testenv = self.env_class(env_path, port, baseurl) self._testenv.start() - self._tester = FunctionalTester(baseurl) + self._tester = self.tester_class(baseurl) self.fixture = (self._testenv, self._tester) def tearDown(self): Modified: bloodhound/vendor/trac/current/trac/tests/functional/better_twill.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/functional/better_twill.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/functional/better_twill.py (original) +++ bloodhound/vendor/trac/current/trac/tests/functional/better_twill.py Fri Nov 14 11:06:23 2014 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2008-2013 Edgewall Software +# Copyright (C) 2008-2014 Edgewall Software # All rights reserved. # # This software is licensed as described in the file COPYING, which @@ -17,8 +17,10 @@ It also handles twill's absense. """ import os -from os.path import abspath, dirname, join import sys +import urllib +import urlparse +from os.path import abspath, dirname, join from pkg_resources import parse_version as pv try: from cStringIO import StringIO @@ -160,7 +162,7 @@ if twill: html_file.write(b.get_html()) html_file.close() - return filename + return urlparse.urljoin('file:', urllib.pathname2url(filename)) # Twill isn't as helpful with errors as I'd like it to be, so we replace # the formvalue function. This would be better done as a patch to Twill. @@ -170,8 +172,8 @@ if twill: except (twill.errors.TwillAssertionError, twill.utils.ClientForm.ItemNotFoundError), e: filename = twill_write_html() - args = e.args + (filename,) - raise twill.errors.TwillAssertionError(*args) + raise twill.errors.TwillAssertionError('%s at %s' % + (unicode(e), filename)) tc.formvalue = better_formvalue tc.fv = better_formvalue @@ -221,8 +223,8 @@ if twill: tcfind(what, flags) except twill.errors.TwillAssertionError, e: filename = twill_write_html() - args = e.args + (filename,) - raise twill.errors.TwillAssertionError(*args) + raise twill.errors.TwillAssertionError('%s at %s' % + (unicode(e), filename)) tc.find = better_find def better_notfind(what, flags='', tcnotfind=tc.notfind): @@ -230,8 +232,8 @@ if twill: tcnotfind(what, flags) except twill.errors.TwillAssertionError, e: filename = twill_write_html() - args = e.args + (filename,) - raise twill.errors.TwillAssertionError(*args) + raise twill.errors.TwillAssertionError('%s at %s' % + (unicode(e), filename)) tc.notfind = better_notfind # Same for tc.url - no hint about what went wrong! @@ -240,8 +242,8 @@ if twill: tcurl(should_be) except twill.errors.TwillAssertionError, e: filename = twill_write_html() - args = e.args + (filename,) - raise twill.errors.TwillAssertionError(*args) + raise twill.errors.TwillAssertionError('%s at %s' % + (unicode(e), filename)) tc.url = better_url else: b = tc = None Modified: bloodhound/vendor/trac/current/trac/tests/functional/compat.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/functional/compat.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/functional/compat.py (original) +++ bloodhound/vendor/trac/current/trac/tests/functional/compat.py Fri Nov 14 11:06:23 2014 @@ -11,5 +11,5 @@ # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. -from trac.util.compat import close_fds from trac.tests.compat import rmtree +from trac.util.compat import close_fds Modified: bloodhound/vendor/trac/current/trac/tests/functional/svntestenv.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/functional/svntestenv.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/functional/svntestenv.py (original) +++ bloodhound/vendor/trac/current/trac/tests/functional/svntestenv.py Fri Nov 14 11:06:23 2014 @@ -24,16 +24,17 @@ class SvnFunctionalTestEnvironment(Funct def work_dir(self): return os.path.join(self.dirname, 'workdir') + def repo_path(self, filename): + return os.path.join(self.dirname, filename) + def repo_path_for_initenv(self): - return os.path.join(self.dirname, 'repo') + return self.repo_path('repo') def create_repo(self): """ Initialize a repo of the type :attr:`self.repotype`. """ - if call(["svnadmin", "create", self.repo_path_for_initenv()], - stdout=logfile, stderr=logfile, close_fds=close_fds): - raise Exception('unable to create subversion repository') + self.svnadmin_create() if call(['svn', 'co', self.repo_url(), self.work_dir()], stdout=logfile, stderr=logfile, close_fds=close_fds): raise Exception('Checkout from %s failed.' % self.repo_url()) @@ -53,6 +54,18 @@ class SvnFunctionalTestEnvironment(Funct else: return 'file://' + repodir + def svnadmin_create(self, filename=None): + """Subversion helper to create a new repository.""" + if filename is None: + path = self.repo_path_for_initenv() + else: + path = self.repo_path(filename) + if call(["svnadmin", "create", path], + stdout=logfile, stderr=logfile, close_fds=close_fds): + raise Exception('unable to create subversion repository: %r' % + path) + return path + def svn_mkdir(self, paths, msg, username='admin'): """Subversion helper to create a new directory within the main repository. Operates directly on the repository url, so a working Modified: bloodhound/vendor/trac/current/trac/tests/functional/testcases.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/functional/testcases.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/functional/testcases.py (original) +++ bloodhound/vendor/trac/current/trac/tests/functional/testcases.py Fri Nov 14 11:06:23 2014 @@ -32,11 +32,61 @@ class TestAttachmentNonexistentParent(Fu 'Parent resource NonexistentPage doesn\'t exist</p>') +class TestErrorPage(FunctionalTwillTestCaseSetup): + """Validate the error page. + Defects reported to trac-hacks should use the Component defined in the + plugin's URL (#11434). + """ + def runTest(self): + env = self._testenv.get_trac_environment() + env.config.set('components', 'RaiseExceptionPlugin.*', 'enabled') + env.config.save() + create_file(os.path.join(env.path, 'plugins', + 'RaiseExceptionPlugin.py'), +"""\ +from trac.core import Component, implements +from trac.web.api import IRequestHandler + +url = None + +class RaiseExceptionPlugin(Component): + implements(IRequestHandler) + + def match_request(self, req): + if req.path_info.startswith('/raise-exception'): + return True + + def process_request(self, req): + print 'maybe?' + if req.args.get('report') == 'tho': + global url + url = 'http://trac-hacks.org/wiki/HelloWorldMacro' + raise Exception + +""") + self._testenv.restart() + + try: + tc.go(self._tester.url + '/raise-exception') + tc.find(internal_error) + tc.find('<form class="newticket" method="get" ' + 'action="http://trac.edgewall.org/newticket">') + + tc.go(self._tester.url + '/raise-exception?report=tho') + tc.find(internal_error) + tc.find('<form class="newticket" method="get" ' + 'action="http://trac-hacks.org/newticket">') + tc.find('<input type="hidden" name="component" ' + 'value="HelloWorldMacro" />') + finally: + env.config.set('components', 'RaiseExceptionPlugin.*', 'disabled') + + class RegressionTestRev6017(FunctionalTwillTestCaseSetup): def runTest(self): """Test for regression of the plugin reload fix in r6017""" # Setup the DeleteTicket plugin - plugin = open(os.path.join(self._testenv.command_cwd, + plugin = open(os.path.join(self._testenv.trac_src, 'sample-plugins', 'workflow', 'DeleteTicket.py')).read() open(os.path.join(self._testenv.tracdir, 'plugins', @@ -198,17 +248,6 @@ See also http://bugs.python.org/issue155 """) -class ErrorPageValidation(FunctionalTwillTestCaseSetup): - def runTest(self): - """Validate the error page""" - url = self._tester.url + '/wiki/WikiStart' - tc.go(url + '?version=bug') - tc.url(url) - tc.find(internal_error) - tc.find('<form class="newticket" method="get" ' - 'action="http://trac.edgewall.org/newticket">') - - class RegressionTestTicket3663(FunctionalTwillTestCaseSetup): def runTest(self): """Regression test for non-UTF-8 PATH_INFO (#3663) @@ -297,6 +336,7 @@ def functionalSuite(suite=None): import trac.tests.functional suite = trac.tests.functional.functionalSuite() suite.addTest(TestAttachmentNonexistentParent()) + suite.addTest(TestErrorPage()) suite.addTest(RegressionTestRev6017()) suite.addTest(RegressionTestTicket3833a()) suite.addTest(RegressionTestTicket3833b()) @@ -304,7 +344,6 @@ def functionalSuite(suite=None): suite.addTest(RegressionTestTicket5572()) suite.addTest(RegressionTestTicket7209()) suite.addTest(RegressionTestTicket9880()) - suite.addTest(ErrorPageValidation()) suite.addTest(RegressionTestTicket3663()) suite.addTest(RegressionTestTicket6318()) suite.addTest(RegressionTestTicket11434()) Modified: bloodhound/vendor/trac/current/trac/tests/functional/testenv.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/functional/testenv.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/functional/testenv.py (original) +++ bloodhound/vendor/trac/current/trac/tests/functional/testenv.py Fri Nov 14 11:06:23 2014 @@ -25,7 +25,7 @@ from subprocess import call, Popen, PIPE from trac.env import open_environment from trac.test import EnvironmentStub, get_dburi from trac.tests.compat import rmtree -from trac.tests.functional import logfile +from trac.tests.functional import logfile, trac_source_tree from trac.tests.functional.better_twill import tc, ConnectError from trac.util import terminate from trac.util.compat import close_fds, wait_for_file_mtime_change @@ -64,6 +64,7 @@ class FunctionalTestEnvironment(object): def __init__(self, dirname, port, url): """Create a :class:`FunctionalTestEnvironment`, see the class itself for parameter information.""" + self.trac_src = trac_source_tree self.url = url self.command_cwd = os.path.normpath(os.path.join(dirname, '..')) self.dirname = os.path.abspath(dirname) @@ -77,8 +78,6 @@ class FunctionalTestEnvironment(object): self.create() locale.setlocale(locale.LC_ALL, '') - trac_src = '.' - @property def dburi(self): dburi = get_dburi() @@ -114,11 +113,13 @@ class FunctionalTestEnvironment(object): def post_create(self, env): """Hook for modifying the environment after creation. For example, to - set configuration like:: + set configuration like: + :: def post_create(self, env): env.config.set('git', 'path', '/usr/bin/git') env.config.save() + """ pass @@ -310,21 +311,24 @@ class FunctionalTestEnvironment(object): def enable_authz_permpolicy(self, authz_content, filename=None): """Enables the Authz permissions policy. The `authz_content` will be written to `filename`, and may be specified in a triple-quoted - string. - ''' - [wiki:WikiStart@*] - * = WIKI_VIEW - [wiki:PrivatePage@*] - john = WIKI_VIEW - * = !WIKI_VIEW - ''' + string.:: + + [wiki:WikiStart@*] + * = WIKI_VIEW + [wiki:PrivatePage@*] + john = WIKI_VIEW + * = !WIKI_VIEW + `authz_content` may also be a dictionary of dictionaries specifying the sections and key/value pairs of each section, however this form should only be used when the order of the entries in the file is not - important, as the order cannot be known. - {'wiki:WikiStart@*': {'*': 'WIKI_VIEW'}, - 'wiki:PrivatePage@*': {'john': 'WIKI_VIEW', '*': '!WIKI_VIEW'}, - } + important, as the order cannot be known.:: + + { + 'wiki:WikiStart@*': {'*': 'WIKI_VIEW'}, + 'wiki:PrivatePage@*': {'john': 'WIKI_VIEW', '*': '!WIKI_VIEW'}, + } + The `filename` parameter is optional, and if omitted a filename will be generated by computing a hash of `authz_content`, prefixed with "authz-". Modified: bloodhound/vendor/trac/current/trac/tests/functional/tester.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/functional/tester.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/functional/tester.py (original) +++ bloodhound/vendor/trac/current/trac/tests/functional/tester.py Fri Nov 14 11:06:23 2014 @@ -20,7 +20,7 @@ import re from trac.tests.functional import internal_error from trac.tests.functional.better_twill import tc, b from trac.tests.contentgen import random_page, random_sentence, random_word, \ - random_unique_camel + random_unique_camel from trac.util.text import to_utf8, unicode_quote try: Modified: bloodhound/vendor/trac/current/trac/tests/perm.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/perm.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/perm.py (original) +++ bloodhound/vendor/trac/current/trac/tests/perm.py Fri Nov 14 11:06:23 2014 @@ -11,18 +11,20 @@ # individuals. For the exact contribution history, see the revision # history and logs, available at http://trac.edgewall.org/log/. +import unittest + from trac import perm from trac.core import * +from trac.resource import Resource from trac.test import EnvironmentStub -import unittest - class DefaultPermissionStoreTestCase(unittest.TestCase): def setUp(self): - self.env = EnvironmentStub(enable=[perm.DefaultPermissionStore, - perm.DefaultPermissionGroupProvider]) + self.env = \ + EnvironmentStub(enable=[perm.DefaultPermissionStore, + perm.DefaultPermissionGroupProvider]) self.store = perm.DefaultPermissionStore(self.env) def tearDown(self): @@ -102,6 +104,48 @@ class TestPermissionRequestor(Component) ('TEST_ADMIN', ['TEST_MODIFY'])] +class PermissionErrorTestCase(unittest.TestCase): + + def setUp(self): + self.env = EnvironmentStub() + + def test_default_message(self): + permission_error = perm.PermissionError() + self.assertEqual(None, permission_error.action) + self.assertEqual(None, permission_error.resource) + self.assertEqual(None, permission_error.env) + self.assertEqual("Insufficient privileges to perform this operation.", + unicode(permission_error)) + self.assertEqual("Forbidden", permission_error.title) + self.assertEqual(unicode(permission_error), permission_error.msg) + + def test_message_specified(self): + message = "The message." + permission_error = perm.PermissionError(msg=message) + self.assertEqual(message, unicode(permission_error)) + + def test_message_from_action(self): + action = 'WIKI_VIEW' + permission_error = perm.PermissionError(action) + self.assertEqual(action, permission_error.action) + self.assertEqual(None, permission_error.resource) + self.assertEqual(None, permission_error.env) + self.assertEqual("WIKI_VIEW privileges are required to perform this " + "operation. You don't have the required " + "permissions.", unicode(permission_error)) + + def test_message_from_action_and_resource(self): + action = 'WIKI_VIEW' + resource = Resource('wiki', 'WikiStart') + permission_error = perm.PermissionError(action, resource, self.env) + self.assertEqual(action, permission_error.action) + self.assertEqual(resource, permission_error.resource) + self.assertEqual(self.env, permission_error.env) + self.assertEqual("WIKI_VIEW privileges are required to perform this " + "operation on WikiStart. You don't have the " + "required permissions.", unicode(permission_error)) + + class PermissionSystemTestCase(unittest.TestCase): def setUp(self): @@ -184,7 +228,8 @@ class PermissionCacheTestCase(unittest.T def test_require(self): self.perm.require('TEST_MODIFY') self.perm.require('TEST_ADMIN') - self.assertRaises(perm.PermissionError, self.perm.require, 'TRAC_ADMIN') + self.assertRaises(perm.PermissionError, + self.perm.require, 'TRAC_ADMIN') def test_assert_permission(self): self.perm.assert_permission('TEST_MODIFY') @@ -231,12 +276,14 @@ class TestPermissionPolicy(Component): class PermissionPolicyTestCase(unittest.TestCase): + def setUp(self): self.env = EnvironmentStub(enable=[perm.DefaultPermissionStore, perm.DefaultPermissionPolicy, TestPermissionPolicy, TestPermissionRequestor]) - self.env.config.set('trac', 'permission_policies', 'TestPermissionPolicy') + self.env.config.set('trac', 'permission_policies', + 'TestPermissionPolicy') self.policy = TestPermissionPolicy(self.env) self.perm = perm.PermissionCache(self.env, 'testuser') @@ -261,7 +308,8 @@ class PermissionPolicyTestCase(unittest. ('testuser', 'TEST_ADMIN'): True}) def test_policy_chaining(self): - self.env.config.set('trac', 'permission_policies', 'TestPermissionPolicy,DefaultPermissionPolicy') + self.env.config.set('trac', 'permission_policies', + 'TestPermissionPolicy,DefaultPermissionPolicy') self.policy.grant('testuser', ['TEST_MODIFY']) system = perm.PermissionSystem(self.env) system.grant_permission('testuser', 'TEST_ADMIN') @@ -279,6 +327,7 @@ class PermissionPolicyTestCase(unittest. def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DefaultPermissionStoreTestCase)) + suite.addTest(unittest.makeSuite(PermissionErrorTestCase)) suite.addTest(unittest.makeSuite(PermissionSystemTestCase)) suite.addTest(unittest.makeSuite(PermissionCacheTestCase)) suite.addTest(unittest.makeSuite(PermissionPolicyTestCase)) Modified: bloodhound/vendor/trac/current/trac/tests/wikisyntax.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/tests/wikisyntax.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/tests/wikisyntax.py (original) +++ bloodhound/vendor/trac/current/trac/tests/wikisyntax.py Fri Nov 14 11:06:23 2014 @@ -25,6 +25,7 @@ from trac.test import MockPerm from trac.web.href import Href from trac.wiki.tests import formatter + SEARCH_TEST_CASES = u""" ============================== search: link resolver search:foo Modified: bloodhound/vendor/trac/current/trac/ticket/admin.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/admin.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/admin.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/admin.py Fri Nov 14 11:06:23 2014 @@ -395,10 +395,11 @@ class MilestoneAdminPanel(TicketAdminPan return self.get_milestone_list() def _do_list(self): - print_table([(m.name, m.due and - format_date(m.due, console_date_format), - m.completed and - format_datetime(m.completed, console_datetime_format)) + print_table([(m.name, + format_date(m.due, console_date_format) + if m.due else None, + format_datetime(m.completed, console_datetime_format) + if m.completed else None) for m in model.Milestone.select(self.env)], [_("Name"), _("Due"), _("Completed")]) @@ -417,15 +418,16 @@ class MilestoneAdminPanel(TicketAdminPan def _do_due(self, name, due): milestone = model.Milestone(self.env, name) - milestone.due = due and parse_date(due, hint='datetime', - locale=get_console_locale(self.env)) + milestone.due = parse_date(due, hint='datetime', + locale=get_console_locale(self.env)) \ + if due else None milestone.update() def _do_completed(self, name, completed): milestone = model.Milestone(self.env, name) - milestone.completed = completed and \ - parse_date(completed, hint='datetime', - locale=get_console_locale(self.env)) + milestone.completed = parse_date(completed, hint='datetime', + locale=get_console_locale(self.env)) \ + if completed else None milestone.update() def _do_remove(self, name): @@ -566,17 +568,18 @@ class VersionAdminPanel(TicketAdminPanel def _do_list(self): print_table([(v.name, - v.time and format_date(v.time, console_date_format)) - for v in model.Version.select(self.env)], + format_date(v.time, console_date_format) + if v.time else None) + for v in model.Version.select(self.env)], [_("Name"), _("Time")]) def _do_add(self, name, time=None): version = model.Version(self.env) version.name = name if time is not None: - version.time = time and \ - parse_date(time, hint='datetime', - locale=get_console_locale(self.env)) + version.time = parse_date(time, hint='datetime', + locale=get_console_locale(self.env)) \ + if time else None version.insert() def _do_rename(self, name, newname): @@ -586,9 +589,9 @@ class VersionAdminPanel(TicketAdminPanel def _do_time(self, name, time): version = model.Version(self.env, name) - version.time = time and \ - parse_date(time, hint='datetime', - locale=get_console_locale(self.env)) + version.time = parse_date(time, hint='datetime', + locale=get_console_locale(self.env)) \ + if time else None version.update() def _do_remove(self, name): Modified: bloodhound/vendor/trac/current/trac/ticket/api.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/api.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/api.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/api.py Fri Nov 14 11:06:23 2014 @@ -122,6 +122,15 @@ class ITicketChangeListener(Interface): def ticket_deleted(ticket): """Called when a ticket is deleted.""" + def ticket_comment_modified(ticket, cdate, author, comment, old_comment): + """Called when a ticket comment is modified.""" + + def ticket_change_deleted(ticket, cdate, changes): + """Called when a ticket change is deleted. + + `changes` is a dictionary of tuple `(oldvalue, newvalue)` + containing the ticket change of the fields that have changed.""" + class ITicketManipulator(Interface): """Miscellaneous manipulation of ticket workflow features.""" Modified: bloodhound/vendor/trac/current/trac/ticket/batch.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/batch.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/batch.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/batch.py Fri Nov 14 11:06:23 2014 @@ -29,6 +29,7 @@ from trac.util.translation import _, tag from trac.web import IRequestHandler from trac.web.chrome import add_warning, add_script_data + class BatchModifyModule(Component): """Ticket batch modification module. Modified: bloodhound/vendor/trac/current/trac/ticket/default_workflow.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/default_workflow.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/default_workflow.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/default_workflow.py Fri Nov 14 11:06:23 2014 @@ -20,6 +20,7 @@ import pkg_resources from ConfigParser import RawConfigParser from StringIO import StringIO +from functools import partial from genshi.builder import tag @@ -98,10 +99,12 @@ class ConfigurableTicketWorkflow(Compone """Ticket action controller which provides actions according to a workflow defined in trac.ini. - The workflow is idefined in the `[ticket-workflow]` section of the + The workflow is defined in the `[ticket-workflow]` section of the [wiki:TracIni#ticket-workflow-section trac.ini] configuration file. """ + implements(IEnvironmentSetupParticipant, ITicketActionController) + ticket_workflow_section = ConfigSection('ticket-workflow', """The workflow for tickets is controlled by plugins. By default, there's only a `ConfigurableTicketWorkflow` component in charge. @@ -129,7 +132,6 @@ class ConfigurableTicketWorkflow(Compone self.log.warning("Ticket workflow action '%s' doesn't define " "any transitions", name) - implements(ITicketActionController, IEnvironmentSetupParticipant) # IEnvironmentSetupParticipant methods @@ -226,19 +228,14 @@ Read TracWorkflow for more information ( this_action = self.actions[action] status = this_action['newstate'] operations = this_action['operations'] - current_owner_or_empty = ticket._old.get('owner', ticket['owner']) - current_owner = current_owner_or_empty or '(none)' - if not (Chrome(self.env).show_email_addresses - or 'EMAIL_VIEW' in req.perm(ticket.resource)): - format_user = obfuscate_email_address - else: - format_user = lambda address: address - current_owner = format_user(current_owner) + current_owner = ticket._old.get('owner', ticket['owner']) + format_author = partial(Chrome(self.env).format_author, req) + formatted_current_owner = format_author(current_owner or _("(none)")) control = [] # default to nothing hints = [] if 'reset_workflow' in operations: - control.append(tag("from invalid state ")) + control.append(_("from invalid state")) hints.append(_("Current state no longer exists")) if 'del_owner' in operations: hints.append(_("The ticket will be disowned")) @@ -256,39 +253,40 @@ Read TracWorkflow for more information ( else: owners = None - if owners == None: + if owners is None: owner = req.args.get(id, req.authname) - control.append(tag_('to %(owner)s', + control.append(tag_("to %(owner)s", owner=tag.input(type='text', id=id, name=id, value=owner))) hints.append(_("The owner will be changed from " "%(current_owner)s to the specified user", - current_owner=current_owner)) + current_owner=formatted_current_owner)) elif len(owners) == 1: owner = tag.input(type='hidden', id=id, name=id, value=owners[0]) - formatted_owner = format_user(owners[0]) - control.append(tag_('to %(owner)s ', - owner=tag(formatted_owner, owner))) + formatted_new_owner = format_author(owners[0]) + control.append(tag_("to %(owner)s", + owner=tag(formatted_new_owner, owner))) if ticket['owner'] != owners[0]: hints.append(_("The owner will be changed from " "%(current_owner)s to %(selected_owner)s", - current_owner=current_owner, - selected_owner=formatted_owner)) + current_owner=formatted_current_owner, + selected_owner=formatted_new_owner)) else: - control.append(tag_('to %(owner)s', owner=tag.select( + control.append(tag_("to %(owner)s", owner=tag.select( [tag.option(x, value=x, selected=(x == selected_owner or None)) for x in owners], id=id, name=id))) hints.append(_("The owner will be changed from " "%(current_owner)s to the selected user", - current_owner=current_owner)) + current_owner=formatted_current_owner)) elif 'set_owner_to_self' in operations and \ ticket._old.get('owner', ticket['owner']) != req.authname: hints.append(_("The owner will be changed from %(current_owner)s " - "to %(authname)s", current_owner=current_owner, - authname=req.authname)) + "to %(authname)s", + current_owner=formatted_current_owner, + authname=format_author(req.authname))) if 'set_resolution' in operations: if 'set_resolution' in this_action: resolutions = [x.strip() for x in @@ -303,7 +301,7 @@ Read TracWorkflow for more information ( if len(resolutions) == 1: resolution = tag.input(type='hidden', id=id, name=id, value=resolutions[0]) - control.append(tag_('as %(resolution)s', + control.append(tag_("as %(resolution)s", resolution=tag(resolutions[0], resolution))) hints.append(_("The resolution will be set to %(name)s", @@ -311,7 +309,7 @@ Read TracWorkflow for more information ( else: selected_option = req.args.get(id, TicketSystem(self.env).default_resolution) - control.append(tag_('as %(resolution)s', + control.append(tag_("as %(resolution)s", resolution=tag.select( [tag.option(x, value=x, selected=(x == selected_option or None)) @@ -321,19 +319,20 @@ Read TracWorkflow for more information ( if 'del_resolution' in operations: hints.append(_("The resolution will be deleted")) if 'leave_status' in operations: - control.append(_('as %(status)s ', + control.append(_("as %(status)s", status= ticket._old.get('status', ticket['status']))) if len(operations) == 1: hints.append(_("The owner will remain %(current_owner)s", - current_owner=current_owner) - if current_owner_or_empty else + current_owner=formatted_current_owner) + if current_owner else _("The ticket will remain with no owner")) else: if status != '*': hints.append(_("Next status will be '%(name)s'", name=status)) - return (this_action['name'], tag(*control), '. '.join(hints) + '.' - if hints else '') + return (this_action['name'], + tag((' ' if i else None, c) for i, c in enumerate(control)), + '. '.join(hints) + '.' if hints else '') def get_ticket_changes(self, req, ticket, action): this_action = self.actions[action] Modified: bloodhound/vendor/trac/current/trac/ticket/model.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/model.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/model.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/model.py Fri Nov 14 11:06:23 2014 @@ -507,6 +507,12 @@ class Ticket(object): self._fetch_ticket(self.id) + changes = dict((field, (oldvalue, newvalue)) + for field, oldvalue, newvalue in fields) + for listener in TicketSystem(self.env).change_listeners: + if hasattr(listener, 'ticket_change_deleted'): + listener.ticket_change_deleted(self, cdate, changes) + def modify_comment(self, cdate, author, comment, when=None): """Modify a ticket comment specified by its date, while keeping a history of edits. @@ -531,8 +537,8 @@ class Ticket(object): # Find the next edit number fields = db("""SELECT field FROM ticket_change WHERE ticket=%%s AND time=%%s AND field %s - """ % db.like(), - (self.id, ts, db.like_escape('_comment') + '%')) + """ % db.prefix_match(), + (self.id, ts, db.prefix_match_value('_comment'))) rev = max(int(field[8:]) for field, in fields) + 1 if fields else 0 db("""INSERT INTO ticket_change (ticket,time,author,field,oldvalue,newvalue) @@ -546,8 +552,8 @@ class Ticket(object): for old_author, in db(""" SELECT author FROM ticket_change WHERE ticket=%%s AND time=%%s AND NOT field %s LIMIT 1 - """ % db.like(), - (self.id, ts, db.like_escape('_') + '%')): + """ % db.prefix_match(), + (self.id, ts, db.prefix_match_value('_'))): db("""INSERT INTO ticket_change (ticket,time,author,field,oldvalue,newvalue) VALUES (%s,%s,%s,'comment','',%s) @@ -563,6 +569,12 @@ class Ticket(object): self.values['changetime'] = when + old_comment = old_comment or '' + for listener in TicketSystem(self.env).change_listeners: + if hasattr(listener, 'ticket_comment_modified'): + listener.ticket_comment_modified(self, cdate, author, comment, + old_comment) + def get_comment_history(self, cnum=None, cdate=None, db=None): """Retrieve the edit history of a comment identified by its number or date. @@ -590,8 +602,8 @@ class Ticket(object): for author0, last_comment in db(""" SELECT author, newvalue FROM ticket_change WHERE ticket=%%s AND time=%%s AND NOT field %s LIMIT 1 - """ % db.like(), - (self.id, ts0, db.like_escape('_') + '%')): + """ % db.prefix_match(), + (self.id, ts0, db.prefix_match_value('_'))): break else: return @@ -600,8 +612,8 @@ class Ticket(object): rows = db("""SELECT field, author, oldvalue, newvalue FROM ticket_change WHERE ticket=%%s AND time=%%s AND field %s - """ % db.like(), - (self.id, ts0, db.like_escape('_comment') + '%')) + """ % db.prefix_match(), + (self.id, ts0, db.prefix_match_value('_comment'))) rows = sorted((int(field[8:]), author, old, new) for field, author, old, new in rows) history = [] @@ -653,8 +665,8 @@ class Ticket(object): for author, in db(""" SELECT author FROM ticket_change WHERE ticket=%%s AND time=%%s AND NOT field %s LIMIT 1 - """ % db.like(), - (self.id, ts, db.like_escape('_') + '%')): + """ % db.prefix_match(), + (self.id, ts, db.prefix_match_value('_'))): break return (ts, author, comment) @@ -1003,11 +1015,10 @@ class Milestone(object): def delete(self, retarget_to=None, author=None, db=None): """Delete the milestone. - :param author: the author of the change - - :since 1.0.2: the `retarget_to` parameter is deprecated and tickets - should moved to another milestone by calling `move_tickets` before - `delete`. + :since 1.0.2: the `retarget_to` and `author` parameters are + deprecated and will be removed in Trac 1.3.1. Tickets + should be moved to another milestone by calling + `move_tickets` before `delete`. :since 1.0: the `db` parameter is no longer needed and will be removed in version 1.1.1 @@ -1015,6 +1026,7 @@ class Milestone(object): with self.env.db_transaction as db: self.env.log.info("Deleting milestone %s", self.name) db("DELETE FROM milestone WHERE name=%s", (self.name,)) + Attachment.delete_all(self.env, 'milestone', self.name) # Don't translate ticket comment (comment:40:ticket:5658) self.move_tickets(retarget_to, author, "Milestone deleted") self._old['name'] = None Modified: bloodhound/vendor/trac/current/trac/ticket/notification.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/notification.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/notification.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/notification.py Fri Nov 14 11:06:23 2014 @@ -26,6 +26,7 @@ from trac.core import * from trac.config import * from trac.notification import NotifyEmail from trac.ticket.api import TicketSystem +from trac.ticket.model import Ticket from trac.util.datefmt import to_utimestamp from trac.util.text import obfuscate_email_address, shorten_line, \ text_width, wrap @@ -75,65 +76,69 @@ class TicketNotificationSystem(Component 0.12.2)''""") -def get_ticket_notification_recipients(env, config, tktid, prev_cc): - """Returns the notifications recipients. +def get_ticket_notification_recipients(env, config, tktid, prev_cc=None, + modtime=None): + """Returns notifications recipients. - :since 1.0.3: the `config` parameter is no longer used. + :since 1.0.2: the `config` parameter is no longer used. + :since 1.0.2: the `prev_cc` parameter is deprecated. """ section = env.config['notification'] - notify_reporter = section.getbool('always_notify_reporter') - notify_owner = section.getbool('always_notify_owner') - notify_updater = section.getbool('always_notify_updater') - - ccrecipients = prev_cc - torecipients = [] - with env.db_query as db: - # Harvest email addresses from the cc, reporter, and owner fields - for row in db("SELECT cc, reporter, owner FROM ticket WHERE id=%s", - (tktid,)): - if row[0]: - ccrecipients += row[0].replace(',', ' ').split() - reporter = row[1] - owner = row[2] - if notify_reporter: - torecipients.append(row[1]) - if notify_owner: - torecipients.append(row[2]) - break - - # Harvest email addresses from the author field of ticket_change(s) - if notify_updater: - for author, ticket in db(""" - SELECT DISTINCT author, ticket FROM ticket_change - WHERE ticket=%s - """, (tktid,)): - torecipients.append(author) - - # Suppress the updater from the recipients - updater = None - for updater, in db(""" - SELECT author FROM ticket_change WHERE ticket=%s - ORDER BY time DESC LIMIT 1 - """, (tktid,)): - break - else: - for updater, in db("SELECT reporter FROM ticket WHERE id=%s", - (tktid,)): - break - - if not notify_updater: - filter_out = True - if notify_reporter and (updater == reporter): - filter_out = False - if notify_owner and (updater == owner): - filter_out = False - if filter_out: - torecipients = [r for r in torecipients - if r and r != updater] - elif updater: - torecipients.append(updater) + always_notify_reporter = section.getbool('always_notify_reporter') + always_notify_owner = section.getbool('always_notify_owner') + always_notify_updater = section.getbool('always_notify_updater') + + cc_recipients = set(prev_cc or []) + to_recipients = set() + tkt = Ticket(env, tktid) + + # CC field is stored as comma-separated string. Parse to list. + to_list = lambda cc: cc.replace(',', ' ').split() + + # Backward compatibility + if not modtime: + modtime = tkt['changetime'] + + # Harvest email addresses from the cc, reporter, and owner fields + if tkt['cc']: + cc_recipients.update(to_list(tkt['cc'])) + if always_notify_reporter: + to_recipients.add(tkt['reporter']) + if always_notify_owner: + to_recipients.add(tkt['owner']) + + # Harvest email addresses from the author field of ticket_change(s) + if always_notify_updater: + for author, ticket in env.db_query(""" + SELECT DISTINCT author, ticket FROM ticket_change + WHERE ticket=%s + """, (tktid, )): + to_recipients.add(author) + + # Harvest previous owner and cc list + author = None + for changelog in tkt.get_changelog(modtime): + author, field, old = changelog[1:4] + if field == 'owner' and always_notify_owner: + to_recipients.add(old) + elif field == 'cc': + cc_recipients.update(to_list(old)) + + # Suppress the updater from the recipients if necessary + updater = author or tkt['reporter'] + if not always_notify_updater: + filter_out = True + if always_notify_reporter and updater == tkt['reporter']: + filter_out = False + if always_notify_owner and updater == tkt['owner']: + filter_out = False + if filter_out: + to_recipients.discard(updater) + elif updater: + to_recipients.add(updater) - return (torecipients, ccrecipients, reporter, owner) + return list(to_recipients), list(cc_recipients), \ + tkt['reporter'], tkt['owner'] class TicketNotifyEmail(NotifyEmail): @@ -148,7 +153,6 @@ class TicketNotifyEmail(NotifyEmail): def __init__(self, env): NotifyEmail.__init__(self, env) - self.prev_cc = [] ambiguous_char_width = env.config.get('notification', 'ambiguous_char_width', 'single') @@ -226,7 +230,6 @@ class TicketNotifyEmail(NotifyEmail): self.ambiwidth) + '\n' if chgcc: changes_body += chgcc - self.prev_cc += self.parse_cc(old) if old else [] else: if field in ['owner', 'reporter']: old = self.obfuscate_email(old) @@ -411,9 +414,9 @@ class TicketNotifyEmail(NotifyEmail): return template.generate(**data).render('text', encoding=None).strip() def get_recipients(self, tktid): - (torecipients, ccrecipients, reporter, owner) = \ - get_ticket_notification_recipients(self.env, self.config, - tktid, self.prev_cc) + torecipients, ccrecipients, reporter, owner = \ + get_ticket_notification_recipients(self.env, self.config, tktid, + modtime=self.modtime) self.reporter = reporter self.owner = owner return (torecipients, ccrecipients) @@ -507,12 +510,11 @@ class BatchTicketNotifyEmail(NotifyEmail return shorten_line(subj) def get_recipients(self, tktids): - alltorecipients = [] - allccrecipients = [] + alltorecipients = set() + allccrecipients = set() for t in tktids: - (torecipients, ccrecipients, reporter, owner) = \ - get_ticket_notification_recipients(self.env, self.config, - t, []) - alltorecipients.extend(torecipients) - allccrecipients.extend(ccrecipients) - return (list(set(alltorecipients)), list(set(allccrecipients))) + torecipients, ccrecipients, reporter, owner = \ + get_ticket_notification_recipients(self.env, self.config, t) + alltorecipients.update(torecipients) + allccrecipients.update(ccrecipients) + return list(alltorecipients), list(allccrecipients) Modified: bloodhound/vendor/trac/current/trac/ticket/query.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/query.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/query.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/query.py Fri Nov 14 11:06:23 2014 @@ -32,14 +32,14 @@ from trac.db import get_column_names from trac.mimeview.api import IContentConverter, Mimeview from trac.resource import Resource from trac.ticket.api import TicketSystem -from trac.ticket.model import Milestone, group_milestones, Ticket +from trac.ticket.model import Milestone, group_milestones from trac.util import Ranges, as_bool from trac.util.compat import any from trac.util.datefmt import format_datetime, from_utimestamp, parse_date, \ to_timestamp, to_utimestamp, utc, user_time from trac.util.presentation import Paginator from trac.util.text import empty, shorten_line, quote_query_string -from trac.util.translation import _, tag_, cleandoc_ +from trac.util.translation import _, tag_, cleandoc_, ngettext from trac.web import arg_list_to_args, parse_arg_list, IRequestHandler from trac.web.href import Href from trac.web.chrome import (INavigationContributor, Chrome, @@ -1165,7 +1165,7 @@ class QueryModule(Component): values = [] for col in cols: value = result[col] - if col in ('cc', 'reporter'): + if col in ('cc', 'owner', 'reporter'): value = Chrome(self.env).format_emails( context.child(ticket), value) elif col in query.time_fields: @@ -1267,6 +1267,9 @@ class TicketQueryMacro(WikiMacroBase): The `rows` parameter can be used to specify which field(s) should be viewed as a row, e.g. `rows=description|summary` + The `col` parameter can be used to specify which fields should + be viewed as columns. For '''table''' format only. + For compatibility with Trac 0.10, if there's a last positional parameter given to the macro, it will be used to specify the `format`. Also, using "&" as a field separator still works (except for `order`) @@ -1327,8 +1330,10 @@ class TicketQueryMacro(WikiMacroBase): if format == 'count': cnt = query.count(req) - return tag.span(cnt, title='%d tickets for which %s' % - (cnt, query_string), class_='query_count') + title = ngettext("%(num)d ticket for which %(query)s", + "%(num)d tickets for which %(query)s", + cnt, query=query_string) + return tag.span(cnt, title=title, class_='query_count') tickets = query.execute(req) @@ -1350,14 +1355,21 @@ class TicketQueryMacro(WikiMacroBase): add_stylesheet(req, 'common/css/roadmap.css') def query_href(extra_args, group_value = None): - q = Query.from_string(self.env, query_string) + q = query_string + ''.join('&%s=%s' % (kw, v) + for kw in extra_args + if kw not in ['group', 'status'] + for v in extra_args[kw]) + q = Query.from_string(self.env, q) + args = {} if q.group: - extra_args[q.group] = group_value - q.group = None + args[q.group] = group_value + q.group = extra_args.get('group') + if 'status' in extra_args: + args['status'] = extra_args['status'] for constraint in q.constraints: - constraint.update(extra_args) + constraint.update(args) if not q.constraints: - q.constraints.append(extra_args) + q.constraints.append(args) return q.get_href(formatter.context) chrome = Chrome(self.env) tickets = apply_ticket_permissions(self.env, req, tickets) Modified: bloodhound/vendor/trac/current/trac/ticket/report.py URL: http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/report.py?rev=1639602&r1=1639601&r2=1639602&view=diff ============================================================================== --- bloodhound/vendor/trac/current/trac/ticket/report.py (original) +++ bloodhound/vendor/trac/current/trac/ticket/report.py Fri Nov 14 11:06:23 2014 @@ -186,17 +186,19 @@ class ReportModule(Component): if content_type: # i.e. alternate format return template, data, content_type + from trac.ticket.query import QueryModule + show_query_link = 'TICKET_VIEW' in req.perm and \ + self.env.is_component_enabled(QueryModule) + if id != -1 or action == 'new': add_ctxtnav(req, _('Available Reports'), href=req.href.report()) add_link(req, 'up', req.href.report(), _('Available Reports')) - else: + elif show_query_link: add_ctxtnav(req, _('Available Reports')) # Kludge: only show link to custom query if the query module # is actually enabled - from trac.ticket.query import QueryModule - if 'TICKET_VIEW' in req.perm and \ - self.env.is_component_enabled(QueryModule): + if show_query_link: add_ctxtnav(req, _('Custom Query'), href=req.href.query()) data['query_href'] = req.href.query() data['saved_query_href'] = req.session.get('query_href') @@ -680,6 +682,9 @@ class ReportModule(Component): try: cursor.execute(count_sql, args) except Exception, e: + self.log.warn('Exception caught while executing report: %r, ' + 'args %r%s', count_sql, args, + exception_to_unicode(e, traceback=True)) return e, count_sql num_items = cursor.fetchone()[0] @@ -689,6 +694,9 @@ class ReportModule(Component): try: cursor.execute(colnames_sql, args) except Exception, e: + self.log.warn('Exception caught while executing report: %r, ' + 'args %r%s', colnames_sql, args, + exception_to_unicode(e, traceback=True)) return e, colnames_sql cols = get_column_names(cursor) @@ -740,6 +748,9 @@ class ReportModule(Component): try: cursor.execute(sql, args) except Exception, e: + self.log.warn('Exception caught while executing report: %r, args ' + '%r%s', + sql, args, exception_to_unicode(e, traceback=True)) if order_by or limit_offset: add_notice(req, _("Hint: if the report failed due to automatic" " modification of the ORDER BY clause or the" @@ -755,14 +766,18 @@ class ReportModule(Component): return cols, rows, num_items, missing_args, limit_offset def get_report(self, id): - for title, description, sql in self.env.db_query(""" - SELECT title, description, query from report WHERE id=%s - """, (id,)): - break + try: + number = int(id) + except (ValueError, TypeError): + pass else: - raise ResourceNotFound(_("Report {%(num)s} does not exist.", - num=id), _("Invalid Report Number")) - return title, description, sql + for title, description, sql in self.env.db_query(""" + SELECT title, description, query from report WHERE id=%s + """, (number,)): + return title, description, sql + + raise ResourceNotFound(_("Report {%(num)s} does not exist.", num=id), + _("Invalid Report Number")) def get_var_args(self, req): # reuse somehow for #9574 (wiki vars)