~launchpad-pqm/launchpad/devel

12293.1.11 by Curtis Hovey
Updated copyright.
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
8687.15.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4236.3.1 by Michael Hudson
create a page at code.launchpad.dev/code-imports
3
4236.3.54 by Michael Hudson
respond to review comments
4
"""Browser views for CodeImports."""
4236.3.1 by Michael Hudson
create a page at code.launchpad.dev/code-imports
5
6
__metaclass__ = type
7
4236.3.11 by Michael Hudson
add a very simple page view for ICodeImport
8
__all__ = [
5895.4.1 by Tim Penhey
Initial methods and email notification.
9
    'CodeImportEditView',
5763.6.1 by Tim Penhey
Machine view
10
    'CodeImportMachineView',
5763.4.12 by Tim Penhey
New pagetest to show creation.
11
    'CodeImportNewView',
9209.4.5 by Guilherme Salgado
Update all existing breadcrumb adapters to use Breadcrumb rather than BreadcrumbBuilder. Also rename them all
12
    'CodeImportSetBreadcrumb',
6233.5.2 by Tim Penhey
Get the page working.
13
    'CodeImportSetNavigation',
4236.3.11 by Michael Hudson
add a very simple page view for ICodeImport
14
    'CodeImportSetView',
4236.3.46 by Michael Hudson
improvements to tests, and removal of a strange hack
15
    'CodeImportView',
4236.3.11 by Michael Hudson
add a very simple page view for ICodeImport
16
    ]
4236.3.1 by Michael Hudson
create a page at code.launchpad.dev/code-imports
17
4236.3.4 by Michael Hudson
beef up test to create a code import, and check that it affects the code import
18
5763.4.2 by Tim Penhey
Initial work.
19
from BeautifulSoup import BeautifulSoup
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
20
from lazr.restful.interface import (
21
    copy_field,
22
    use_template,
23
    )
5763.4.2 by Tim Penhey
Initial work.
24
from zope.app.form import CustomWidgetFactory
25
from zope.app.form.interfaces import IInputWidget
26
from zope.app.form.utility import setUpWidget
27
from zope.component import getUtility
28
from zope.formlib import form
6650.1.2 by Michael Hudson
less hacking, no more working
29
from zope.interface import Interface
10454.5.2 by James Westby
Import Choice.
30
from zope.schema import Choice
5763.4.2 by Tim Penhey
Initial work.
31
4236.3.48 by Michael Hudson
after what felt like heroic Z3 hacking, a fairly small amount of code to allow
32
from canonical.launchpad import _
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
33
from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
34
from canonical.launchpad.webapp import (
35
    canonical_url,
36
    LaunchpadView,
37
    Navigation,
38
    stepto,
39
    )
40
from canonical.launchpad.webapp.batching import BatchNavigator
41
from canonical.launchpad.webapp.breadcrumb import Breadcrumb
42
from canonical.launchpad.webapp.menu import structured
12293.1.10 by Curtis Hovey
Formatted imports.
43
from lp.app.browser.launchpadform import (
44
    action,
45
    custom_widget,
46
    LaunchpadFormView,
47
    )
48
from lp.app.errors import NotFoundError
12293.1.9 by Curtis Hovey
Deglobbed widget imports.
49
from lp.app.widgets.itemswidgets import (
50
    LaunchpadDropdownWidget,
51
    LaunchpadRadioWidget,
52
    )
12293.1.3 by Curtis Hovey
Migrated all the canonical.widgets to app.widgets.
53
from lp.app.widgets.textwidgets import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
54
    StrippedTextWidget,
55
    URIWidget,
56
    )
8555.2.5 by Tim Penhey
Move the branch subscription enums.
57
from lp.code.enums import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
58
    BranchSubscriptionDiffSize,
59
    BranchSubscriptionNotificationLevel,
60
    CodeImportReviewStatus,
61
    CodeReviewNotificationLevel,
62
    RevisionControlSystems,
63
    )
11270.2.5 by Tim Penhey
Last import change.
64
from lp.code.errors import BranchExists
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
65
from lp.code.interfaces.branch import (
66
    IBranch,
67
    user_has_special_branch_access,
68
    )
8615.1.1 by Tim Penhey
Fix the oops when trying to import a branch for a private project.
69
from lp.code.interfaces.branchnamespace import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
70
    get_branch_namespace,
71
    IBranchNamespacePolicy,
72
    )
73
from lp.code.interfaces.branchtarget import IBranchTarget
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
74
from lp.code.interfaces.codeimport import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
75
    ICodeImport,
76
    ICodeImportSet,
