Merge lp:~cjwatson/launchpad/codeimport-git-read-only-views into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18236
Proposed branch: lp:~cjwatson/launchpad/codeimport-git-read-only-views
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/git-code-import-security-deletion
Diff against target: 620 lines (+186/-88)
13 files modified
lib/lp/code/browser/branch.py (+18/-35)
lib/lp/code/browser/codeimport.py (+50/-11)
lib/lp/code/browser/configure.zcml (+5/-2)
lib/lp/code/browser/gitrepository.py (+19/-1)
lib/lp/code/browser/tests/test_codeimport.py (+39/-19)
lib/lp/code/configure.zcml (+1/-0)
lib/lp/code/interfaces/codeimport.py (+10/-4)
lib/lp/code/model/codeimport.py (+7/-0)
lib/lp/code/templates/branch-index.pt (+1/-1)
lib/lp/code/templates/codeimport-machine-index.pt (+2/-2)
lib/lp/code/templates/codeimport-machines.pt (+3/-3)
lib/lp/code/templates/gitrepository-index.pt (+10/-0)
lib/lp/code/templates/import-details.pt (+21/-10)
To merge this branch: bzr merge lp:~cjwatson/launchpad/codeimport-git-read-only-views
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+308374@code.launchpad.net

Commit message

Add read-only views for Git-targeted code imports.

Description of the change

