You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by ah...@apache.org on 2013/07/28 19:22:24 UTC

svn commit: r1507818 [2/3] - in /bloodhound/branches/bep_0007_embeddable_objects: ./ bloodhound_dashboard/ bloodhound_dashboard/bhdashboard/ bloodhound_dashboard/bhdashboard/widgets/ bloodhound_dashboard/bhdashboard/widgets/templates/ bloodhound_multip...

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/templates/manage.html
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/templates/manage.html?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/templates/manage.html (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/templates/manage.html Sun Jul 28 17:22:23 2013
@@ -1,127 +1,127 @@
-<!--!
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing,
-  software distributed under the License is distributed on an
-  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-  KIND, either express or implied.  See the License for the
-  specific language governing permissions and limitations
-  under the License.
--->
-
-<!DOCTYPE html
-    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
-      xmlns:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:i18n="http://genshi.edgewall.org/i18n"
-      xmlns:bh="http://issues.apache.org/bloodhound/wiki/Ui/Dashboard">
-  <xi:include href="layout.html" />
-  <xi:include href="widget_macros.html" />
-
-  <head>
-    <title py:choose="">Ticket relations for #${ticket.id}</title>
-  </head>
-
-  <body>
-    <h1>Manage relations for ticket <a href="${href.ticket(ticket.id)}">#$ticket.id</a></h1>
-
-    <div class="row">
-      <div class="span8">
-        <py:if test='error'>
-          <div class="alert alert-error">
-            <span class="label label-important">Oops !</span>
-            Could not create relation.
-            $error
-          </div>
-        </py:if>
-
-        <form id="addrelation" class="well form-horizontal" method="post" action="">
-          <fieldset>
-            <legend>Add relation:</legend>
-          </fieldset>
-
-          <div class="control-group">
-            <label class="control-label" for="dest_tid">Related ticket:</label>
-            <div class="controls">
-              <input type="text" id="dest_tid" class="span4" name="dest_tid" value="$relation.destination" />
-            </div>
-          </div>
-
-          <div class="control-group">
-            <label class="control-label" for="reltype">Relation type:</label>
-            <div class="controls">
-              <select class="span4" id="reltype" name="reltype">
-                <option py:for="reltype,label in reltypes.iteritems()" value="$reltype" selected="${True if reltype == relation.type else None}">$label</option>
-              </select>
-            </div>
-          </div>
-
-          <div class="control-group">
-            <label class="control-label" for="comment">Comment:</label>
-            <div class="controls">
-              <textarea name="comment" rows="3" class="span4">${relation.comment}</textarea>
-            </div>
-          </div>
-
-          <div class="control-group">
-            <div class="controls">
-              <input type="submit" class="btn" name="add" value="${_('Add')}" />
-            </div>
-          </div>
-        </form>
-      </div>
-    </div>
-
-    <div class="row">
-      <div class="span8">
-        <py:choose>
-          <form py:when="relations" id="rmrelations" class="form-horizontal" method="post" action="">
-            <table class="table table-condensed table-bordered">
-              <thead>
-                <tr>
-                  <th class="sel"><i class="icon-check"></i></th>
-                  <th>Type</th><th>Product</th><th>Ticket</th><th>Comment</th><th>Author</th><th class="hidden-phone">Changed</th>
-                </tr>
-              </thead>
-              <tbody py:for="relgroup,items in relations.iteritems()">
-                <tr py:for="item in items">
-                  <td class="sel"><input type="checkbox" name="sel" value="${item.relation_id}" /></td>
-                  <td>${relgroup if items.index(item) == 0 else None}</td>
-                  <td>
-                    <a href="${href.products(item['destticket'].env.product.prefix)}">
-                      <span class="hidden-phone">${item['destticket'].env.product.name} (${item['destticket'].env.product.prefix})</span>
-                      <span class="visible-phone">${item['destticket'].env.product.prefix}</span>
-                    </a>
-                  </td>
-                  <td><a href="${item['desthref']}">#${item['destticket'].id}</a> - ${item['destticket'].summary}</td>
-                  <td>$item.comment</td>
-                  <td>$item.author</td>
-                  <td class="hidden-phone">${pretty_dateinfo(item.when)}</td>
-                </tr>
-              </tbody>
-            </table>
-
-            <div class="control-group">
-              <input type="submit" class="btn" name="remove" value="${_('Remove selected relations')}" />
-            </div>
-          </form>
-
-          <div py:otherwise="" class="alert alert-info">
-            No defined relations for this ticket.
-          </div>
-        </py:choose>
-      </div>
-    </div>
-  </body>
-</html>
-
+<!--!
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:i18n="http://genshi.edgewall.org/i18n"
+      xmlns:bh="http://issues.apache.org/bloodhound/wiki/Ui/Dashboard">
+  <xi:include href="layout.html" />
+  <xi:include href="widget_macros.html" />
+
+  <head>
+    <title py:choose="">Ticket relations for #${ticket.id}</title>
+  </head>
+
+  <body>
+    <h1>Manage relations for ticket <a href="${href.ticket(ticket.id)}">#$ticket.id</a></h1>
+
+    <div class="row">
+      <div class="span8">
+        <py:if test='error'>
+          <div class="alert alert-error">
+            <span class="label label-important">Oops !</span>
+            Could not create relation.
+            $error
+          </div>
+        </py:if>
+
+        <form id="addrelation" class="well form-horizontal" method="post" action="">
+          <fieldset>
+            <legend>Add relation:</legend>
+          </fieldset>
+
+          <div class="control-group">
+            <label class="control-label" for="dest_tid">Related ticket:</label>
+            <div class="controls">
+              <input type="text" id="dest_tid" class="span4" name="dest_tid" value="$relation.destination" />
+            </div>
+          </div>
+
+          <div class="control-group">
+            <label class="control-label" for="reltype">Relation type:</label>
+            <div class="controls">
+              <select class="span4" id="reltype" name="reltype">
+                <option py:for="reltype,label in reltypes.iteritems()" value="$reltype" selected="${True if reltype == relation.type else None}">$label</option>
+              </select>
+            </div>
+          </div>
+
+          <div class="control-group">
+            <label class="control-label" for="comment">Comment:</label>
+            <div class="controls">
+              <textarea name="comment" rows="3" class="span4">${relation.comment}</textarea>
+            </div>
+          </div>
+
+          <div class="control-group">
+            <div class="controls">
+              <input type="submit" class="btn" name="add" value="${_('Add')}" />
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="span8">
+        <py:choose>
+          <form py:when="relations" id="rmrelations" class="form-horizontal" method="post" action="">
+            <table class="table table-condensed table-bordered">
+              <thead>
+                <tr>
+                  <th class="sel"><i class="icon-check"></i></th>
+                  <th>Type</th><th>Product</th><th>Ticket</th><th>Comment</th><th>Author</th><th class="hidden-phone">Changed</th>
+                </tr>
+              </thead>
+              <tbody py:for="relgroup,items in relations.iteritems()">
+                <tr py:for="item in items">
+                  <td class="sel"><input type="checkbox" name="sel" value="${item.relation_id}" /></td>
+                  <td>${relgroup if items.index(item) == 0 else None}</td>
+                  <td>
+                    <a href="${href.products(item['destticket'].env.product.prefix)}">
+                      <span class="hidden-phone">${item['destticket'].env.product.name} (${item['destticket'].env.product.prefix})</span>
+                      <span class="visible-phone">${item['destticket'].env.product.prefix}</span>
+                    </a>
+                  </td>
+                  <td><a href="${item['desthref']}">#${item['destticket'].id}</a> - ${item['destticket'].summary}</td>
+                  <td>$item.comment</td>
+                  <td>$item.author</td>
+                  <td class="hidden-phone">${pretty_dateinfo(item.when)}</td>
+                </tr>
+              </tbody>
+            </table>
+
+            <div class="control-group">
+              <input type="submit" class="btn" name="remove" value="${_('Remove selected relations')}" />
+            </div>
+          </form>
+
+          <div py:otherwise="" class="alert alert-info">
+            No defined relations for this ticket.
+          </div>
+        </py:choose>
+      </div>
+    </div>
+  </body>
+</html>
+

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/api.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/api.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/api.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/api.py Sun Jul 28 17:22:23 2013
@@ -18,99 +18,19 @@
 #  specific language governing permissions and limitations
 #  under the License.
 from datetime import datetime
-from _sqlite3 import OperationalError, IntegrityError
+from _sqlite3 import IntegrityError
 import unittest
-from bhrelations.api import (EnvironmentSetup, RelationsSystem,
-                             TicketRelationsSpecifics)
+from bhrelations.api import TicketRelationsSpecifics
 from bhrelations.tests.mocks import TestRelationChangingListener
 from bhrelations.validation import ValidationError
+from bhrelations.tests.base import BaseRelationsTestCase
 from multiproduct.env import ProductEnvironment
-from tests.env import MultiproductTestCase
 from trac.ticket.model import Ticket
-from trac.test import EnvironmentStub, Mock, MockPerm
 from trac.core import TracError
 from trac.util.datefmt import utc
 
-try:
-    from babel import Locale
 
-    locale_en = Locale.parse('en_US')
-except ImportError:
-    locale_en = None
-
-
-class BaseApiApiTestCase(MultiproductTestCase):
-    def setUp(self, enabled=()):
-        env = EnvironmentStub(
-            default_data=True,
-            enable=(['trac.*', 'multiproduct.*', 'bhrelations.*'] +
-                    list(enabled))
-        )
-        env.config.set('bhrelations', 'global_validators',
-                       'NoSelfReferenceValidator,ExclusiveValidator,'
-                       'BlockerValidator')
-        config_name = RelationsSystem.RELATIONS_CONFIG_NAME
-        env.config.set(config_name, 'dependency', 'dependson,dependent')
-        env.config.set(config_name, 'dependency.validators',
-                       'NoCycles,SingleProduct')
-        env.config.set(config_name, 'dependson.blocks', 'true')
-        env.config.set(config_name, 'parent_children', 'parent,children')
-        env.config.set(config_name, 'parent_children.validators',
-                       'OneToMany,SingleProduct,NoCycles')
-        env.config.set(config_name, 'children.label', 'Overridden')
-        env.config.set(config_name, 'parent.copy_fields',
-                       'summary, foo')
-        env.config.set(config_name, 'parent.exclusive', 'true')
-        env.config.set(config_name, 'multiproduct_relation', 'mprel,mpbackrel')
-        env.config.set(config_name, 'oneway', 'refersto')
-        env.config.set(config_name, 'duplicate', 'duplicateof,duplicatedby')
-        env.config.set(config_name, 'duplicate.validators', 'ReferencesOlder')
-        env.config.set(config_name, 'duplicateof.label', 'Duplicate of')
-        env.config.set(config_name, 'duplicatedby.label', 'Duplicated by')
-        env.config.set(config_name, 'blocker', 'blockedby,blocks')
-        env.config.set(config_name, 'blockedby.blocks', 'true')
-
-        self.global_env = env
-        self._upgrade_mp(self.global_env)
-        self._setup_test_log(self.global_env)
-        self._load_product_from_data(self.global_env, self.default_product)
-        self.env = ProductEnvironment(self.global_env, self.default_product)
-
-        self.req = Mock(href=self.env.href, authname='anonymous', tz=utc,
-                        args=dict(action='dummy'),
-                        locale=locale_en, lc_time=locale_en)
-        self.req.perm = MockPerm()
-        self.relations_system = RelationsSystem(self.env)
-        self._upgrade_env()
-
-    def tearDown(self):
-        self.global_env.reset_db()
-
-    def _upgrade_env(self):
-        environment_setup = EnvironmentSetup(self.env)
-        try:
-            environment_setup.upgrade_environment(self.env.db_transaction)
-        except OperationalError:
-            # table remains but database version is deleted
-            pass
-
-    @classmethod
-    def _insert_ticket(cls, env, summary, **kw):
-        """Helper for inserting a ticket into the database"""
-        ticket = Ticket(env)
-        ticket["Summary"] = summary
-        for k, v in kw.items():
-            ticket[k] = v
-        return ticket.insert()
-
-    def _insert_and_load_ticket(self, summary, **kw):
-        return Ticket(self.env, self._insert_ticket(self.env, summary, **kw))
-
-    def _insert_and_load_ticket_with_env(self, env, summary, **kw):
-        return Ticket(env, self._insert_ticket(env, summary, **kw))
-
-
-class ApiTestCase(BaseApiApiTestCase):
+class ApiTestCase(BaseRelationsTestCase):
     def test_can_add_two_ways_relations(self):
         #arrange
         ticket = self._insert_and_load_ticket("A1")
@@ -475,7 +395,7 @@ class ApiTestCase(BaseApiApiTestCase):
         )
 
     def test_cannot_create_other_relations_between_descendants(self):