77
    )
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
78
from lp.code.interfaces.codeimportmachine import ICodeImportMachineSet
10155.4.1 by Tim Penhey
Provide the new code import view for products directly so the user doesn't need to specify it.
79
from lp.registry.interfaces.product import IProduct
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
80
from lp.services.fields import URIField
11382.6.34 by Gavin Panella
Reformat imports in all files touched so far.
81
from lp.services.propertycache import cachedproperty
4236.3.49 by Michael Hudson
whitespace, ensure that the code imports are listed in id order, test cleanups
82
5763.4.20 by Tim Penhey
Last updates from review.
83
6233.5.2 by Tim Penhey
Get the page working.
84
class CodeImportSetNavigation(Navigation):
85
    """Navigation methods for IBuilder."""
86
    usedfor = ICodeImportSet
87
88
    @stepto('+machines')
89
    def bugs(self):
90
        return getUtility(ICodeImportMachineSet)
91
6767.6.17 by Maris Fogels
Migrated the CodeImport breadcrumb to the new system interface.
92
9209.4.5 by Guilherme Salgado
Update all existing breadcrumb adapters to use Breadcrumb rather than BreadcrumbBuilder. Also rename them all
93
class CodeImportSetBreadcrumb(Breadcrumb):
6767.6.26 by Maris Fogels
Rework based on reviewer feedback.
94
    """Builds a breadcrumb for an `ICodeImportSet`."""
6767.6.17 by Maris Fogels
Migrated the CodeImport breadcrumb to the new system interface.
95
    text = u'Code Import System'
6233.5.2 by Tim Penhey
Get the page working.
96
97
10427.14.1 by Michael Hudson
browser code changes
98
class DropdownWidgetWithAny(LaunchpadDropdownWidget):
4236.3.54 by Michael Hudson
respond to review comments
99
    """A <select> widget with a more appropriate 'no value' message.
100
101
    By default `LaunchpadDropdownWidget` displays 'no value' when the
102
    associated value is None or not supplied, which is not what we want on
103
    this page.
104
    """
4236.3.51 by Michael Hudson
consistency-with-other-code changes
105
    _messageNoValue = _('Any')
106
107
4236.3.1 by Michael Hudson
create a page at code.launchpad.dev/code-imports
108
class CodeImportSetView(LaunchpadView):
4236.3.54 by Michael Hudson
respond to review comments
109
    """The default view for `ICodeImportSet`.
110
111
    We present the CodeImportSet as a list of all imports.
112
    """
113
4236.3.29 by Michael Hudson
make the codeimportsetview batched
114
    def initialize(self):
4236.3.54 by Michael Hudson
respond to review comments
115
        """See `LaunchpadView.initialize`."""
10427.14.6 by Michael Hudson
address review comments
116
        review_status_field = copy_field(
117
            ICodeImport['review_status'], required=False, default=None)
118
        self.review_status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
119
        setUpWidget(self, 'review_status',  review_status_field, IInputWidget)
4236.3.48 by Michael Hudson
after what felt like heroic Z3 hacking, a fairly small amount of code to allow
120
10427.14.6 by Michael Hudson
address review comments
121
        rcs_type_field = copy_field(
122
            ICodeImport['rcs_type'], required=False, default=None)
123
        self.rcs_type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
124
        setUpWidget(self, 'rcs_type',  rcs_type_field, IInputWidget)
10427.14.1 by Michael Hudson
browser code changes
125
4236.3.56 by Michael Hudson
preparations for merging
126
        # status should be None if either (a) there were no query arguments
127
        # supplied, i.e. the user browsed directly to this page (this is when
128
        # hasValidInput returns False) or (b) the user chose 'Any' in the
129
        # status widget (this is when hasValidInput returns True but
130
        # getInputValue returns None).
10427.14.6 by Michael Hudson
address review comments
131
        review_status = None
132
        if self.review_status_widget.hasValidInput():
133
            review_status = self.review_status_widget.getInputValue()
10427.14.1 by Michael Hudson
browser code changes
134
        # Similar for 'type'
135
        rcs_type = None
10427.14.6 by Michael Hudson
address review comments
136
        if self.rcs_type_widget.hasValidInput():
137
            rcs_type = self.rcs_type_widget.getInputValue()
4236.3.48 by Michael Hudson
after what felt like heroic Z3 hacking, a fairly small amount of code to allow
138
10427.14.6 by Michael Hudson
address review comments
139
        imports = self.context.search(
140
            review_status=review_status, rcs_type=rcs_type)
4236.3.48 by Michael Hudson
after what felt like heroic Z3 hacking, a fairly small amount of code to allow
141
4236.3.53 by Michael Hudson
so it turns out that the default batch size is 5 (ridiculously small) only for
142
        self.batchnav = BatchNavigator(imports, self.request)
4236.3.46 by Michael Hudson
improvements to tests, and removal of a strange hack
143
4236.3.49 by Michael Hudson
whitespace, ensure that the code imports are listed in id order, test cleanups
144
4236.3.46 by Michael Hudson
improvements to tests, and removal of a strange hack
145
class CodeImportView(LaunchpadView):
4236.3.54 by Michael Hudson
respond to review comments
146
    """The default view for `ICodeImport`.
147
148
    We present the CodeImport as a simple page listing all the details of the
10454.4.9 by James Westby
Use BranchTarget in a couple of places when requesting an import.
149
    import such as target and branch, who requested the import,
4236.3.54 by Michael Hudson
respond to review comments
150
    and so on.
151
    """