We don't have much in the way of tests for this yet, but that will be easier to bring up once we have creation webservice/view handling in place.

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
1=== modified file 'lib/lp/code/browser/branch.py'
2--- lib/lp/code/browser/branch.py 2016-05-07 00:40:18 +0000
3+++ lib/lp/code/browser/branch.py 2016-10-14 15:45:46 +0000
4@@ -89,12 +89,12 @@
5 latest_proposals_for_each_branch,
6 )
7 from lp.code.browser.branchref import BranchRef
8+from lp.code.browser.codeimport import CodeImportTargetMixin
9 from lp.code.browser.decorations import DecoratedBranch
10 from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
11 from lp.code.browser.widgets.branchtarget import BranchTargetWidget
12 from lp.code.enums import (
13 BranchType,
14- CodeImportResultStatus,
15 CodeImportReviewStatus,
16 )
17 from lp.code.errors import (
18@@ -240,9 +240,6 @@
19 links = (
20 'edit', 'reviewer', 'edit_whiteboard', 'webhooks', 'delete')
21
22- def branch_is_import(self):
23- return self.context.branch_type == BranchType.IMPORTED
24-
25 @enabled_with_permission('launchpad.Edit')
26 def edit(self):
27 text = 'Change branch details'
28@@ -256,7 +253,7 @@
29 @enabled_with_permission('launchpad.AnyPerson')
30 def edit_whiteboard(self):
31 text = 'Edit whiteboard'
32- enabled = self.branch_is_import()
33+ enabled = self.context.branch_type == BranchType.IMPORTED
34 return Link(
35 '+whiteboard', text, icon='edit', enabled=enabled)
36
37@@ -403,7 +400,7 @@
38
39
40 class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
41- LaunchpadView, HasSnapsViewMixin):
42+ LaunchpadView, HasSnapsViewMixin, CodeImportTargetMixin):
43
44 feed_types = (
45 BranchFeedLink,
46@@ -448,8 +445,7 @@
47
48 The whiteboard is only shown for import branches.
49 """
50- if (self.context.branch_type == BranchType.IMPORTED and
51- self.context.whiteboard):
52+ if self.is_imported and self.context.whiteboard:
53 return True
54 else:
55 return False
56@@ -536,11 +532,23 @@
57 count).escapedtext
58
59 @property
60+ def is_imported(self):
61+ """Is this an imported branch?"""
62+ return self.context.branch_type == BranchType.IMPORTED
63+
64+ @property
65+ def can_edit_import(self):
66+ """Can the user edit this import?"""
67+ # XXX cjwatson 2016-10-14: Delete this once we have views for
68+ # editing Git imports.
69+ return True
70+
71+ @property
72 def is_import_branch_with_no_landing_candidates(self):
73 """Is the branch an import branch with no landing candidates?"""
74 if self.landing_candidates:
75 return False
76- if not self.context.branch_type == BranchType.IMPORTED:
77+ if not self.is_imported:
78 return False
79 return True
80
81@@ -596,31 +604,6 @@
82 return collection.getExtendedRevisionDetails(
83 self.user, self.context.latest_revisions)
84
85- @cachedproperty
86- def latest_code_import_results(self):
87- """Return the last 10 CodeImportResults."""
88- return list(self.context.code_import.results[:10])
89-
90- def iconForCodeImportResultStatus(self, status):
91- """The icon to represent the `CodeImportResultStatus` `status`."""
92- if status == CodeImportResultStatus.SUCCESS_PARTIAL:
93- return "/@@/yes-gray"
94- elif status in CodeImportResultStatus.successes:
95- return "/@@/yes"
96- else:
97- return "/@@/no"
98-
99- @property
100- def url_is_web(self):
101- """True if an imported branch's URL is HTTP or HTTPS."""
102- # You should only be calling this if it's an SVN, BZR or GIT code
103- # import
104- assert self.context.code_import
105- url = self.context.code_import.url
106- assert url
107- # https starts with http too!
108- return url.startswith("http")
109-
110 @property
111 def show_merge_links(self):
112 """Return whether or not merge proposal links should be shown.
113@@ -799,7 +782,7 @@
114 target is not None and target != self.context.target):
115 try:
116 self.context.setTarget(self.user, project=target)
117- except BranchTargetError, e:
118+ except BranchTargetError as e:
119 self.setFieldError('target', e.message)
120 return
121
122
123=== modified file 'lib/lp/code/browser/codeimport.py'
124--- lib/lp/code/browser/codeimport.py 2016-10-06 15:59:34 +0000
125+++ lib/lp/code/browser/codeimport.py 2016-10-14 15:45:46 +0000
126@@ -12,6 +12,7 @@
127 'CodeImportSetBreadcrumb',
128 'CodeImportSetNavigation',
129 'CodeImportSetView',
130+ 'CodeImportTargetMixin',
131 'validate_import_url',
132 ]
133
134@@ -49,6 +50,7 @@
135 from lp.code.enums import (
136 BranchSubscriptionDiffSize,
137 BranchSubscriptionNotificationLevel,
138+ CodeImportResultStatus,
139 CodeImportReviewStatus,
140 CodeReviewNotificationLevel,
141 NON_CVS_RCS_TYPES,
142@@ -191,8 +193,8 @@
143 self.addError(structured("""
144 Those CVS details are already specified for
145 the imported branch <a href="%s">%s</a>.""",
146- canonical_url(code_import.branch),
147- code_import.branch.unique_name))
148+ canonical_url(code_import.target),
149+ code_import.target.unique_name))
150
151 def _validateURL(self, url, rcs_type, existing_import=None,
152 field_name='url'):
153@@ -495,12 +497,12 @@
154 class CodeImportEditView(CodeImportBaseView):
155 """View for editing code imports.
156
157- This view is registered against the branch, but mostly edits the code
158- import for that branch -- the exception being that it also allows the
159- editing of the branch whiteboard. If the branch has no associated code
160- import, then the result is a 404. If the branch does have a code import,
161- then the adapters property allows the form internals to do the associated
162- mappings.
163+ This view is registered against the target, but mostly edits the code
164+ import for that target -- the exception being that it also allows the
165+ editing of the branch whiteboard in the case of Bazaar branches. If the
166+ target has no associated code import, then the result is a 404. If the
167+ target does have a code import, then the adapters property allows the
168+ form internals to do the associated mappings.
169 """
170
171 schema = EditCodeImportForm
172@@ -513,16 +515,20 @@
173
174 @property
175 def initial_values(self):
176- return {'whiteboard': self.context.whiteboard}
177+ if (self.code_import.target_rcs_type ==
178+ TargetRevisionControlSystems.BZR):
179+ return {'whiteboard': self.context.whiteboard}
180+ else:
181+ return {}
182
183 def initialize(self):
184- """Show a 404 if the branch has no code import."""
185+ """Show a 404 if the target has no code import."""
186 self.code_import = self.context.code_import
187 if self.code_import is None:
188 raise NotFoundError
189 if not self._super_user:
190 raise Unauthorized
191- # The next and cancel location is the branch details page.
192+ # The next and cancel location is the target details page.
193 self.cancel_url = self.next_url = canonical_url(self.context)
194 super(CodeImportEditView, self).initialize()
195
196@@ -543,6 +549,10 @@
197 else:
198 raise AssertionError('Unknown rcs_type for code import.')
199
200+ if (self.code_import.target_rcs_type !=
201+ TargetRevisionControlSystems.BZR):
202+ self.form_fields = self.form_fields.omit('whiteboard')
203+
204 def _showButtonForStatus(self, status):
205 """If the status is different, and the user is super, show button."""
206 return self._super_user and self.code_import.review_status != status
207@@ -604,3 +614,32 @@
208 "This foreign branch URL is already specified for the imported "
209 "branch <a href='%s'>%s</a>.", canonical_url(code_import.branch),
210 code_import.branch.unique_name)
211+
212+
213+class CodeImportTargetMixin:
214+ """Common code import methods for Branch and GitRepository views."""
215+
216+ @cachedproperty
217+ def latest_code_import_results(self):
218+ """Return the last 10 CodeImportResults."""
219+ return list(self.context.code_import.results[:10])
220+
221+ def iconForCodeImportResultStatus(self, status):
222+ """The icon to represent the `CodeImportResultStatus` `status`."""
223+ if status == CodeImportResultStatus.SUCCESS_PARTIAL:
224+ return "/@@/yes-gray"
225+ elif status in CodeImportResultStatus.successes:
226+ return "/@@/yes"
227+ else:
228+ return "/@@/no"
229+
230+ @property
231+ def url_is_web(self):
232+ """True if an imported branch's URL is HTTP or HTTPS."""
233+ # You should only be calling this if it's an SVN, BZR or GIT code
234+ # import
235+ assert self.context.code_import
236+ url = self.context.code_import.url
237+ assert url
238+ # https starts with http too!
239+ return url.startswith("http")
240
241=== modified file 'lib/lp/code/browser/configure.zcml'
242--- lib/lp/code/browser/configure.zcml 2016-06-25 08:05:06 +0000
243+++ lib/lp/code/browser/configure.zcml 2016-10-14 15:45:46 +0000
244@@ -361,7 +361,7 @@
245 template="../templates/branch-recipes.pt" />
246 <browser:page
247 name="++branch-import-details"
248- template="../templates/branch-import-details.pt" />
249+ template="../templates/import-details.pt" />
250 <browser:page
251 name="++branch-revisions"
252 template="../templates/branch-revisions.pt" />
253@@ -629,7 +629,7 @@
254 permission="launchpad.AnyPerson"/>
255 <browser:url
256 for="lp.code.interfaces.codeimport.ICodeImport"
257- attribute_to_parent="branch"
258+ attribute_to_parent="target"
259 path_expression="string:+code-import"
260 rootsite="code"/>
261 <browser:navigation
262@@ -815,6 +815,9 @@
263 <browser:page
264 name="++repository-recipes"
265 template="../templates/gitrepository-recipes.pt"/>
266+ <browser:page
267+ name="++repository-import-details"
268+ template="../templates/import-details.pt"/>
269 </browser:pages>
270 <browser:page
271 for="lp.code.interfaces.gitrepository.IGitRepository"
272
273=== modified file 'lib/lp/code/browser/gitrepository.py'
274--- lib/lp/code/browser/gitrepository.py 2016-10-05 10:07:08 +0000
275+++ lib/lp/code/browser/gitrepository.py 2016-10-14 15:45:46 +0000
276@@ -53,6 +53,7 @@
277 from lp.app.vocabularies import InformationTypeVocabulary
278 from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
279 from lp.code.browser.branch import CodeEditOwnerMixin
280+from lp.code.browser.codeimport import CodeImportTargetMixin
281 from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
282 from lp.code.browser.widgets.gitrepositorytarget import (
283 GitRepositoryTargetDisplayWidget,
284@@ -191,6 +192,11 @@
285 return None
286 return GitRepositoryDiffView(self.context, self.request, old, new)
287
288+ @stepto("+code-import")
289+ def traverse_code_import(self):
290+ """Traverses to the `ICodeImport` for the repository."""
291+ return self.context.code_import
292+
293
294 class GitRepositoryEditMenu(NavigationMenu):
295 """Edit menu for `IGitRepository`."""
296@@ -294,7 +300,7 @@
297
298
299 class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
300- HasSnapsViewMixin):
301+ HasSnapsViewMixin, CodeImportTargetMixin):
302
303 related_features = {
304 "code.git.recipes.enabled": False,
305@@ -345,6 +351,18 @@
306 '<a href="+recipes">%s recipes</a> using this repository.',
307 count).escapedtext
308
309+ @property
310+ def is_imported(self):
311+ """Is this an imported repository?"""
312+ return self.context.repository_type == GitRepositoryType.IMPORTED
313+
314+ @property
315+ def can_edit_import(self):
316+ """Can the user edit this import?"""
317+ # XXX cjwatson 2016-10-14: Delete this once we have views for
318+ # editing Git imports.
319+ return False
320+
321
322 class GitRepositoryEditFormView(LaunchpadEditFormView):
323 """Base class for forms that edit a Git repository."""
324
325=== modified file 'lib/lp/code/browser/tests/test_codeimport.py'
326--- lib/lp/code/browser/tests/test_codeimport.py 2014-02-24 07:19:52 +0000
327+++ lib/lp/code/browser/tests/test_codeimport.py 2016-10-14 15:45:46 +0000
328@@ -1,4 +1,4 @@
329-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
330+# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
331 # GNU Affero General Public License version 3 (see the file LICENSE).
332
333 """Tests for the code import browser code."""
334@@ -7,9 +7,14 @@
335
336 import re
337
338+from testtools.matchers import StartsWith
339 from zope.security.interfaces import Unauthorized
340
341-from lp.code.enums import RevisionControlSystems
342+from lp.code.enums import (
343+ RevisionControlSystems,
344+ TargetRevisionControlSystems,
345+ )
346+from lp.code.tests.helpers import GitHostingFixture
347 from lp.services.webapp import canonical_url
348 from lp.testing import (
349 person_logged_in,
350@@ -27,28 +32,43 @@
351
352 layer = DatabaseFunctionalLayer
353
354- def assertSvnDetailsDisplayed(self, svn_details, rcs_type):
355- """Assert the `svn_details` tag describes a Subversion import.
356+ def assertImportDetailsDisplayed(self, code_import, details_id,
357+ prefix_text, span_title=None):
358+ """A code import has its details displayed properly.
359
360- :param svn_details: The BeautifulSoup object for the
361- 'svn-import-details' area.
362- :param rcs_type: SVN or BZR_SVN from RevisionControlSystems.
363+ :param code_import: An `ICodeImport`.
364+ :param details_id: The HTML tag id to search for.
365+ :param prefix_text: An expected prefix of the details text.
366+ :param span_title: If present, the expected contents of a span title
367+ attribute.
368 """
369- self.assertEquals(rcs_type.title, svn_details.span['title'])
370- text = re.sub('\s+', ' ', extract_text(svn_details))
371- self.assertTrue(
372- text.startswith(
373- 'This branch is an import of the Subversion branch'))
374+ browser = self.getUserBrowser(canonical_url(code_import.target))
375+ details = find_tag_by_id(browser.contents, details_id)
376+ if span_title is not None:
377+ self.assertEqual(span_title, details.span['title'])
378+ text = re.sub('\s+', ' ', extract_text(details))
379+ self.assertThat(text, StartsWith(prefix_text))
380
381 def test_bzr_svn_import(self):
382- # The branch page for a bzr-svn-imported branch contains
383+ # The branch page for a bzr-svn-imported branch contains a summary
384+ # of the import details.
385+ code_import = self.factory.makeCodeImport(
386+ rcs_type=RevisionControlSystems.BZR_SVN)
387+ self.assertImportDetailsDisplayed(
388+ code_import, 'svn-import-details',
389+ 'This branch is an import of the Subversion branch',
390+ span_title=RevisionControlSystems.BZR_SVN.title)
391+
392+ def test_git_to_git_import(self):
393+ # The repository page for a git-to-git-imported repository contains
394 # a summary of the import details.
395- bzr_svn_import = self.factory.makeCodeImport(
396- rcs_type=RevisionControlSystems.BZR_SVN)
397- browser = self.getUserBrowser(canonical_url(bzr_svn_import.branch))
398- svn_details = find_tag_by_id(browser.contents, 'svn-import-details')
399- self.assertSvnDetailsDisplayed(
400- svn_details, RevisionControlSystems.BZR_SVN)
401+ self.useFixture(GitHostingFixture())
402+ code_import = self.factory.makeCodeImport(
403+ rcs_type=RevisionControlSystems.GIT,
404+ target_rcs_type=TargetRevisionControlSystems.GIT)
405+ self.assertImportDetailsDisplayed(
406+ code_import, 'git-import-details',
407+ 'This repository is an import of the Git repository')
408
409 def test_branch_owner_of_import_forbidden(self):
410 # Unauthorized users are forbidden to edit an import.
411
412=== modified file 'lib/lp/code/configure.zcml'
413--- lib/lp/code/configure.zcml 2016-10-07 15:50:10 +0000
414+++ lib/lp/code/configure.zcml 2016-10-14 15:45:46 +0000
415@@ -642,6 +642,7 @@
416 series
417 review_status
418 rcs_type
419+ target_rcs_type
420 cvs_root
421 cvs_module
422 url
423
424=== modified file 'lib/lp/code/interfaces/codeimport.py'
425--- lib/lp/code/interfaces/codeimport.py 2016-10-03 17:00:56 +0000
426+++ lib/lp/code/interfaces/codeimport.py 2016-10-14 15:45:46 +0000
427@@ -41,6 +41,7 @@
428 from lp.code.enums import (
429 CodeImportReviewStatus,
430 RevisionControlSystems,
431+ TargetRevisionControlSystems,
432 )
433 from lp.code.interfaces.branch import IBranch
434 from lp.code.interfaces.gitrepository import IGitRepository
435@@ -112,11 +113,16 @@
436 description=_("Only reviewed imports are processed.")))
437
438 rcs_type = exported(
439- Choice(title=_("Type of RCS"), readonly=True,
440+ Choice(
441+ title=_("Type of RCS"), readonly=True,
442 required=True, vocabulary=RevisionControlSystems,
443- description=_(
444- "The version control system to import from. "
445- "Can be CVS or Subversion.")))
446+ description=_("The revision control system to import from.")))
447+
448+ target_rcs_type = exported(
449+ Choice(
450+ title=_("Type of target RCS"), readonly=True,
451+ required=True, vocabulary=TargetRevisionControlSystems,
452+ description=_("The revision control system to import to.")))
453
454 url = exported(
455 URIField(title=_("URL"), required=False, readonly=True,
456
457=== modified file 'lib/lp/code/model/codeimport.py'
458--- lib/lp/code/model/codeimport.py 2016-10-11 14:26:31 +0000
459+++ lib/lp/code/model/codeimport.py 2016-10-14 15:45:46 +0000
460@@ -121,6 +121,13 @@
461 rcs_type = EnumCol(schema=RevisionControlSystems,
462 notNull=False, default=None)
463
464+ @property
465+ def target_rcs_type(self):
466+ if self.branch is not None:
467+ return TargetRevisionControlSystems.BZR
468+ else:
469+ return TargetRevisionControlSystems.GIT
470+
471 cvs_root = StringCol(default=None)
472
473 cvs_module = StringCol(default=None)
474
475=== modified file 'lib/lp/code/templates/branch-index.pt'
476--- lib/lp/code/templates/branch-index.pt 2015-09-17 12:13:31 +0000
477+++ lib/lp/code/templates/branch-index.pt 2016-10-14 15:45:46 +0000
478@@ -113,7 +113,7 @@
479 tal:condition="context/branch_type/enumvalue:IMPORTED">
480 <div class="portlet">
481 <h2>Import details</h2>
482- <tal:branch-pending-merges
483+ <tal:branch-import-details
484 replace="structure context/@@++branch-import-details" />
485 </div>
486 </div>
487
488=== modified file 'lib/lp/code/templates/codeimport-machine-index.pt'
489--- lib/lp/code/templates/codeimport-machine-index.pt 2009-08-18 00:25:14 +0000
490+++ lib/lp/code/templates/codeimport-machine-index.pt 2016-10-14 15:45:46 +0000
491@@ -42,7 +42,7 @@
492 <table class="simple">
493 <tal:jobs repeat="job jobs">
494 <tr>
495- <td><tal:branch replace="structure job/code_import/branch/fmt:link"/></td>
496+ <td><tal:target replace="structure job/code_import/target/fmt:link"/></td>
497 <td>Started: <tal:started replace="job/date_started/fmt:approximatedate"/></td>
498 <td>Last heartbeat: <tal:heartbeat replace="job/heartbeat/fmt:approximatedate"/></td>
499 </tr>
500@@ -71,7 +71,7 @@
501 </tal:has-person>
502 <tal:event replace="event/event_type/title" />
503 <tal:code-import condition="event/code_import"
504- replace="structure event/code_import/branch/fmt:link"/>
505+ replace="structure event/code_import/target/fmt:link"/>
506 </dt>
507 <dd tal:condition="event/items">
508 <tal:items repeat="item event/items">
509
510=== modified file 'lib/lp/code/templates/codeimport-machines.pt'
511--- lib/lp/code/templates/codeimport-machines.pt 2009-08-18 00:22:50 +0000
512+++ lib/lp/code/templates/codeimport-machines.pt 2016-10-14 15:45:46 +0000
513@@ -40,9 +40,9 @@
514 <table class="simple">
515 <tal:jobs repeat="job jobs">
516 <tr>
517- <td><tal:branch replace="structure job/code_import/branch/fmt:link"/></td>
518- <td>Started: <tal:branch replace="job/date_started/fmt:approximatedate"/></td>
519- <td>Last heartbeat: <tal:branch replace="job/heartbeat/fmt:approximatedate"/></td>
520+ <td><tal:target replace="structure job/code_import/target/fmt:link"/></td>
521+ <td>Started: <tal:started replace="job/date_started/fmt:approximatedate"/></td>
522+ <td>Last heartbeat: <tal:heartbeat replace="job/heartbeat/fmt:approximatedate"/></td>
523 </tr>
524 </tal:jobs>
525 </table>
526
527=== modified file 'lib/lp/code/templates/gitrepository-index.pt'
528--- lib/lp/code/templates/gitrepository-index.pt 2016-01-12 15:10:57 +0000
529+++ lib/lp/code/templates/gitrepository-index.pt 2016-10-14 15:45:46 +0000
530@@ -54,6 +54,16 @@
531 </div>
532 </div>
533
534+ <div id="repository-import-details"
535+ class="yui-g"
536+ tal:condition="context/repository_type/enumvalue:IMPORTED">
537+ <div class="portlet">
538+ <h2>Import details</h2>
539+ <tal:repository-import-details
540+ replace="structure context/@@++repository-import-details" />
541+ </div>
542+ </div>
543+
544 <div class="yui-g">
545 <div id="repository-branches" class="portlet"
546 tal:define="branches view/branches">
547
548=== renamed file 'lib/lp/code/templates/branch-import-details.pt' => 'lib/lp/code/templates/import-details.pt'
549--- lib/lp/code/templates/branch-import-details.pt 2014-02-24 07:19:52 +0000
550+++ lib/lp/code/templates/import-details.pt 2016-10-14 15:45:46 +0000
551@@ -4,17 +4,19 @@
552 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
553 tal:define="context_menu view/context/menu:context">
554
555- <tal:imported-branch tal:condition="context/branch_type/enumvalue:IMPORTED">
556- <div id="import-details" tal:define="branch context;
557- code_import branch/code_import">
558- <tal:has-codeimport condition="branch/code_import"
559- define="code_import branch/code_import">
560+ <tal:imported tal:condition="view/is_imported">
561+ <div id="import-details"
562+ tal:define="branch_or_repository context;
563+ code_import branch_or_repository/code_import">
564+ <tal:has-codeimport
565+ condition="branch_or_repository/code_import"
566+ define="code_import branch_or_repository/code_import">
567
568 <div><strong>Import Status:</strong>
569 <span tal:attributes="class string:codeimport${code_import/review_status/name}"
570 tal:content="code_import/review_status/title"/>
571 <form tal:attributes="action string:${context/fmt:url}/@@+try-again"
572- tal:condition="python:view.user and code_import.review_status.name == 'FAILING'"
573+ tal:condition="python:view.can_edit_import and view.user and code_import.review_status.name == 'FAILING'"
574 style="display: inline; padding-left: 1em"
575 id="tryagain"
576 name="tryagain"
577@@ -39,7 +41,16 @@
578 </div>
579
580 <tal:git-import condition="code_import/rcs_type/enumvalue:GIT">
581- <p>This branch is an import of the HEAD branch of the Git repository at
582+ <p id="git-import-details">
583+ <tal:git-to-bzr-import
584+ condition="code_import/target_rcs_type/enumvalue:BZR">
585+ This branch is an import of the HEAD branch
586+ </tal:git-to-bzr-import>
587+ <tal:git-to-git-import
588+ condition="code_import/target_rcs_type/enumvalue:GIT">
589+ This repository is an import
590+ </tal:git-to-git-import>
591+ of the Git repository at
592 <tal:is-web-url condition="view/url_is_web">
593 <a tal:attributes="href code_import/url"
594 tal:content="code_import/url" />.
595@@ -117,7 +128,7 @@
596 in 2 hours
597 </tal:date-started>.
598 <tal:button
599- condition="view/user"
600+ condition="python:view.can_edit_import and view.user"
601 replace="structure view/context/@@+request-import" />
602 </tal:not-overdue>
603 </tal:not-running>
604@@ -147,7 +158,7 @@
605 </tal:result>
606 </div>
607
608- <div class="actions">
609+ <div class="actions" tal:condition="view/can_edit_import">
610 <div
611 tal:define="link context_menu/edit_import"
612 tal:condition="link/enabled"
613@@ -160,6 +171,6 @@
614 </div>
615 </tal:has-codeimport>
616 </div>
617- </tal:imported-branch>
618+ </tal:imported>
619
620 </div>