-        t1, t2, t3, t4, t5 = map(self._insert_and_load_ticket, range(5))
+        t1, t2, t3, t4, t5 = map(self._insert_and_load_ticket, "12345")
         self.relations_system.add(t4, t2, "parent")  #    t1 -> t2
         self.relations_system.add(t3, t2, "parent")  #         /  \
         self.relations_system.add(t2, t1, "parent")  #       t3    t4
@@ -503,7 +423,7 @@ class ApiTestCase(BaseApiApiTestCase):
             self.fail("Could not add valid relation.")
 
     def test_cannot_add_parent_if_this_would_cause_invalid_relations(self):
-        t1, t2, t3, t4, t5 = map(self._insert_and_load_ticket, range(5))
+        t1, t2, t3, t4, t5 = map(self._insert_and_load_ticket, "12345")
         self.relations_system.add(t4, t2, "parent")  #    t1 -> t2
         self.relations_system.add(t3, t2, "parent")  #         /  \
         self.relations_system.add(t2, t1, "parent")  #       t3    t4    t5
@@ -553,7 +473,7 @@ class ApiTestCase(BaseApiApiTestCase):
         self.relations_system.add(t2, t1, "duplicateof")
 
     def test_detects_blocker_cycles(self):
-        t1, t2, t3, t4, t5 = map(self._insert_and_load_ticket, range(5))
+        t1, t2, t3, t4, t5 = map(self._insert_and_load_ticket, "12345")
         self.relations_system.add(t1, t2, "blocks")
         self.relations_system.add(t3, t2, "dependson")
         self.relations_system.add(t4, t3, "blockedby")
@@ -577,7 +497,7 @@ class ApiTestCase(BaseApiApiTestCase):
         self.relations_system.add(t2, t1, "refersto")
 
 
-class RelationChangingListenerTestCase(BaseApiApiTestCase):
+class RelationChangingListenerTestCase(BaseRelationsTestCase):
     def test_can_sent_adding_event(self):
         #arrange
         ticket1 = self._insert_and_load_ticket("A1")