152
4236.3.46 by Michael Hudson
improvements to tests, and removal of a strange hack
153
    def initialize(self):
4236.3.54 by Michael Hudson
respond to review comments
154
        """See `LaunchpadView.initialize`."""
10454.4.15 by James Westby
Fix another instance of accessing code_import.target, rather than branch.target.
155
        self.title = "Code Import for %s" % (self.context.branch.target.name,)
5763.4.1 by Tim Penhey
Initial work
156
5763.4.2 by Tim Penhey
Initial work.
157
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
158
class CodeImportBaseView(LaunchpadFormView):
159
    """A base view for both new and edit code import views."""
160
161
    schema = ICodeImport
162
163
    custom_widget('cvs_root', StrippedTextWidget, displayWidth=50)
164
    custom_widget('cvs_module', StrippedTextWidget, displayWidth=20)
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
165
    custom_widget('url', URIWidget, displayWidth=50)
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
166
167
    @cachedproperty
168
    def _super_user(self):
169
        """Is the user an admin or member of vcs-imports?"""
170
        celebs = getUtility(ILaunchpadCelebrities)
171
        return (self.user.inTeam(celebs.admin) or
172
                self.user.inTeam(celebs.vcs_imports))
173
174
    def showOptionalMarker(self, field_name):
175
        """Don't show the optional marker for rcs locations."""
5763.5.11 by Tim Penhey
OMG, the test passes, quick, commit!
176
        # No field in either the new or edit view needs an optional marker,
177
        # so we can be simple here.
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
178
        return False
179
180
    def setSecondaryFieldError(self, field, error):
181
        """Set the field error only if there isn't an error already."""
182
        if self.getFieldError(field):
183
            # Leave this one as it is often required or a validator error.
184
            pass
185
        else:
186
            self.setFieldError(field, error)
187
5763.5.9 by Tim Penhey
Fix validation.
188
    def _validateCVS(self, cvs_root, cvs_module, existing_import=None):
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
189
        """If the user has specified cvs, then we need to make
190
        sure that there isn't already an import with those values."""
191
        if cvs_root is None:
192
            self.setSecondaryFieldError(
193
                'cvs_root', 'Enter a CVS root.')
194
        if cvs_module is None:
195
            self.setSecondaryFieldError(
196
                'cvs_module', 'Enter a CVS module.')
197
198
        if cvs_root and cvs_module:
199
            code_import = getUtility(ICodeImportSet).getByCVSDetails(
200
                cvs_root, cvs_module)
5763.5.9 by Tim Penhey
Fix validation.
201
            if (code_import is not None and
202
                code_import != existing_import):
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
203
                self.addError(structured("""
204
                    Those CVS details are already specified for
205
                    the imported branch <a href="%s">%s</a>.""",
206
                    canonical_url(code_import.branch),
207
                    code_import.branch.unique_name))
208
10129.6.9 by Tim Penhey
Yet more test fixes.
209
    def _validateURL(self, url, existing_import=None, field_name='url'):
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
210
        """If the user has specified a url, we need to make sure that there
211
        isn't already an import with that url."""
212
        if url is None:
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
213
            self.setSecondaryFieldError(
10129.6.9 by Tim Penhey
Yet more test fixes.
214
                field_name, 'Enter the URL of a foreign VCS branch.')
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
215
        else:
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
216
            code_import = getUtility(ICodeImportSet).getByURL(url)
5763.5.9 by Tim Penhey
Fix validation.
217
            if (code_import is not None and
218
                code_import != existing_import):
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
219
                self.setFieldError(
10129.6.9 by Tim Penhey
Yet more test fixes.
220
                    field_name,
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
221
                    structured("""
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
222
                    This foreign branch URL is already specified for
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
223
                    the imported branch <a href="%s">%s</a>.""",
224
                    canonical_url(code_import.branch),
225
                    code_import.branch.unique_name))
226
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
227
228
229
class NewCodeImportForm(Interface):
230
    """The fields presented on the form for editing a code import."""
231
10762.1.1 by Tim Penhey
Allow the users to specify the owner for a new import.
232
    use_template(IBranch, ['owner'])
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
233
    use_template(
234
        ICodeImport,
10454.4.3 by James Westby
Hack product out of NewCodeImportForm.
235
        ['rcs_type', 'cvs_root', 'cvs_module'])
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
236
237
    svn_branch_url = URIField(
238
        title=_("Branch URL"), required=False,
239
        description=_(
10635.5.4 by Michael Hudson
thumper approved wording
240
            "The URL of a Subversion branch, starting with svn:// or "
241
            "http(s)://.   You can include a username and password as part "
242
            "of the url, but this will be displayed on the branch page."),
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
243
        allowed_schemes=["http", "https", "svn"],
10635.5.2 by Michael Hudson
starting steps
244
        allow_userinfo=True,
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
245
        allow_port=True,
246
        allow_query=False,
247
        allow_fragment=False,
248
        trailing_slash=False)
