Merge lp:~cjwatson/launchpad/git-ref-url into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: no longer in the source branch.
Merged at revision: 17404
Proposed branch: lp:~cjwatson/launchpad/git-ref-url
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/git-ref-scanner
Diff against target: 342 lines (+181/-3)
12 files modified
lib/lp/app/browser/configure.zcml (+8/-1)
lib/lp/app/browser/tales.py (+10/-1)
lib/lp/app/doc/tales.txt (+15/-0)
lib/lp/code/browser/configure.zcml (+18/-0)
lib/lp/code/browser/gitref.py (+19/-0)
lib/lp/code/browser/gitrepository.py (+22/-0)
lib/lp/code/browser/tests/test_gitrepository.py (+21/-0)
lib/lp/code/interfaces/gitref.py (+4/-0)
lib/lp/code/model/gitref.py (+4/-0)
lib/lp/code/model/tests/test_gitref.py (+24/-0)
lib/lp/code/templates/gitref-index.pt (+34/-0)
lib/lp/registry/browser/person.py (+2/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/git-ref-url
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+252900@code.launchpad.net

Commit message

Add basic navigation support for Git references.

Description of the change

Add basic navigation support for Git references.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/app/browser/configure.zcml'
--- lib/lp/app/browser/configure.zcml 2014-11-29 01:33:59 +0000
+++ lib/lp/app/browser/configure.zcml 2015-03-13 14:20:26 +0000
@@ -1,4 +1,4 @@
1<!-- Copyright 2009-2014 Canonical Ltd. This software is licensed under the1<!-- Copyright 2009-2015 Canonical Ltd. This software is licensed under the
2 GNU Affero General Public License version 3 (see the file LICENSE).2 GNU Affero General Public License version 3 (see the file LICENSE).
3-->3-->
44
@@ -729,6 +729,13 @@
729 />729 />
730730
731 <adapter731 <adapter
732 for="lp.code.interfaces.gitrepository.IGitRepository"
733 provides="zope.traversing.interfaces.IPathAdapter"
734 factory="lp.app.browser.tales.GitRepositoryFormatterAPI"
735 name="fmt"
736 />
737
738 <adapter
732 for="lp.bugs.interfaces.bugbranch.IBugBranch"739 for="lp.bugs.interfaces.bugbranch.IBugBranch"
733 provides="zope.traversing.interfaces.IPathAdapter"740 provides="zope.traversing.interfaces.IPathAdapter"
734 factory="lp.app.browser.tales.BugBranchFormatterAPI"741 factory="lp.app.browser.tales.BugBranchFormatterAPI"
735742
=== modified file 'lib/lp/app/browser/tales.py'
--- lib/lp/app/browser/tales.py 2014-11-27 05:01:51 +0000
+++ lib/lp/app/browser/tales.py 2015-03-13 14:20:26 +0000
@@ -1,4 +1,4 @@
1# Copyright 2009-2014 Canonical Ltd. This software is licensed under the1# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Implementation of the lp: htmlform: fmt: namespaces in TALES."""4"""Implementation of the lp: htmlform: fmt: namespaces in TALES."""
@@ -1674,6 +1674,15 @@
1674 }1674 }
16751675
16761676
1677class GitRepositoryFormatterAPI(CustomizableFormatter):
1678 """Adapter for IGitRepository objects to a formatted string."""
1679
1680 _link_summary_template = '%(display_name)s'
1681
1682 def _link_summary_values(self):
1683 return {'display_name': self._context.display_name}
1684
1685
1677class BugBranchFormatterAPI(CustomizableFormatter):1686class BugBranchFormatterAPI(CustomizableFormatter):
1678 """Adapter providing fmt support for BugBranch objects"""1687 """Adapter providing fmt support for BugBranch objects"""
16791688
16801689
=== modified file 'lib/lp/app/doc/tales.txt'
--- lib/lp/app/doc/tales.txt 2014-12-31 13:24:43 +0000
+++ lib/lp/app/doc/tales.txt 2015-03-13 14:20:26 +0000
@@ -394,6 +394,7 @@
394394
395 * people / teams395 * people / teams
396 * branches396 * branches
397 * Git repositories
397 * bugs398 * bugs
398 * bug subscriptions399 * bug subscriptions
399 * bug tasks400 * bug tasks
@@ -520,6 +521,20 @@
520 <a href=".../~eric/fooix/bar" class="sprite branch">lp://dev/fooix</a>521 <a href=".../~eric/fooix/bar" class="sprite branch">lp://dev/fooix</a>
521522
522523
524Git repositories
525................
526
527For Git repositories, fmt:link links to the branch page.
528
529 >>> from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
530 >>> from lp.services.features.testing import FeatureFixture
531 >>> with FeatureFixture({GIT_FEATURE_FLAG: 'on'}):
532 ... repository = factory.makeGitRepository(
533 ... owner=eric, target=fooix, name=u'bar')
534 ... print test_tales("repository/fmt:link", repository=repository)
535 <a href=".../~eric/fooix/+git/bar">lp:~eric/fooix/+git/bar</a>
536
537
523Bugs538Bugs
524....539....
525540
526541
=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml 2015-03-05 16:23:26 +0000
+++ lib/lp/code/browser/configure.zcml 2015-03-13 14:20:26 +0000
@@ -771,6 +771,9 @@
771 <browser:url771 <browser:url
772 for="lp.code.interfaces.gitrepository.IGitRepository"772 for="lp.code.interfaces.gitrepository.IGitRepository"
773 urldata="lp.code.browser.gitrepository.GitRepositoryURL"/>773 urldata="lp.code.browser.gitrepository.GitRepositoryURL"/>
774 <browser:navigation
775 module="lp.code.browser.gitrepository"
776 classes="GitRepositoryNavigation"/>
774 <browser:menus777 <browser:menus
775 module="lp.code.browser.gitrepository"778 module="lp.code.browser.gitrepository"
776 classes="779 classes="
@@ -795,6 +798,21 @@
795 factory="lp.code.browser.gitrepository.GitRepositoryBreadcrumb"798 factory="lp.code.browser.gitrepository.GitRepositoryBreadcrumb"
796 permission="zope.Public"/>799 permission="zope.Public"/>
797800
801 <browser:defaultView
802 for="lp.code.interfaces.gitref.IGitRef"
803 name="+index"/>
804 <browser:url
805 for="lp.code.interfaces.gitref.IGitRef"
806 path_expression="string:+ref/${path}"
807 attribute_to_parent="repository"
808 rootsite="code"/>
809 <browser:page
810 for="lp.code.interfaces.gitref.IGitRef"
811 class="lp.code.browser.gitref.GitRefView"
812 permission="launchpad.View"
813 name="+index"
814 template="../templates/gitref-index.pt"/>
815
798 <browser:menus816 <browser:menus
799 classes="ProductBranchesMenu"817 classes="ProductBranchesMenu"
800 module="lp.code.browser.branchlisting"/>818 module="lp.code.browser.branchlisting"/>
801819
=== added file 'lib/lp/code/browser/gitref.py'
--- lib/lp/code/browser/gitref.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/browser/gitref.py 2015-03-13 14:20:26 +0000
@@ -0,0 +1,19 @@
1# Copyright 2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Git reference views."""
5
6__metaclass__ = type
7
8__all__ = [
9 'GitRefView',
10 ]
11
12from lp.services.webapp import LaunchpadView
13
14
15class GitRefView(LaunchpadView):
16
17 @property
18 def label(self):
19 return self.context.display_name
020
=== modified file 'lib/lp/code/browser/gitrepository.py'
--- lib/lp/code/browser/gitrepository.py 2015-03-04 16:49:42 +0000
+++ lib/lp/code/browser/gitrepository.py 2015-03-13 14:20:26 +0000
@@ -8,6 +8,7 @@
8__all__ = [8__all__ = [
9 'GitRepositoryBreadcrumb',9 'GitRepositoryBreadcrumb',
10 'GitRepositoryContextMenu',10 'GitRepositoryContextMenu',
11 'GitRepositoryNavigation',
11 'GitRepositoryURL',12 'GitRepositoryURL',
12 'GitRepositoryView',13 'GitRepositoryView',
13 ]14 ]
@@ -16,12 +17,15 @@
16from zope.interface import implements17from zope.interface import implements
1718
18from lp.app.browser.informationtype import InformationTypePortletMixin19from lp.app.browser.informationtype import InformationTypePortletMixin
20from lp.app.errors import NotFoundError
19from lp.code.interfaces.gitrepository import IGitRepository21from lp.code.interfaces.gitrepository import IGitRepository
20from lp.services.config import config22from lp.services.config import config
21from lp.services.webapp import (23from lp.services.webapp import (
22 ContextMenu,24 ContextMenu,
23 LaunchpadView,25 LaunchpadView,
24 Link,26 Link,
27 Navigation,
28 stepto,
25 )29 )
26from lp.services.webapp.authorization import (30from lp.services.webapp.authorization import (
27 check_permission,31 check_permission,
@@ -54,6 +58,24 @@
54 return self.context.unique_name.split("/")[-1]58 return self.context.unique_name.split("/")[-1]
5559
5660
61class GitRepositoryNavigation(Navigation):
62
63 usedfor = IGitRepository
64
65 @stepto("+ref")
66 def traverse_ref(self):
67 segments = list(self.request.getTraversalStack())
68 ref_segments = []
69 while segments:
70 ref_segments.append(segments.pop())
71 ref = self.context.getRefByPath("/".join(ref_segments))
72 if ref is not None:
73 for _ in range(len(ref_segments)):
74 self.request.stepstogo.consume()
75 return ref
76 raise NotFoundError
77
78
57class GitRepositoryContextMenu(ContextMenu):79class GitRepositoryContextMenu(ContextMenu):
58 """Context menu for `IGitRepository`."""80 """Context menu for `IGitRepository`."""
5981
6082
=== modified file 'lib/lp/code/browser/tests/test_gitrepository.py'
--- lib/lp/code/browser/tests/test_gitrepository.py 2015-03-05 15:28:11 +0000
+++ lib/lp/code/browser/tests/test_gitrepository.py 2015-03-13 14:20:26 +0000
@@ -24,15 +24,36 @@
24 login_person,24 login_person,
25 logout,25 logout,
26 person_logged_in,26 person_logged_in,
27 TestCaseWithFactory,
27 )28 )
28from lp.testing.layers import DatabaseFunctionalLayer29from lp.testing.layers import DatabaseFunctionalLayer
29from lp.testing.pages import (30from lp.testing.pages import (
30 setupBrowser,31 setupBrowser,
31 setupBrowserForUser,32 setupBrowserForUser,
32 )33 )
34from lp.testing.publication import test_traverse
33from lp.testing.views import create_initialized_view35from lp.testing.views import create_initialized_view
3436
3537
38class TestGitRepositoryNavigation(TestCaseWithFactory):
39
40 layer = DatabaseFunctionalLayer
41
42 def setUp(self):
43 super(TestGitRepositoryNavigation, self).setUp()
44 self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
45
46 def test_traverse_ref(self):
47 [ref] = self.factory.makeGitRefs()
48 url = "%s/+ref/%s" % (canonical_url(ref.repository), ref.path)
49 self.assertEqual(ref, test_traverse(url)[0])
50
51 def test_traverse_ref_missing(self):
52 repository = self.factory.makeGitRepository()
53 url = "%s/+ref/refs/heads/master" % canonical_url(repository)
54 self.assertRaises(NotFound, test_traverse, url)
55
56
36class TestGitRepositoryView(BrowserTestCase):57class TestGitRepositoryView(BrowserTestCase):
3758
38 layer = DatabaseFunctionalLayer59 layer = DatabaseFunctionalLayer
3960
=== modified file 'lib/lp/code/interfaces/gitref.py'
--- lib/lp/code/interfaces/gitref.py 2015-03-13 14:20:26 +0000
+++ lib/lp/code/interfaces/gitref.py 2015-03-13 14:20:26 +0000
@@ -41,3 +41,7 @@
41 object_type = Choice(41 object_type = Choice(
42 title=_("Object type"), required=True, readonly=True,42 title=_("Object type"), required=True, readonly=True,
43 vocabulary=GitObjectType)43 vocabulary=GitObjectType)
44
45 display_name = TextLine(
46 title=_("Display name"), required=True, readonly=True,
47 description=_("Display name of the reference."))
4448
=== modified file 'lib/lp/code/model/gitref.py'
--- lib/lp/code/model/gitref.py 2015-03-13 14:20:26 +0000
+++ lib/lp/code/model/gitref.py 2015-03-13 14:20:26 +0000
@@ -35,3 +35,7 @@
35 commit_sha1 = Unicode(name='commit_sha1', allow_none=False)35 commit_sha1 = Unicode(name='commit_sha1', allow_none=False)
3636
37 object_type = EnumCol(enum=GitObjectType, notNull=True)37 object_type = EnumCol(enum=GitObjectType, notNull=True)
38
39 @property
40 def display_name(self):
41 return self.path.split("/", 2)[-1]
3842
=== added file 'lib/lp/code/model/tests/test_gitref.py'
--- lib/lp/code/model/tests/test_gitref.py 1970-01-01 00:00:00 +0000
+++ lib/lp/code/model/tests/test_gitref.py 2015-03-13 14:20:26 +0000
@@ -0,0 +1,24 @@
1# Copyright 2015 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for Git references."""
5
6__metaclass__ = type
7
8from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
9from lp.services.features.testing import FeatureFixture
10from lp.testing import TestCaseWithFactory
11from lp.testing.layers import DatabaseFunctionalLayer
12
13
14class TestGitRef(TestCaseWithFactory):
15
16 layer = DatabaseFunctionalLayer
17
18 def test_display_name(self):
19 self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
20 [master, personal] = self.factory.makeGitRefs(
21 paths=[u"refs/heads/master", u"refs/heads/people/foo/bar"])
22 self.assertEqual(
23 ["master", "people/foo/bar"],
24 [ref.display_name for ref in (master, personal)])
025
=== added file 'lib/lp/code/templates/gitref-index.pt'
--- lib/lp/code/templates/gitref-index.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/code/templates/gitref-index.pt 2015-03-13 14:20:26 +0000
@@ -0,0 +1,34 @@
1<html
2 xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
6 metal:use-macro="view/macro:page/main_side"
7 i18n:domain="launchpad"
8>
9
10<body>
11
12<div metal:fill-slot="main">
13
14 <div class="yui-g">
15 <div id="ref-info" class="portlet">
16 <h2>Branch information</h2>
17 <div class="two-column-list">
18 <dl id="name">
19 <dt>Name:</dt>
20 <dd tal:content="context/display_name" />
21 </dl>
22
23 <dl id="repository">
24 <dt>Repository:</dt>
25 <dd tal:content="structure context/repository/fmt:link" />
26 </dl>
27 </div>
28 </div>
29 </div>
30
31</div>
32
33</body>
34</html>
035
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2015-03-09 15:04:54 +0000
+++ lib/lp/registry/browser/person.py 2015-03-13 14:20:26 +0000
@@ -379,7 +379,8 @@
379 iter_segments, owner=self.context)379 iter_segments, owner=self.context)
380 if repository is None:380 if repository is None:
381 raise NotFoundError381 raise NotFoundError
382 for i in range(num_segments - len(list(iter_segments))):382 # Subtract one because the pillar has already been traversed.
383 for _ in range(num_segments - len(list(iter_segments)) - 1):
383 self.request.stepstogo.consume()384 self.request.stepstogo.consume()
384385
385 if IProduct.providedBy(target):386 if IProduct.providedBy(target):