@@ -608,7 +528,7 @@ class RelationChangingListenerTestCase(B
         self.assertEqual("dependent", relation.type)
 
 
-class TicketChangeRecordUpdaterTestCase(BaseApiApiTestCase):
+class TicketChangeRecordUpdaterTestCase(BaseRelationsTestCase):
     def test_can_update_ticket_history_on_relation_add_on(self):
         #arrange
         ticket1 = self._insert_and_load_ticket("A1")

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/notification.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/notification.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/notification.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/notification.py Sun Jul 28 17:22:23 2013
@@ -21,11 +21,11 @@ import unittest
 from trac.tests.notification import SMTPServerStore, SMTPThreadedServer
 from trac.ticket.tests.notification import (
     SMTP_TEST_PORT, smtp_address, parse_smtp_message)
+from bhrelations.tests.base import BaseRelationsTestCase
 from bhrelations.notification import RelationNotifyEmail
-from bhrelations.tests.api import BaseApiApiTestCase
 
 
-class NotificationTestCase(BaseApiApiTestCase):
+class NotificationTestCase(BaseRelationsTestCase):
     @classmethod
     def setUpClass(cls):
         cls.smtpd = CustomSMTPThreadedServer(SMTP_TEST_PORT)

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/search.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/search.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/search.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/search.py Sun Jul 28 17:22:23 2013
@@ -21,25 +21,25 @@ import shutil
 import tempfile
 import unittest
 
-from bhrelations.tests.api import BaseApiApiTestCase
 from bhsearch.api import BloodhoundSearchApi
 
 # TODO: Figure how to get trac to load components from these modules
 import bhsearch.query_parser, bhsearch.search_resources.ticket_search, \
     bhsearch.whoosh_backend
 import bhrelations.search
+from bhrelations.tests.base import BaseRelationsTestCase
 
 
-class SearchIntegrationTestCase(BaseApiApiTestCase):
+class SearchIntegrationTestCase(BaseRelationsTestCase):
     def setUp(self):
-        BaseApiApiTestCase.setUp(self, enabled=['bhsearch.*'])
+        BaseRelationsTestCase.setUp(self, enabled=['bhsearch.*'])
         self.global_env.path = tempfile.mkdtemp('bhrelations-tempenv')
         self.search_api = BloodhoundSearchApi(self.env)
         self.search_api.upgrade_environment(self.env.db_transaction)
 
     def tearDown(self):
         shutil.rmtree(self.env.path)
-        BaseApiApiTestCase.tearDown(self)
+        BaseRelationsTestCase.tearDown(self)
 
     def test_relations_are_indexed_on_creation(self):
         t1 = self._insert_and_load_ticket("Foo")

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/validation.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/validation.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/validation.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/validation.py Sun Jul 28 17:22:23 2013
@@ -20,10 +20,10 @@
 import unittest
 
 from bhrelations.validation import Validator
-from bhrelations.tests.api import BaseApiApiTestCase
+from bhrelations.tests.base import BaseRelationsTestCase
 
 
-class GraphFunctionsTestCase(BaseApiApiTestCase):
+class GraphFunctionsTestCase(BaseRelationsTestCase):
     edges = [
         ('A', 'B', 'p'),  #      A    H
         ('A', 'C', 'p'),  #     /  \ /
@@ -35,7 +35,7 @@ class GraphFunctionsTestCase(BaseApiApiT
     ]
 
     def setUp(self):
-        BaseApiApiTestCase.setUp(self)
+        BaseRelationsTestCase.setUp(self)
         # bhrelations point from destination to source
         for destination, source, type in self.edges:
             self.env.db_direct_transaction(

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/web_ui.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/web_ui.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/web_ui.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/tests/web_ui.py Sun Jul 28 17:22:23 2013
@@ -18,27 +18,31 @@
 #  specific language governing permissions and limitations
 #  under the License.
 import unittest
-
+from bhrelations.api import ResourceIdSerializer
 from bhrelations.web_ui import RelationManagementModule
-from bhrelations.tests.api import BaseApiApiTestCase
+from bhrelations.tests.base import BaseRelationsTestCase
+
+from multiproduct.ticket.web_ui import TicketModule
+from trac.ticket import Ticket
+from trac.util.datefmt import to_utimestamp
+from trac.web import RequestDone
 
 
-class RelationManagementModuleTestCase(BaseApiApiTestCase):
+class RelationManagementModuleTestCase(BaseRelationsTestCase):
     def setUp(self):
-        BaseApiApiTestCase.setUp(self)
+        BaseRelationsTestCase.setUp(self)
         ticket_id = self._insert_ticket(self.env, "Foo")
-        args=dict(action='add', id=ticket_id, dest_tid='', reltype='', comment='')
-        self.req.method = 'GET',
+        self.req.method = 'POST'
         self.req.args['id'] = ticket_id
 
     def test_can_process_empty_request(self):
+        self.req.method = 'GET'
         data = self.process_request()
 
         self.assertSequenceEqual(data['relations'], [])
         self.assertEqual(len(data['reltypes']), 11)
 
     def test_handles_missing_ticket_id(self):
-        self.req.method = "POST"
         self.req.args['add'] = 'add'
 
         data = self.process_request()
@@ -46,8 +50,7 @@ class RelationManagementModuleTestCase(B
         self.assertIn("Invalid ticket", data["error"])
 
     def test_handles_invalid_ticket_id(self):
-        self.req.method = "POST"
-        self.req.args['add'] = 'add'
+        self.req.args['add'] = True
         self.req.args['dest_tid'] = 'no such ticket'
 
         data = self.process_request()
@@ -56,8 +59,7 @@ class RelationManagementModuleTestCase(B
 
     def test_handles_missing_relation_type(self):
         t2 = self._insert_ticket(self.env, "Bar")
-        self.req.method = "POST"
-        self.req.args['add'] = 'add'
+        self.req.args['add'] = True
         self.req.args['dest_tid'] = str(t2)
 
         data = self.process_request()
@@ -66,8 +68,7 @@ class RelationManagementModuleTestCase(B
 
     def test_handles_invalid_relation_type(self):
         t2 = self._insert_ticket(self.env, "Bar")
-        self.req.method = "POST"
-        self.req.args['add'] = 'add'
+        self.req.args['add'] = True
         self.req.args['dest_tid'] = str(t2)
         self.req.args['reltype'] = 'no such relation'
 
@@ -77,8 +78,7 @@ class RelationManagementModuleTestCase(B
 
     def test_shows_relation_that_was_just_added(self):
         t2 = self._insert_ticket(self.env, "Bar")
-        self.req.method = "POST"
-        self.req.args['add'] = 'add'
+        self.req.args['add'] = True
         self.req.args['dest_tid'] = str(t2)
         self.req.args['reltype'] = 'dependson'
 
@@ -92,6 +92,117 @@ class RelationManagementModuleTestCase(B
         return data
 
 
+class ResolveTicketIntegrationTestCase(BaseRelationsTestCase):
+    def setUp(self):
+        BaseRelationsTestCase.setUp(self)
+
+        self.mock_request()
+        self.configure()
+
+        self.req.redirect = self.redirect
+        self.redirect_url = None
+        self.redirect_permanent = None
+
+    def test_creates_duplicate_relation_from_duplicate_id(self):
+        t1 = self._insert_and_load_ticket("Foo")
+        t2 = self._insert_and_load_ticket("Bar")
+
+        self.assertRaises(RequestDone,
+                          self.resolve_as_duplicate,
+                          t2, self.get_id(t1))
+        relations = self.relations_system.get_relations(t2)
+        self.assertEqual(len(relations), 1)
+        relation = relations[0]
+        self.assertEqual(relation['destination_id'], self.get_id(t1))
+        self.assertEqual(relation['type'], 'duplicateof')
+
+    def test_prefills_duplicate_id_if_relation_exists(self):
+        t1 = self._insert_and_load_ticket("Foo")
+        t2 = self._insert_and_load_ticket("Bar")
+        self.relations_system.add(t2, t1, 'duplicateof')
+        self.req.path_info = '/ticket/%d' % t2.id
+
+        data = self.process_request()
+
+        self.assertIn('ticket_duplicate_of', data)
+        t1id = ResourceIdSerializer.get_resource_id_from_instance(self.env, t1)
+        self.assertEqual(data['ticket_duplicate_of'], t1id)
+
+    def test_can_set_duplicate_resolution_even_if_relation_exists(self):
+        t1 = self._insert_and_load_ticket("Foo")
+        t2 = self._insert_and_load_ticket("Bar")
+        self.relations_system.add(t2, t1, 'duplicateof')
+
+        self.assertRaises(RequestDone,
+                          self.resolve_as_duplicate,
+                          t2, self.get_id(t1))
+        t2 = Ticket(self.env, t2.id)
+        self.assertEqual(t2['status'], 'closed')
+        self.assertEqual(t2['resolution'], 'duplicate')
+
+    def test_post_process_request_does_not_break_ticket(self):
+        t1 = self._insert_and_load_ticket("Foo")
+        self.req.path_info = '/ticket/%d' % t1.id
+        self.process_request()
+
+    def test_post_process_request_does_not_break_newticket(self):
+        self.req.path_info = '/newticket'
+        self.process_request()
+
+    def test_post_process_request_can_handle_none_data(self):
+        self.req.path_info = '/source'
+        RelationManagementModule(self.env).post_process_request(
+            self.req, '', None, '')
+
+    def resolve_as_duplicate(self, ticket, duplicate_id):
+        self.req.method = 'POST'
+        self.req.path_info = '/ticket/%d' % ticket.id
+        self.req.args['id'] = ticket.id
+        self.req.args['action'] = 'resolve'
+        self.req.args['action_resolve_resolve_resolution'] = 'duplicate'
+        self.req.args['duplicate_id'] = duplicate_id
+        self.req.args['view_time'] = str(to_utimestamp(ticket['changetime']))
+        self.req.args['submit'] = True
+
+        return self.process_request()
+
+    def process_request(self):
+        ticket_module = TicketModule(self.env)
+
+        ticket_module.match_request(self.req)
+        template, data, content_type = ticket_module.process_request(self.req)
+        template, data, content_type = \
+            RelationManagementModule(self.env).post_process_request(
+                self.req, template, data, content_type)
+        return data
+
+    def mock_request(self):
+        self.req.method = 'GET'
+        self.req.get_header = lambda x: None
+        self.req.authname = 'x'
+        self.req.session = {}
+        self.req.chrome = {'warnings': []}
+        self.req.form_token = ''
+
+    def configure(self):
+        config = self.env.config
+        config['ticket-workflow'].set('resolve', 'new -> closed')
+        config['ticket-workflow'].set('resolve.operations', 'set_resolution')
+        config['ticket-workflow'].set('resolve.permissions', 'TICKET_MODIFY')
+        with self.env.db_transaction as db:
+            db("INSERT INTO enum VALUES "
+               "('resolution', 'duplicate', 'duplicate')")
+
+    def redirect(self, url, permanent=False):
+        self.redirect_url = url
+        self.redirect_permanent = permanent
+        raise RequestDone
+
+    def get_id(self, ticket):
+        return ResourceIdSerializer.get_resource_id_from_instance(self.env,
+                                                                  ticket)
+
+
 def suite():
     test_suite = unittest.TestSuite()
     test_suite.addTest(unittest.makeSuite(RelationManagementModuleTestCase, 'test'))

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/web_ui.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/web_ui.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/web_ui.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/web_ui.py Sun Jul 28 17:22:23 2013
@@ -1,183 +1,172 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8 -*-
-
-#  Licensed to the Apache Software Foundation (ASF) under one
-#  or more contributor license agreements.  See the NOTICE file
-#  distributed with this work for additional information
-#  regarding copyright ownership.  The ASF licenses this file
-#  to you under the Apache License, Version 2.0 (the
-#  "License"); you may not use this file except in compliance
-#  with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing,
-#  software distributed under the License is distributed on an
-#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#  KIND, either express or implied.  See the License for the
-#  specific language governing permissions and limitations
-#  under the License.
-
-
-r"""Ticket relations for Apache(TM) Bloodhound
-
-Ticket relations user interface.
-"""
-
-import re
-import pkg_resources
-
-from trac.core import Component, implements, TracError
-from trac.resource import get_resource_url, ResourceNotFound, Resource
-from trac.ticket.model import Ticket
-from trac.util.translation import _
-from trac.web import IRequestHandler
-from trac.web.chrome import ITemplateProvider, add_warning
-
-from bhrelations.api import RelationsSystem, ResourceIdSerializer, \
-    TicketRelationsSpecifics, UnknownRelationType
-from bhrelations.model import Relation
-from bhrelations.validation import ValidationError
-
-from multiproduct.model import Product
-from multiproduct.env import ProductEnvironment
-
-class RelationManagementModule(Component):
-    implements(IRequestHandler, ITemplateProvider)
-
-    # IRequestHandler methods
-    def match_request(self, req):
-        match = re.match(r'/ticket/([0-9]+)/relations/*$', req.path_info)
-        if not match:
-            return False
-
-        req.args['id'] = match.group(1)
-        return True
-
-    def process_request(self, req):
-        tid = req.args.get('id')
-        if not tid:
-            raise TracError(_('No ticket id provided.'))
-
-        try:
-            ticket = Ticket(self.env, tid)
-        except ValueError:
-            raise TracError(_('Invalid ticket id.'))
-
-        req.perm.require('TICKET_VIEW')
-        relsys = RelationsSystem(self.env)
-
-        data = {
-            'relation': {},
-        }
-        if req.method == 'POST':
-            # for modifying the relations TICKET_MODIFY is required for
-            # both the source and the destination tickets
-            req.perm.require('TICKET_MODIFY')
-
-            if 'remove' in req.args:
-                rellist = req.args.get('sel')
-                if rellist:
-                    if isinstance(rellist, basestring):
-                        rellist = [rellist, ]
-                    self.remove_relations(req, rellist)
-            elif 'add' in req.args:
-                relation = dict(
-                    destination=req.args.get('dest_tid', ''),
-                    type=req.args.get('reltype', ''),
-                    comment=req.args.get('comment', ''),
-                )
-                try:
-                    dest_ticket = self.find_ticket(relation['destination'])
-                    req.perm.require('TICKET_MODIFY',
-                                     Resource(dest_ticket.id))
-                    relsys.add(ticket, dest_ticket,
-                               relation['type'],
-                               relation['comment'],
-                               req.authname)
-                except NoSuchTicketError:
-                    data['error'] = _('Invalid ticket id.')
-                except UnknownRelationType:
-                    data['error'] = _('Unknown relation type.')
-                except ValidationError as ex:
-                    data['error'] = ex.message
-                if 'error' in data:
-                    data['relation'] = relation
-
-            else:
-                raise TracError(_('Invalid operation.'))
-
-        data.update({
-            'ticket': ticket,
-            'reltypes': relsys.get_relation_types(),
-            'relations': self.get_ticket_relations(ticket),
-        })
-        return 'manage.html', data, None
-
-    # ITemplateProvider methods
-    def get_htdocs_dirs(self):
-        resource_filename = pkg_resources.resource_filename
-        return [('relations', resource_filename('bhrelations', 'htdocs')), ]
-
-    def get_templates_dirs(self):
-        resource_filename = pkg_resources.resource_filename
-        return [resource_filename('bhrelations', 'templates'), ]
-
-    # utility functions
-    def get_ticket_relations(self, ticket):
-        grouped_relations = {}
-        relsys = RelationsSystem(self.env)
-        reltypes = relsys.get_relation_types()
-        trs = TicketRelationsSpecifics(self.env)
-        for r in relsys.get_relations(ticket):
-            r['desthref'] = get_resource_url(self.env, r['destination'],
-                self.env.href)
-            r['destticket'] = trs._create_ticket_by_full_id(r['destination'])
-            grouped_relations.setdefault(reltypes[r['type']], []).append(r)
-        return grouped_relations
-
-    def find_ticket(self, ticket_spec):
-        ticket = None
-        m = re.match(r'#?(?P<tid>\d+)', ticket_spec)
-        if m:
-            tid = m.group('tid')
-            try:
-                ticket = Ticket(self.env, tid)
-            except ResourceNotFound:
-                # ticket not found in current product, try all other products
-                for p in Product.select(self.env):
-                    if p.prefix != self.env.product.prefix:
-                        # TODO: check for PRODUCT_VIEW permissions
-                        penv = ProductEnvironment(self.env.parent, p.prefix)
-                        try:
-                            ticket = Ticket(penv, tid)
-                        except ResourceNotFound:
-                            pass
-                        else:
-                            break
-
-        # ticket still not found, use fallback for <prefix>:ticket:<id> syntax
-        if ticket is None:
-            trs = TicketRelationsSpecifics(self.env)
-            try:
-                resource = ResourceIdSerializer.get_resource_by_id(tid)
-                ticket = trs._create_ticket_by_full_id(resource)
-            except:
-                raise NoSuchTicketError
-        return ticket
-
-    def remove_relations(self, req, rellist):
-        relsys = RelationsSystem(self.env)
-        for relid in rellist:
-            relation = Relation.load_by_relation_id(self.env, relid)
-            resource = ResourceIdSerializer.get_resource_by_id(
-                relation.destination)
-            if 'TICKET_MODIFY' in req.perm(resource):
-                relsys.delete(relid)
-            else:
-                add_warning(req,
-                    _('Not enough permissions to remove relation "%s"' % relid))
-
-
-class NoSuchTicketError(ValueError):
-    pass
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+
+r"""Ticket relations for Apache(TM) Bloodhound
+
+Ticket relations user interface.
+"""
+
+import re
+
+from trac.core import Component, implements, TracError
+from trac.resource import get_resource_url, Resource
+from trac.ticket.model import Ticket
+from trac.util.translation import _
+from trac.web import IRequestHandler, IRequestFilter
+from trac.web.chrome import ITemplateProvider, add_warning
+
+from bhrelations.api import RelationsSystem, ResourceIdSerializer, \
+    TicketRelationsSpecifics, UnknownRelationType, NoSuchTicketError
+from bhrelations.model import Relation
+from bhrelations.validation import ValidationError
+
+
+class RelationManagementModule(Component):
+    implements(IRequestFilter, IRequestHandler, ITemplateProvider)
+
+    # IRequestHandler methods
+    def match_request(self, req):
+        match = re.match(r'/ticket/([0-9]+)/relations/*$', req.path_info)
+        if not match:
+            return False
+
+        req.args['id'] = match.group(1)
+        return True
+
+    def process_request(self, req):
+        tid = req.args.get('id')
+        if not tid:
+            raise TracError(_('No ticket id provided.'))
+
+        try:
+            ticket = Ticket(self.env, tid)
+        except ValueError:
+            raise TracError(_('Invalid ticket id.'))
+
+        req.perm.require('TICKET_VIEW')
+        relsys = RelationsSystem(self.env)
+
+        data = {
+            'relation': {},
+        }
+        if req.method == 'POST':
+            # for modifying the relations TICKET_MODIFY is required for
+            # both the source and the destination tickets
+            req.perm.require('TICKET_MODIFY')
+
+            if 'remove' in req.args:
+                rellist = req.args.get('sel')
+                if rellist:
+                    if isinstance(rellist, basestring):
+                        rellist = [rellist, ]
+                    self.remove_relations(req, rellist)
+            elif 'add' in req.args:
+                relation = dict(
+                    destination=req.args.get('dest_tid', ''),
+                    type=req.args.get('reltype', ''),
+                    comment=req.args.get('comment', ''),
+                )
+                try:
+                    trs = TicketRelationsSpecifics(self.env)
+                    dest_ticket = trs.find_ticket(relation['destination'])
+                except NoSuchTicketError:
+                    data['error'] = _('Invalid ticket ID.')
+                else:
+                    req.perm.require('TICKET_MODIFY', Resource(dest_ticket.id))
+
+                    try:
+                        relsys.add(ticket, dest_ticket,
+                            relation['type'],
+                            relation['comment'],
+                            req.authname)
+                    except NoSuchTicketError:
+                        data['error'] = _('Invalid ticket ID.')
+                    except UnknownRelationType:
+                        data['error'] = _('Unknown relation type.')
+                    except ValidationError as ex:
+                        data['error'] = ex.message
+
+                if 'error' in data:
+                    data['relation'] = relation
+            else:
+                raise TracError(_('Invalid operation.'))
+
+        data.update({
+            'ticket': ticket,
+            'reltypes': relsys.get_relation_types(),
+            'relations': self.get_ticket_relations(ticket),
+        })
+        return 'manage.html', data, None
+
+    # ITemplateProvider methods
+    def get_htdocs_dirs(self):
+        return []
+
+    def get_templates_dirs(self):
+        from pkg_resources import resource_filename
+        return [resource_filename('bhrelations', 'templates')]
+
+    # IRequestFilter methods
+    def pre_process_request(self, req, handler):
+        return handler
+
+    def post_process_request(self, req, template, data, content_type):
+        if req.path_info.startswith('/ticket/'):
+            ticket = data['ticket']
+            rls = RelationsSystem(self.env)
+            try:
+                resid = ResourceIdSerializer.get_resource_id_from_instance(
+                    self.env, ticket)
+            except ValueError:
+                resid = None
+
+            if rls.duplicate_relation_type and resid is not None:
+                duplicate_relations = \
+                    rls._select_relations(resid, rls.duplicate_relation_type)
+                if duplicate_relations:
+                    data['ticket_duplicate_of'] = \
+                        duplicate_relations[0].destination
+        return template, data, content_type
+
+    # utility functions
+    def get_ticket_relations(self, ticket):
+        grouped_relations = {}
+        relsys = RelationsSystem(self.env)
+        reltypes = relsys.get_relation_types()
+        trs = TicketRelationsSpecifics(self.env)
+        for r in relsys.get_relations(ticket):
+            r['desthref'] = get_resource_url(self.env, r['destination'],
+                self.env.href)
+            r['destticket'] = trs._create_ticket_by_full_id(r['destination'])
+            grouped_relations.setdefault(reltypes[r['type']], []).append(r)
+        return grouped_relations
+
+    def remove_relations(self, req, rellist):
+        relsys = RelationsSystem(self.env)
+        for relid in rellist:
+            relation = Relation.load_by_relation_id(self.env, relid)
+            resource = ResourceIdSerializer.get_resource_by_id(
+                relation.destination)
+            if 'TICKET_MODIFY' in req.perm(resource):
+                relsys.delete(relid)
+            else:
+                add_warning(req,
+                    _('Not enough permissions to remove relation "%s"' % relid))

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/widgets/relations.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/widgets/relations.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/widgets/relations.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/bhrelations/widgets/relations.py Sun Jul 28 17:22:23 2013
@@ -1,75 +1,75 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8 -*-
-
-#  Licensed to the Apache Software Foundation (ASF) under one
-#  or more contributor license agreements.  See the NOTICE file
-#  distributed with this work for additional information
-#  regarding copyright ownership.  The ASF licenses this file
-#  to you under the Apache License, Version 2.0 (the
-#  "License"); you may not use this file except in compliance
-#  with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing,
-#  software distributed under the License is distributed on an
-#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#  KIND, either express or implied.  See the License for the
-#  specific language governing permissions and limitations
-#  under the License.
-
-
-r"""Project dashboard for Apache(TM) Bloodhound
-
-Widgets displaying ticket relations.
-"""
-
-from trac.util.translation import _
-from trac.ticket.model import Ticket
-
-from bhdashboard.util import WidgetBase, check_widget_name, pretty_wrapper
-from bhrelations.web_ui import RelationManagementModule
-
-__metaclass__ = type
-
-
-class TicketRelationsWidget(WidgetBase):
-    """Display ticket relations.
-    """
-    def get_widget_params(self, name):
-        """Return a dictionary containing arguments specification for
-        the widget with specified name.
-        """
-        return {
-                'tid' : {
-                        'desc' : """Source ticket id""",
-                        'type' : int
-                    },
-
-                'max' : {
-                        'desc' : """Limit the number of relations displayed""",
-                        'type' : int
-                    },
-            }
-
-    get_widget_params = pretty_wrapper(get_widget_params, check_widget_name)
-
-    def render_widget(self, name, context, options):
-        """Gather list of relations and render data in compact view
-        """
-        req = context.req
-        title = _('Related tickets')
-        params = ('tid', 'max')
-        tid, max_ = self.bind_params(name, options, *params)
-
-        ticket = Ticket(self.env, tid)
-        data = {
-            'ticket': ticket,
-            'relations': \
-                RelationManagementModule(self.env).get_ticket_relations(ticket),
-        }
-        return 'widget_relations.html', \
-            { 'title': title, 'data': data, }, context
-
-    render_widget = pretty_wrapper(render_widget, check_widget_name)
-
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+
+
+r"""Project dashboard for Apache(TM) Bloodhound
+
+Widgets displaying ticket relations.
+"""
+
+from trac.util.translation import _
+from trac.ticket.model import Ticket
+
+from bhdashboard.util import WidgetBase, check_widget_name, pretty_wrapper
+from bhrelations.web_ui import RelationManagementModule
+
+__metaclass__ = type
+
+
+class TicketRelationsWidget(WidgetBase):
+    """Display ticket relations.
+    """
+    def get_widget_params(self, name):
+        """Return a dictionary containing arguments specification for
+        the widget with specified name.
+        """
+        return {
+                'tid' : {
+                        'desc' : """Source ticket id""",
+                        'type' : int
+                    },
+
+                'max' : {
+                        'desc' : """Limit the number of relations displayed""",
+                        'type' : int
+                    },
+            }
+
+    get_widget_params = pretty_wrapper(get_widget_params, check_widget_name)
+
+    def render_widget(self, name, context, options):
+        """Gather list of relations and render data in compact view
+        """
+        req = context.req
+        title = _('Related tickets')
+        params = ('tid', 'max')
+        tid, max_ = self.bind_params(name, options, *params)
+
+        ticket = Ticket(self.env, tid)
+        data = {
+            'ticket': ticket,
+            'relations': \
+                RelationManagementModule(self.env).get_ticket_relations(ticket),
+        }
+        return 'widget_relations.html', \
+            {'title': title, 'data': data, }, context
+
+    render_widget = pretty_wrapper(render_widget, check_widget_name)
+

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/setup.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/setup.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/setup.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_relations/setup.py Sun Jul 28 17:22:23 2013
@@ -17,7 +17,8 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-
+import sys
+from pkg_resources import parse_version
 try:
     from setuptools import setup
 except ImportError:
@@ -32,6 +33,7 @@ versions = [
     (0, 4, 0),
     (0, 5, 0),
     (0, 6, 0),
+    (0, 7, 0),
     ]
 
 latest = '.'.join(str(x) for x in versions[-1])
@@ -71,13 +73,6 @@ cats = [
       "Topic :: Software Development :: User Interfaces",
     ]
 
-# Be compatible with older versions of Python
-from sys import version
-if version < '2.2.3':
-    from distutils.dist import DistributionMetadata
-    DistributionMetadata.classifiers = None
-    DistributionMetadata.download_url = None
-
 # Add the change log to the package description.
 chglog = None
 try:
@@ -97,6 +92,8 @@ PKG_INFO = {'bhrelations': ('bhrelations
                               'htdocs/img/*.*', 'htdocs/js/*.js',
                               'templates/*', 'default-pages/*'],
                           ),
+            'bhrelations.widgets': (
+                'bhrelations/widgets', ['templates/*.html']),
             'bhrelations.tests': (
                 'bhrelations/tests', ['data/*.*']),
             }
@@ -116,7 +113,7 @@ setup(
     description=DESC.split('\n', 1)[0],
     author = "Apache Bloodhound",
     license = "Apache License v2",
-    url = "http://incubator.apache.org/bloodhound/",
+    url = "https://bloodhound.apache.org/",
     requires = ['trac'],
     install_requires = [
         'setuptools>=0.6b1',
@@ -132,6 +129,7 @@ setup(
     entry_points = ENTRY_POINTS,
     classifiers = cats,
     long_description= DESC,
-    test_suite='bhrelations.tests.test_suite'
+    test_suite='bhrelations.tests.test_suite',
+    tests_require=['unittest2' if parse_version(sys.version) < parse_version('2.7') else '']
     )
 

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/__init__.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/__init__.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/__init__.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/__init__.py Sun Jul 28 17:22:23 2013
@@ -17,10 +17,10 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-import sys
-if sys.version_info[:2] < (2, 7):
+
+try:
     import unittest2 as unittest
-else:
+except ImportError:
     import unittest
 
 from bhsearch.tests import (whoosh_backend, index_with_whoosh, web_ui,

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/api.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/api.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/api.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/api.py Sun Jul 28 17:22:23 2013
@@ -17,13 +17,13 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-import unittest
 import shutil
 
 from bhsearch.api import BloodhoundSearchApi, ASC, SortInstruction
 from bhsearch.query_parser import DefaultQueryParser
-from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.search_resources.ticket_search import TicketSearchParticipant
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.whoosh_backend import WhooshBackend
 
 

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/base.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/base.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/base.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/base.py Sun Jul 28 17:22:23 2013
@@ -23,29 +23,26 @@ Test utils methods
 """
 import contextlib
 import shutil
-import sys
 import tempfile
-if sys.version_info[:2] < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
 
-
-from bhsearch.web_ui import BloodhoundSearchModule
-from trac.ticket import Ticket, Milestone
 from trac.test import EnvironmentStub, Mock, MockPerm
+from trac.ticket import Ticket, Milestone
 from trac.web import Href, arg_list_to_args
 from trac.wiki import WikiPage
 
+from bhsearch.tests import unittest
+from bhsearch.web_ui import BloodhoundSearchModule
+
 BASE_PATH = "/main/"
 
+
 class BaseBloodhoundSearchTest(unittest.TestCase):
 
     def setUp(self, enabled=None, create_req=False, enable_security=False):
         if not enabled:
             enabled = ['trac.*', 'bhsearch.*']
         if not enable_security:
-            disabled = ['bhsearch.security.SecurityPreprocessor']
+            disabled = ['bhsearch.security.*']
         else:
             disabled = []
 

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/index_with_whoosh.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/index_with_whoosh.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/index_with_whoosh.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/index_with_whoosh.py Sun Jul 28 17:22:23 2013
@@ -18,14 +18,16 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-import unittest
 import shutil
+
 from bhsearch.api import BloodhoundSearchApi
 from bhsearch.search_resources.milestone_search import MilestoneIndexer
-from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.search_resources.ticket_search import TicketIndexer
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.whoosh_backend import WhooshBackend
 
+
 class IndexWhooshTestCase(BaseBloodhoundSearchTest):
     def setUp(self):
         super(IndexWhooshTestCase, self).setUp()

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_parser.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_parser.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_parser.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_parser.py Sun Jul 28 17:22:23 2013
@@ -18,10 +18,11 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-import unittest
-from bhsearch.tests.base import BaseBloodhoundSearchTest
-from bhsearch.query_parser import DefaultQueryParser
 from trac.test import Mock
+
+from bhsearch.query_parser import DefaultQueryParser
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
 from whoosh import query
 
 

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_suggestion.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_suggestion.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_suggestion.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/query_suggestion.py Sun Jul 28 17:22:23 2013
@@ -18,11 +18,10 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-import unittest
-from bhsearch.tests.base import BaseBloodhoundSearchTest
-
 from bhsearch.api import BloodhoundSearchApi
 from bhsearch.query_suggestion import SuggestionFields
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.web_ui import RequestParameters, RequestContext
 from bhsearch.whoosh_backend import WhooshBackend
 

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/real_index_view.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/real_index_view.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/real_index_view.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/real_index_view.py Sun Jul 28 17:22:23 2013
@@ -17,16 +17,18 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-import unittest
-from bhsearch.web_ui import RequestParameters
+
 import os
-from bhsearch.tests.base import BaseBloodhoundSearchTest
 
-from bhsearch.whoosh_backend import WhooshBackend
 from trac.test import EnvironmentStub, Mock, MockPerm
-from whoosh import query
 from trac.web import Href, arg_list_to_args
 
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
+from bhsearch.web_ui import RequestParameters
+from bhsearch.whoosh_backend import WhooshBackend
+from whoosh import query
+
 
 class RealIndexTestCase(BaseBloodhoundSearchTest):
     """

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/base.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/base.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/base.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/base.py Sun Jul 28 17:22:23 2013
@@ -18,17 +18,14 @@
 #  specific language governing permissions and limitations
 #  under the License.
 
-import sys
-if sys.version_info[:2] < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
+from trac.test import MockPerm
+from trac.web import Href
+from trac.wiki import format_to_html
 
 from bhsearch.search_resources.base import SimpleSearchWikiSyntaxFormatter
+from bhsearch.tests import unittest
 from bhsearch.tests.base import BaseBloodhoundSearchTest
-from trac.web import Href
-from trac.test import MockPerm
-from trac.wiki import format_to_html
+
 
 class SimpleSearchWikiSyntaxFormatterTestCase(BaseBloodhoundSearchTest):
     def setUp(self):

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/changeset_search.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/changeset_search.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/changeset_search.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/changeset_search.py Sun Jul 28 17:22:23 2013
@@ -17,17 +17,18 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-import unittest
+
+from trac.core import Component, implements
+from trac.versioncontrol import Changeset
+from trac.versioncontrol.api import (
+    IRepositoryConnector, Repository, RepositoryManager)
 
 from bhsearch.api import BloodhoundSearchApi
 from bhsearch.search_resources.changeset_search import (
     ChangesetSearchParticipant)
+from bhsearch.tests import unittest
 from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.whoosh_backend import WhooshBackend
-from trac.core import Component, implements
-from trac.versioncontrol.api import (IRepositoryConnector, RepositoryManager,
-                                     Repository)
-from trac.versioncontrol import Changeset
 
 
 class ChangesetIndexerEventsTestCase(BaseBloodhoundSearchTest):

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/milestone_search.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/milestone_search.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/milestone_search.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/milestone_search.py Sun Jul 28 17:22:23 2013
@@ -17,16 +17,16 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-import unittest
+
+from trac.ticket import Milestone
 
 from bhsearch.api import BloodhoundSearchApi
 from bhsearch.search_resources.milestone_search import (
     MilestoneSearchParticipant)
+from bhsearch.tests import unittest
 from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.whoosh_backend import WhooshBackend
 
-from trac.ticket import Milestone
-
 
 class MilestoneIndexerEventsTestCase(BaseBloodhoundSearchTest):
     DUMMY_MILESTONE_NAME = "dummyName"

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/ticket_search.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/ticket_search.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/ticket_search.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/ticket_search.py Sun Jul 28 17:22:23 2013
@@ -17,13 +17,15 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-import unittest
+
+from trac.ticket.model import Component
+
 from bhsearch.api import BloodhoundSearchApi
+from bhsearch.search_resources.ticket_search import TicketIndexer
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.whoosh_backend import WhooshBackend
 
-from bhsearch.tests.base import BaseBloodhoundSearchTest
-from bhsearch.search_resources.ticket_search import TicketIndexer
-from trac.ticket.model import Component
 
 class TicketIndexerTestCase(BaseBloodhoundSearchTest):
     def setUp(self):

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/wiki_search.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/wiki_search.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/wiki_search.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/search_resources/wiki_search.py Sun Jul 28 17:22:23 2013
@@ -18,16 +18,16 @@
 #  specific language governing permissions and limitations
 #  under the License.
 import shutil
-import unittest
+
+from trac.wiki import WikiSystem, WikiPage
 
 from bhsearch.api import BloodhoundSearchApi
 from bhsearch.query_parser import DefaultQueryParser
-from bhsearch.tests.base import BaseBloodhoundSearchTest
-from bhsearch.whoosh_backend import WhooshBackend
 from bhsearch.search_resources.wiki_search import (
     WikiIndexer, WikiSearchParticipant)
-
-from trac.wiki import WikiSystem, WikiPage
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
+from bhsearch.whoosh_backend import WhooshBackend
 
 
 class WikiIndexerSilenceOnExceptionTestCase(BaseBloodhoundSearchTest):

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/security.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/security.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/security.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/security.py Sun Jul 28 17:22:23 2013
@@ -24,17 +24,16 @@ system backend.
 """
 import contextlib
 import os
-import unittest
 from sqlite3 import OperationalError
 
-from trac.perm import (PermissionSystem, DefaultPermissionPolicy,
-                       PermissionCache)
+from trac.perm import (DefaultPermissionPolicy, PermissionCache,
+                       PermissionSystem)
 
-from multiproduct.api import MultiProductSystem, ProductEnvironment
-
-from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.api import BloodhoundSearchApi
+from bhsearch.tests import unittest
+from bhsearch.tests.base import BaseBloodhoundSearchTest
 from bhsearch.whoosh_backend import WhooshBackend
+from multiproduct.api import MultiProductSystem, ProductEnvironment
 
 # TODO: Convince trac to register modules without these imports
 from trac.wiki import web_ui

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/web_ui.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/web_ui.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/web_ui.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/web_ui.py Sun Jul 28 17:22:23 2013
@@ -17,31 +17,28 @@
 #  KIND, either express or implied.  See the License for the
 #  specific language governing permissions and limitations
 #  under the License.
-import sys
-if sys.version_info[:2] < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
 
 from urllib import urlencode, unquote, unquote_plus
 
+from trac.core import TracError
+from trac.search.web_ui import SearchModule as TracSearchModule
+from trac.test import Mock, MockPerm
+from trac.util import format_datetime
+from trac.util.datefmt import FixedOffset
+from trac.web import Href, RequestDone, arg_list_to_args, parse_arg_list
+
 from bhsearch import web_ui
 from bhsearch.api import ASC, DESC, SortInstruction
+from bhsearch.tests import unittest
 from bhsearch.tests.base import BaseBloodhoundSearchTest
-from bhsearch.web_ui import RequestParameters, BloodhoundSearchModule
+from bhsearch.web_ui import BloodhoundSearchModule, RequestParameters
 from bhsearch.whoosh_backend import WhooshBackend
 
-from trac.test import Mock, MockPerm
-from trac.core import TracError
-from trac.search.web_ui import SearchModule as TracSearchModule
-from trac.util.datefmt import FixedOffset
-from trac.util import format_datetime
-from trac.web import Href, arg_list_to_args, parse_arg_list, RequestDone
-
 BASE_PATH = "/main/"
 BHSEARCH_URL = BASE_PATH + "bhsearch"
 DEFAULT_DOCS_PER_PAGE = 10
 
+
 class WebUiTestCaseWithWhoosh(BaseBloodhoundSearchTest):
     def setUp(self):
         super(WebUiTestCaseWithWhoosh, self).setUp(

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/whoosh_backend.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/whoosh_backend.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/whoosh_backend.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/bhsearch/tests/whoosh_backend.py Sun Jul 28 17:22:23 2013
@@ -20,23 +20,20 @@
 from datetime import datetime
 import os
 import shutil
-import sys
 import tempfile
-if sys.version_info[:2] < (2, 7):
-    import unittest2 as unittest
-else:
-    import unittest
+
+from trac.util.datefmt import FixedOffset, utc
 
 from bhsearch.api import ASC, DESC, SCORE, SortInstruction
 from bhsearch.query_parser import DefaultQueryParser
+from bhsearch.tests import unittest
 from bhsearch.tests.base import BaseBloodhoundSearchTest
-from bhsearch.whoosh_backend import (WhooshBackend,
-    WhooshEmptyFacetErrorWorkaround)
-from trac.util.datefmt import FixedOffset, utc
-from whoosh import index, sorting, query
-from whoosh.fields import Schema, ID, TEXT, KEYWORD
-from whoosh.qparser import MultifieldPlugin, QueryParser, WhitespacePlugin, \
-    PhrasePlugin, MultifieldParser
+from bhsearch.whoosh_backend import WhooshBackend, \
+    WhooshEmptyFacetErrorWorkaround
+from whoosh import index, query, sorting
+from whoosh.fields import ID, KEYWORD, TEXT, Schema
+from whoosh.qparser import MultifieldParser, MultifieldPlugin, PhrasePlugin, \
+    QueryParser, WhitespacePlugin
 
 
 class WhooshBackendTestCase(BaseBloodhoundSearchTest):

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/setup.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/setup.py?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/setup.py (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_search/setup.py Sun Jul 28 17:22:23 2013
@@ -18,6 +18,8 @@
 #  under the License.
 
 
+import sys
+from pkg_resources import parse_version
 try:
     from setuptools import setup
 except ImportError:
@@ -32,6 +34,7 @@ versions = [
     (0, 4, 0),
     (0, 5, 0),
     (0, 6, 0),
+    (0, 7, 0),
     ]
 
 latest = '.'.join(str(x) for x in versions[-1])
@@ -71,13 +74,6 @@ cats = [
       "Topic :: Software Development :: User Interfaces",
     ]
 
-# Be compatible with older versions of Python
-from sys import version
-if version < '2.2.3':
-    from distutils.dist import DistributionMetadata
-    DistributionMetadata.classifiers = None
-    DistributionMetadata.download_url = None
-
 # Add the change log to the package description.
 chglog = None
 try:
@@ -145,7 +141,7 @@ setup(
     description=DESC.split('\n', 1)[0],
     author = "Apache Bloodhound",
     license = "Apache License v2",
-    url = "http://incubator.apache.org/bloodhound/",
+    url = "https://bloodhound.apache.org/",
     requires = ['trac'],
     install_requires = [
         'setuptools>=0.6b1',
@@ -163,5 +159,6 @@ setup(
     classifiers = cats,
     long_description= DESC,
     test_suite='bhsearch.tests.test_suite',
+    tests_require=['unittest2' if parse_version(sys.version) < parse_version('2.7') else '']
     )
 

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/bloodhound.css
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/bloodhound.css?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/bloodhound.css (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/bloodhound.css Sun Jul 28 17:22:23 2013
@@ -174,6 +174,11 @@ div.reports form {
  text-align: right;
 }
 
+#duplicate_id {
+  margin-left: 10px;
+  margin-right: 10px;
+}
+
 #trac-ticket-title {
  margin-bottom: 5px;
 }
@@ -465,6 +470,12 @@ h1, h2, h3, h4 {
  padding: 0.8em 0px;
 }
 
+#edit, #text {
+ min-height: 10em;
+ padding: 0px;
+ resize: vertical;
+}
+
 /* @end */
 
 /* @group Search */

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/js/theme.js
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/js/theme.js?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/js/theme.js (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/htdocs/js/theme.js Sun Jul 28 17:22:23 2013
@@ -123,17 +123,17 @@ $( function () {
       );
     $('#qct-create').click(
         function() {
-          var base_url = $('#qct-create').attr('data-target');
-          if (base_url === '/')
-            base_url = '';
-          $.post(base_url + '/qct', $('#qct-form').serialize(), 
-              function(ticket_id) {
-                var href = base_url + '/ticket/' + ticket_id;
+          // data-target is the base url for the product in current scope
+          var product_base_url = $('#qct-create').attr('data-target');
+          if (product_base_url === '/')
+            product_base_url = '';
+          $.post(product_base_url + '/qct', $('#qct-form').serialize(),
+              function(ticket) {
                 qct_alert({
-                    ticket: ticket_id,
+                    ticket: ticket.id,
                     msg: '<span class="alert alert-success">' +
                          'Has been created</span> ' +
-                         '<a href="' + href + '">View / Edit</a>'
+                         '<a href="' + ticket.url + '">View / Edit</a>'
                   });
               })
               .error(function(jqXHR, textStatus, errorMsg) {
@@ -155,30 +155,29 @@ $( function () {
       );
 
     $('#qct-inline-create').click(function() {
-      var base_url = $('#qct-inline-create').attr('data-target');
-      if (base_url === '/')
-        base_url = '';
-
-      $.post(base_url + '/qct', $('#qct-inline-form').serialize(), 
-        function(ticket_id) {
-          var href = base_url + '/ticket/' + ticket_id;
-          var msg = 'Ticket #' + ticket_id + ' has been created. ';
-          msg += '<a href="' + href + '">View / Edit</a>';
-          $('#qct-inline-notice-success span').html(msg);
-          $('#qct-inline-notice-success').show({'duration': 400});
-        })
-        .error(function(jqXHR, textStatus, errorMsg) {
-          var msg;
-          if (textStatus === 'timeout')
-            msg = 'Request timed out';
-          else if (textStatus === 'error')
-            msg = 'Could not create ticket. Error : ' + errorMsg;
-          else if (textStatus === 'abort')
-            msg = 'Aborted request';
-
-          $('#qct-inline-notice-error span').html(msg);
-          $('#qct-inline-notice-error').show({'duration': 400});
-        });
+      // data-target is the base url for the product in current scope
+      var product_base_url = $('#qct-inline-create').attr('data-target');
+      if (product_base_url === '/')
+        product_base_url = '';
+      $.post(product_base_url + '/qct', $('#qct-inline-form').serialize(),
+          function(ticket) {
+            var msg = 'Ticket #' + ticket.id + ' has been created. ';
+            msg += '<a href="' + ticket.url + '">View / Edit</a>';
+            $('#qct-inline-notice-success span').html(msg);
+            $('#qct-inline-notice-success').show({'duration': 400});
+          })
+          .error(function(jqXHR, textStatus, errorMsg) {
+            var msg;
+            if (textStatus === 'timeout')
+              msg = 'Request timed out';
+            else if (textStatus === 'error')
+              msg = 'Could not create ticket. Error : ' + errorMsg;
+            else if (textStatus === 'abort')
+              msg = 'Aborted request';
+
+            $('#qct-inline-notice-error span').html(msg);
+            $('#qct-inline-notice-error').show({'duration': 400});
+          });
 
       qct_clearui();
       qct_inline_close();

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_about.html
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_about.html?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_about.html (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_about.html Sun Jul 28 17:22:23 2013
@@ -39,7 +39,7 @@
 
   <body>
     <div id="content" class="about">
-      <a href="http://incubator.apache.org/bloodhound/"
+      <a href="https://bloodhound.apache.org/"
          style="border: none; float: right; margin-left: 2em">
         <img style="display: block" src="${href.chrome('theme/img/bh_logo.png')}"
              alt="${chrome.labels.application_full}: Integrated SCM &amp; Project Management"/>

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_account_details.html
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_account_details.html?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_account_details.html (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_account_details.html Sun Jul 28 17:22:23 2013
@@ -29,6 +29,7 @@
   <?python
     if _dgettext:
         dgettext = _dgettext ?>
+
   <head>
     <title>Account Details</title>
   </head>

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_products.html
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_products.html?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_products.html (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_products.html Sun Jul 28 17:22:23 2013
@@ -142,8 +142,8 @@
                       </td>
                       <td class="owner">$prod.owner</td>
                       <td class="default">
-                        <input type="radio" name="default" value="$prod.name"
-                               checked="${prod.name==default or None}" />
+                        <input type="radio" name="default" value="$prod.prefix"
+                               checked="${prod.prefix==default or None}" />
                       </td>
                     </tr>
                   </tbody>

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_users.html
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_users.html?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_users.html (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_admin_users.html Sun Jul 28 17:22:23 2013
@@ -31,6 +31,11 @@
         dgettext = _dgettext ?>
   <head>
     <title>Accounts</title>
+    <script type="text/javascript">
+      jQuery(document).ready(function($) {
+        $("#username").focus();
+      });
+    </script>
   </head>
 
   <body>
@@ -137,49 +142,21 @@
                       py:with="msglabel = 'Warning';
                                msgbody = _('This password store does not support listing users.')"/>
         </div>
-        <form py:otherwise="" method="post"  py:strip="not delete_enabled">
-          <div>
-            <table id="accountlist"
-                   class="listing table table-striped table-condensed table-bordered" >
-              <thead>
-                <tr>
-                  <th py:if="delete_enabled" class="sel">
-                    <i class="icon-check"></i>
-                  </th>
-                  <th>Account</th><th>Name</th><th>Email</th><th>Last Login</th>
-                </tr>
-              </thead>
-              <tbody>
-                <tr py:for="acct in accounts">
-                  <td py:if="delete_enabled">
-                    <input type="checkbox" name="sel" value="${acct.username}" />
-                  </td>
-                  <td>
-                    <a href="${acct.review_url}">$acct.username</a>
-                    <!--! Additional account status icons -->
-                    <py:choose py:if="acct.locked">
-                      <img py:when="acct.release_hint" alt="Account locked"
-                           src="${href.chrome('/acct_mgr/time-locked.png')}"
-                           title="${acct.release_hint}" />
-                      <img py:otherwise="" alt="Permanently locked"
-                           src="${href.chrome('/acct_mgr/locked.png')}"
-                           title="${dgettext('acct_mgr', 'Permanently locked')}" />
-                    </py:choose>
-                  </td>
-                  <td>$acct.name</td>
-                  <td>$acct.email</td>
-                  <td>$acct.last_visit</td>
-                </tr>
-              </tbody>
-            </table>
-          </div>
-          <div class="buttons">
-            <input type="submit" name="reset" class="btn"
+        <form method="post" py:otherwise="" py:strip="not delete_enabled">
+          <div class="system-message"
+               py:if="deletion_error"><p>${deletion_error}</p></div>
+          <xi:include href="bh_user_table.html"/>
+          <div class="buttons control-group">
+            <input type="submit" class="btn" name="reset"
                    py:if="password_reset_enabled"
+                   title="Send another random password"
                    value="${dgettext('acct_mgr', 'Reset passwords')}" />
-            <input type="submit" name="remove" class="btn"
+            <input type="submit" class="btn" name="remove"
                    py:if="delete_enabled"
                    value="${dgettext('acct_mgr', 'Remove selected accounts')}" />
+            <input type="submit" class="btn" name="cleanup"
+                   py:if="req.perm.has_permission('ACCTMGR_ADMIN')"
+                   value="${dgettext('acct_mgr', 'Review account attributes')}" />
           </div>
         </form>
         </py:choose>

Modified: bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_browser.html
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_browser.html?rev=1507818&r1=1507817&r2=1507818&view=diff
==============================================================================
--- bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_browser.html (original)
+++ bloodhound/branches/bep_0007_embeddable_objects/bloodhound_theme/bhtheme/templates/bh_browser.html Sun Jul 28 17:22:23 2013
@@ -255,9 +255,12 @@
         </form>
       </div>
 
-      <div id="help" i18n:msg="" class="help-block pull-right">
-        <span class="label label-info">Note</span> See <a href="${href.wiki('TracBrowser')}">TracBrowser</a>
-        for help on using the repository browser.
+      <div id="help" class="span12" i18n:msg="">
+        <p class="help-block pull-right">
+          <span class="label label-info">Note</span> See
+          <a href="${href.wiki('TracBrowser')}">TracBrowser</a>
+          for help on using the repository browser.
+        </p>
       </div>
 
     </div>