249
250
    git_repo_url = URIField(
251
        title=_("Repo URL"), required=False,
252
        description=_(
10129.6.21 by Tim Penhey
Correct the wording on the git and hg import strings.
253
            "The URL of the git repository.  The HEAD branch will be "
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
254
            "imported."),
10427.9.1 by Michael Hudson
allow http and https for git imports
255
        allowed_schemes=["git", "http", "https"],
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
256
        allow_userinfo=False, # Only anonymous access is supported.
257
        allow_port=True,
10129.7.1 by Tim Penhey
Minor tweaks mentioned in review.
258
        allow_query=False,
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
259
        allow_fragment=False,
260
        trailing_slash=False)
261
10129.6.13 by Tim Penhey
Merge jelmer's hg-import branch and resolve conflicts.
262
    hg_repo_url = URIField(
263
        title=_("Repo URL"), required=False,
10129.6.21 by Tim Penhey
Correct the wording on the git and hg import strings.
264
        description=_(
265
            "The URL of the Mercurial repository.  The tip branch will be "
266
            "imported."),
10129.6.13 by Tim Penhey
Merge jelmer's hg-import branch and resolve conflicts.
267
        allowed_schemes=["http", "https"],
268
        allow_userinfo=False, # Only anonymous access is supported.
269
        allow_port=True,
270
        allow_query=False,    # Query makes no sense in Mercurial
271
        allow_fragment=False, # Fragment makes no sense in Mercurial
272
        trailing_slash=False) # See http://launchpad.net/bugs/56357.
273
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
274
    branch_name = copy_field(
275
        IBranch['name'],
10129.6.9 by Tim Penhey
Yet more test fixes.
276
        __name__='branch_name',
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
277
        title=_('Branch Name'),
278
        description=_(
279
            "This will be used in the branch URL to identify the "
280
            "imported branch.  Examples: main, trunk."),
281
        )
9905.7.6 by Jelmer Vernooij
More tests.
282
10454.4.10 by James Westby
Define product on the new codeimport form again.
283
    product = Choice(
10454.4.14 by James Westby
Products are called projects in the UI.
284
        title=_('Project'),
285
        description=_("The Project to associate the code import with."),
10454.4.10 by James Westby
Define product on the new codeimport form again.
286
        vocabulary="Product",
287
        )
288
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
289
290
class CodeImportNewView(CodeImportBaseView):
5763.4.1 by Tim Penhey
Initial work
291
    """The view to request a new code import."""
292
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
293
    schema = NewCodeImportForm
5763.4.2 by Tim Penhey
Initial work.
294
    for_input = True
5763.4.1 by Tim Penhey
Initial work
295
5763.4.2 by Tim Penhey
Initial work.
296
    custom_widget('rcs_type', LaunchpadRadioWidget)
297
10762.1.1 by Tim Penhey
Allow the users to specify the owner for a new import.
298
    @property
299
    def initial_values(self):
300
        return {
301
            'owner': self.user,
302
            'rcs_type': RevisionControlSystems.BZR_SVN,
303
            'branch_name': 'trunk',
304
            }
5763.4.2 by Tim Penhey
Initial work.
305
5763.4.16 by Tim Penhey
Updates following review.
306
    @property
10155.4.1 by Tim Penhey
Provide the new code import view for products directly so the user doesn't need to specify it.
307
    def context_is_product(self):
308
        return IProduct.providedBy(self.context)
309
310
    @property
311
    def label(self):
312
        if self.context_is_product:
313
            return 'Request a code import for %s' % self.context.displayname
314
        else:
315
            return 'Request a code import'
316
317
    @property
5763.4.16 by Tim Penhey
Updates following review.
318
    def cancel_url(self):
319
        """Cancel should take the user back to the root site."""
320
        return '/'
321
5763.4.2 by Tim Penhey
Initial work.
322
    def setUpFields(self):
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
323
        CodeImportBaseView.setUpFields(self)
10155.4.1 by Tim Penhey
Provide the new code import view for products directly so the user doesn't need to specify it.
324
        if self.context_is_product:
325
            self.form_fields = self.form_fields.omit('product')
326
10762.1.1 by Tim Penhey
Allow the users to specify the owner for a new import.
327
        # If the user can administer branches, then they should be able to
328
        # assign the ownership of the branch to any valid person or team.
329
        if user_has_special_branch_access(self.user):
330
            owner_field = self.schema['owner']
331
            any_owner_choice = Choice(
332
                __name__='owner', title=owner_field.title,
333
                description = _("As an administrator you are able to reassign"
334
                                " this branch to any person or team."),
335
                required=True, vocabulary='ValidPersonOrTeam')
336
            any_owner_field = form.Fields(
337
                any_owner_choice, render_context=self.render_context)
338
            # Replace the normal owner field with a more permissive vocab.
339
            self.form_fields = self.form_fields.omit('owner')
340
            self.form_fields = any_owner_field + self.form_fields
341
5763.4.2 by Tim Penhey
Initial work.
342
    def setUpWidgets(self):
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
343
        CodeImportBaseView.setUpWidgets(self)
5763.4.2 by Tim Penhey
Initial work.
344
5763.4.16 by Tim Penhey
Updates following review.
345
        # Extract the radio buttons from the rcs_type widget, so we can
5763.4.2 by Tim Penhey
Initial work.
346
        # display them separately in the form.
347
        soup = BeautifulSoup(self.widgets['rcs_type']())
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
348
        fields = soup.findAll('input')
9905.7.6 by Jelmer Vernooij
More tests.
349
        [cvs_button, svn_button, git_button, hg_button, empty_marker] = [
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
350
            field for field in fields
9905.7.13 by Jelmer Vernooij
Merge devel.
351
            if field.get('value') in ['CVS', 'BZR_SVN', 'GIT', 'HG', '1']]
5763.4.2 by Tim Penhey
Initial work.
352
        cvs_button['onclick'] = 'updateWidgets()'
353
        svn_button['onclick'] = 'updateWidgets()'
8245.3.2 by Paul Hummer
Fixed the javascript for new imports
354
        git_button['onclick'] = 'updateWidgets()'
9905.7.6 by Jelmer Vernooij
More tests.
355
        hg_button['onclick'] = 'updateWidgets()'
5763.4.16 by Tim Penhey
Updates following review.
356
        # The following attributes are used only in the page template.
5763.4.2 by Tim Penhey
Initial work.
357
        self.rcs_type_cvs = str(cvs_button)
358
        self.rcs_type_svn = str(svn_button)
8245.3.1 by Paul Hummer
Added git fields to the new import page
359
        self.rcs_type_git = str(git_button)
9905.7.6 by Jelmer Vernooij
More tests.
360
        self.rcs_type_hg = str(hg_button)
5763.4.2 by Tim Penhey
Initial work.
361
        self.rcs_type_emptymarker = str(empty_marker)
362
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
363
    def _getImportLocation(self, data):
364
        """Return the import location based on type."""
365
        rcs_type = data['rcs_type']
366
        if rcs_type == RevisionControlSystems.CVS:
367
            return data.get('cvs_root'), data.get('cvs_module'), None
368
        elif rcs_type == RevisionControlSystems.BZR_SVN:
369
            return None, None, data.get('svn_branch_url')
370
        elif rcs_type == RevisionControlSystems.GIT:
371
            return None, None, data.get('git_repo_url')
10129.6.13 by Tim Penhey
Merge jelmer's hg-import branch and resolve conflicts.
372
        elif rcs_type == RevisionControlSystems.HG:
373
            return None, None, data.get('hg_repo_url')
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
374
        else:
375
            raise AssertionError(
376
                'Unexpected revision control type %r.' % rcs_type)
377
5763.4.16 by Tim Penhey
Updates following review.
378
    def _create_import(self, data, status):
379
        """Create the code import."""
10155.4.1 by Tim Penhey
Provide the new code import view for products directly so the user doesn't need to specify it.
380
        product = self.getProduct(data)
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
381
        cvs_root, cvs_module, url = self._getImportLocation(data)
5763.4.16 by Tim Penhey
Updates following review.
382
        return getUtility(ICodeImportSet).new(
5763.4.2 by Tim Penhey
Initial work.
383
            registrant=self.user,
10762.1.1 by Tim Penhey
Allow the users to specify the owner for a new import.
384
            owner=data['owner'],
10454.4.16 by James Westby
Make ICodeImportSet.new act on IBranchTargets.
385
            target=IBranchTarget(product),
5763.4.17 by Tim Penhey
Merge RF.
386
            branch_name=data['branch_name'],
5763.4.2 by Tim Penhey
Initial work.
387
            rcs_type=data['rcs_type'],
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
388
            url=url,
389
            cvs_root=cvs_root,
390
            cvs_module=cvs_module,
391
            review_status=status)
5763.4.16 by Tim Penhey
Updates following review.
392
7362.3.3 by Jonathan Lange
Use the richer errors from BranchSet.new to remove look-before-you-leap
393
    def _setBranchExists(self, existing_branch):
394
        """Set a field error indicating that the branch already exists."""
395
        self.setFieldError(
396
           'branch_name',
397
            structured("""
398
            There is already an existing import for
399
            <a href="%(product_url)s">%(product_name)s</a>
400
            with the name of
401
            <a href="%(branch_url)s">%(branch_name)s</a>.""",
10454.4.9 by James Westby
Use BranchTarget in a couple of places when requesting an import.
402
                       product_url=canonical_url(existing_branch.target),
403
                       product_name=existing_branch.target.name,
7362.3.3 by Jonathan Lange
Use the richer errors from BranchSet.new to remove look-before-you-leap
404
                       branch_url=canonical_url(existing_branch),
405
                       branch_name=existing_branch.name))
406
5763.4.20 by Tim Penhey
Last updates from review.
407
    @action(_('Request Import'), name='request_import')
408
    def request_import_action(self, action, data):
5763.4.16 by Tim Penhey
Updates following review.
409
        """Create the code_import, and subscribe the user to the branch."""
7362.3.3 by Jonathan Lange
Use the richer errors from BranchSet.new to remove look-before-you-leap
410
        try:
411
            code_import = self._create_import(data, None)
412
        except BranchExists, e:
413
            self._setBranchExists(e.existing_branch)
414
            return
5763.4.16 by Tim Penhey
Updates following review.
415
416
        # Subscribe the user.
5763.4.8 by Tim Penhey
Subscribe the user to the newly created imported branch.
417
        code_import.branch.subscribe(
418
            self.user,
419
            BranchSubscriptionNotificationLevel.FULL,
5608.5.40 by Aaron Bentley
Fix failing tests
420
            BranchSubscriptionDiffSize.NODIFF,
7675.708.3 by Tim Penhey
Add subscribed_by and unsubscribed_by to the subscribe and unsubscribe branch methods.
421
            CodeReviewNotificationLevel.NOEMAIL,
422
            self.user)
5763.4.8 by Tim Penhey
Subscribe the user to the newly created imported branch.
423
5763.4.4 by Tim Penhey
Make the CodeImport stuff a little more permissive.
424
        self.next_url = canonical_url(code_import.branch)
5763.4.1 by Tim Penhey
Initial work
425
5763.4.16 by Tim Penhey
Updates following review.
426
        self.request.response.addNotification("""
5763.4.20 by Tim Penhey
Last updates from review.
427
            New code import created. The code import operators
5763.4.16 by Tim Penhey
Updates following review.
428
            have been notified and the request will be reviewed shortly.""")
429
430
    def _showApprove(self, ignored):
431
        """Is the user an admin or member of vcs-imports?"""
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
432
        return self._super_user
5763.4.16 by Tim Penhey
Updates following review.
433
434
    @action(_('Create Approved Import'), name='approve',
435
            condition=_showApprove)
436
    def approve_action(self, action, data):
437
        """Create the code_import, and subscribe the user to the branch."""
7362.3.3 by Jonathan Lange
Use the richer errors from BranchSet.new to remove look-before-you-leap
438
        try:
439
            code_import = self._create_import(
440
                data, CodeImportReviewStatus.REVIEWED)
441
        except BranchExists, e:
442
            self._setBranchExists(e.existing_branch)
443
            return
5763.4.16 by Tim Penhey
Updates following review.
444
445
        # Don't subscribe the requester as they are an import operator.
446
        self.next_url = canonical_url(code_import.branch)
447
448
        self.request.response.addNotification(
449
            "New reviewed code import created.")
450
10155.4.1 by Tim Penhey
Provide the new code import view for products directly so the user doesn't need to specify it.
451
    def getProduct(self, data):
452
        """If the context is a product, use that, otherwise get from data."""
453
        if self.context_is_product:
454
            return self.context
455
        else:
456
            return data.get('product')
457
5763.4.1 by Tim Penhey
Initial work
458
    def validate(self, data):
5763.4.16 by Tim Penhey
Updates following review.
459
        """See `LaunchpadFormView`."""
8615.1.1 by Tim Penhey
Fix the oops when trying to import a branch for a private project.
460
        # Make sure that the user is able to create branches for the specified
461
        # namespace.
10155.4.1 by Tim Penhey
Provide the new code import view for products directly so the user doesn't need to specify it.
462
        product = self.getProduct(data)
10762.1.1 by Tim Penhey
Allow the users to specify the owner for a new import.
463
        # 'owner' in data may be None if it failed validation.
464
        owner = data.get('owner')
465
        if product is not None and owner is not None:
466
            namespace = get_branch_namespace(owner, product)
8615.1.1 by Tim Penhey
Fix the oops when trying to import a branch for a private project.
467
            policy = IBranchNamespacePolicy(namespace)
10762.1.1 by Tim Penhey
Allow the users to specify the owner for a new import.
468
            if not policy.canCreateBranches(self.user):
8615.1.1 by Tim Penhey
Fix the oops when trying to import a branch for a private project.
469
                self.setFieldError(
470
                    'product',
471
                    "You are not allowed to register imports for %s."
472
                    % product.displayname)
473
5763.4.2 by Tim Penhey
Initial work.
474
        rcs_type = data['rcs_type']
475
        # Make sure fields for unselected revision control systems
476
        # are blanked out:
477
        if rcs_type == RevisionControlSystems.CVS:
478
            self._validateCVS(data.get('cvs_root'), data.get('cvs_module'))
9946.1.13 by Michael Hudson
more tests, create bzr-svn imports by default
479
        elif rcs_type == RevisionControlSystems.BZR_SVN:
10129.6.9 by Tim Penhey
Yet more test fixes.
480
            self._validateURL(
481
                data.get('svn_branch_url'), field_name='svn_branch_url')
8245.3.3 by Paul Hummer
Completed full registration of git import creation (minus the database constraint issue.
482
        elif rcs_type == RevisionControlSystems.GIT:
10129.6.9 by Tim Penhey
Yet more test fixes.
483
            self._validateURL(
484
                data.get('git_repo_url'), field_name='git_repo_url')
9905.7.6 by Jelmer Vernooij
More tests.
485
        elif rcs_type == RevisionControlSystems.HG:
10129.6.13 by Tim Penhey
Merge jelmer's hg-import branch and resolve conflicts.
486
            self._validateURL(
487
                data.get('hg_repo_url'), field_name='hg_repo_url')
5763.4.2 by Tim Penhey
Initial work.
488
        else:
9946.1.13 by Michael Hudson
more tests, create bzr-svn imports by default
489
            raise AssertionError(
490
                'Unexpected revision control type %r.' % rcs_type)
5763.4.12 by Tim Penhey
New pagetest to show creation.
491
5895.4.3 by Tim Penhey
Added edit view.
492
6650.1.2 by Michael Hudson
less hacking, no more working
493
class EditCodeImportForm(Interface):
494
    """The fields presented on the form for editing a code import."""
495
10454.3.4 by James Westby
Fix other tests from fallout.
496
    url = copy_field(ICodeImport['url'], readonly=False)
497
    cvs_root = copy_field(ICodeImport['cvs_root'], readonly=False)
498
    cvs_module = copy_field(ICodeImport['cvs_module'], readonly=False)
6650.1.1 by Michael Hudson
some hacking
499
    whiteboard = copy_field(IBranch['whiteboard'])
500
501
6650.1.13 by Michael Hudson
some kind of progress
502
def _makeEditAction(label, status, text):
503
    """Make an Action to call a particular code import method.
504
505
    :param label: The label for the action, which will end up as the
506
         button title.
507
    :param status: If the code import has this as its review_status, don't
508
        show the button (always show the button if it is None).
509
    :param text: The text to go after 'The code import has been' in a
510
        notifcation, if a change was made.
511
    """
512
    if status is not None:
513
        def condition(self, ignored):
514
            return self._showButtonForStatus(status)
515
    else:
516
        condition = None
517
    def success(self, action, data):
518
        """Make the requested status change."""
519
        if status is not None:
520
            data['review_status'] = status
521
        event = self.code_import.updateFromData(data, self.user)
522
        if event is not None:
523
            self.request.response.addNotification(
524
                'The code import has been ' + text + '.')
525
        else:
526
            self.request.response.addNotification('No changes made.')
527
    name = label.lower().replace(' ', '_')
528
    return form.Action(
529
        label, name=name, success=success, condition=condition)
530
531
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
532
class CodeImportEditView(CodeImportBaseView):
5895.4.3 by Tim Penhey
Added edit view.
533
    """View for editing code imports.
534
6650.1.2 by Michael Hudson
less hacking, no more working
535
    This view is registered against the branch, but mostly edits the code
536
    import for that branch -- the exception being that it also allows the
537
    editing of the branch whiteboard.  If the branch has no associated code
538
    import, then the result is a 404.  If the branch does have a code import,
539
    then the adapters property allows the form internals to do the associated
540
    mappings.
5895.4.3 by Tim Penhey
Added edit view.
541
    """
542
6650.1.2 by Michael Hudson
less hacking, no more working
543
    schema = EditCodeImportForm
544
5763.5.11 by Tim Penhey
OMG, the test passes, quick, commit!
545
    # Need this to render the context to prepopulate the form fields.
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
546
    # Added here as the base class isn't LaunchpadEditFormView.
547
    render_context = True
9129.2.4 by Tim Penhey
Remove codeimport-edit.pt in favour of generic-edit.pt.
548
    page_title = 'Edit import details'
549
    label = page_title
6650.1.1 by Michael Hudson
some hacking
550
551
    @property
552
    def initial_values(self):
6650.1.19 by Michael Hudson
review stuff
553
        return {'whiteboard': self.context.whiteboard}
5895.4.3 by Tim Penhey
Added edit view.
554
555
    def initialize(self):
556
        """Show a 404 if the branch has no code import."""
557
        self.code_import = self.context.code_import
558
        if self.code_import is None:
559
            raise NotFoundError
5763.5.7 by Tim Penhey
Add new pagetest.
560
        # The next and cancel location is the branch details page.
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
561
        self.cancel_url = self.next_url = canonical_url(self.context)
562
        CodeImportBaseView.initialize(self)
5895.4.3 by Tim Penhey
Added edit view.
563
564
    @property
565
    def adapters(self):
5763.5.11 by Tim Penhey
OMG, the test passes, quick, commit!
566
        """See `LaunchpadFormView`."""
6650.1.2 by Michael Hudson
less hacking, no more working
567
        return {EditCodeImportForm: self.code_import}
5895.4.3 by Tim Penhey
Added edit view.
568
569
    def setUpFields(self):
5895.4.6 by Tim Penhey
Added unittest around code import approval et al, and got the pages working.
570
        CodeImportBaseView.setUpFields(self)
5895.4.3 by Tim Penhey
Added edit view.
571
572
        # If the import is a Subversion import, then omit the CVS
573
        # fields, and vice versa.
574
        if self.code_import.rcs_type == RevisionControlSystems.CVS:
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
575
            self.form_fields = self.form_fields.omit('url')
9946.1.7 by Michael Hudson
make test actually test what it was intended to, fix it
576
        elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
577
                                           RevisionControlSystems.BZR_SVN,
10129.6.13 by Tim Penhey
Merge jelmer's hg-import branch and resolve conflicts.
578
                                           RevisionControlSystems.GIT,
579
                                           RevisionControlSystems.HG):
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
580
            self.form_fields = self.form_fields.omit(
581
                'cvs_root', 'cvs_module')
5895.4.3 by Tim Penhey
Added edit view.
582
        else:
583
            raise AssertionError('Unknown rcs_type for code import.')
584
585
    def _showButtonForStatus(self, status):
586
        """If the status is different, and the user is super, show button."""
587
        return self._super_user and self.code_import.review_status != status
588
6650.1.3 by Michael Hudson
refactor so that I only have to change once place to do stuff with the new
589
    actions = form.Actions(
6650.1.13 by Michael Hudson
some kind of progress
590
        _makeEditAction(_('Update'), None, 'updated'),
6650.1.10 by Michael Hudson
renames, docstrings
591
        _makeEditAction(
6650.1.13 by Michael Hudson
some kind of progress
592
            _('Approve'), CodeImportReviewStatus.REVIEWED,
6650.1.3 by Michael Hudson
refactor so that I only have to change once place to do stuff with the new
593
            'approved'),
6650.1.10 by Michael Hudson
renames, docstrings
594
        _makeEditAction(
6650.1.13 by Michael Hudson
some kind of progress
595
            _('Mark Invalid'), CodeImportReviewStatus.INVALID,
6650.1.3 by Michael Hudson
refactor so that I only have to change once place to do stuff with the new
596
            'set as invalid'),
6650.1.10 by Michael Hudson
renames, docstrings
597
        _makeEditAction(
6650.1.13 by Michael Hudson
some kind of progress
598
            _('Suspend'), CodeImportReviewStatus.SUSPENDED,
6650.1.3 by Michael Hudson
refactor so that I only have to change once place to do stuff with the new
599
            'suspended'),
6650.1.10 by Michael Hudson
renames, docstrings
600
        _makeEditAction(
6650.1.13 by Michael Hudson
some kind of progress
601
            _('Mark Failing'), CodeImportReviewStatus.FAILING,
6650.1.3 by Michael Hudson
refactor so that I only have to change once place to do stuff with the new
602
            'marked as failing'),
603
        )
6505.2.1 by Michael Hudson
cargo culting ftw
604
5763.5.8 by Tim Penhey
Add validate method.
605
    def validate(self, data):
606
        """See `LaunchpadFormView`."""
607
        if self.code_import.rcs_type == RevisionControlSystems.CVS:
5763.5.9 by Tim Penhey
Fix validation.
608
            self._validateCVS(
609
                data.get('cvs_root'), data.get('cvs_module'),
610
                self.code_import)
9946.1.7 by Michael Hudson
make test actually test what it was intended to, fix it
611
        elif self.code_import.rcs_type in (RevisionControlSystems.SVN,
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
612
                                           RevisionControlSystems.BZR_SVN,
10129.6.13 by Tim Penhey
Merge jelmer's hg-import branch and resolve conflicts.
613
                                           RevisionControlSystems.GIT,
614
                                           RevisionControlSystems.HG):
10129.6.3 by Tim Penhey
Starting to unify the urls for code imports, particularly in the browser code.
615
            self._validateURL(data.get('url'), self.code_import)
5763.5.11 by Tim Penhey
OMG, the test passes, quick, commit!
616
        else:
617
            raise AssertionError('Unknown rcs_type for code import.')
5763.6.1 by Tim Penhey
Machine view
618
619
620
class CodeImportMachineView(LaunchpadView):
621
    """The view for the page that shows all the import machines."""
622
623
    label = "Import machines for Launchpad"
624
625
    @property
626
    def machines(self):
627
        """Get the machines, sorted alphabetically by hostname."""
628
        return getUtility(ICodeImportMachineSet).getAll()