~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/code/browser/branch.py

  • Committer: Julian Edwards
  • Date: 2011-07-28 20:46:18 UTC
  • mfrom: (13553 devel)
  • mto: This revision was merged to the branch mainline in revision 13555.
  • Revision ID: julian.edwards@canonical.com-20110728204618-tivj2wx2oa9s32bx
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
    copy_field,
46
46
    use_template,
47
47
    )
48
 
from lazr.restful.utils import smartquote
49
48
from lazr.uri import URI
50
49
import pytz
51
 
from zope.app.form import CustomWidgetFactory
 
50
import simplejson
52
51
from zope.app.form.browser import TextAreaWidget
53
 
from zope.app.form.browser.boolwidgets import CheckBoxWidget
54
52
from zope.component import (
55
53
    getUtility,
56
54
    queryAdapter,
80
78
    _,
81
79
    searchbuilder,
82
80
    )
83
 
from lp.services.feeds.browser import (
 
81
from canonical.launchpad.browser.feeds import (
84
82
    BranchFeedLink,
85
83
    FeedsMixin,
86
84
    )
96
94
    stepthrough,
97
95
    stepto,
98
96
    )
99
 
from canonical.launchpad.webapp.authorization import (
100
 
    check_permission,
101
 
    precache_permission_for_objects,
102
 
    )
 
97
from canonical.launchpad.webapp.authorization import check_permission
103
98
from canonical.launchpad.webapp.interfaces import ICanonicalUrlData
104
99
from canonical.launchpad.webapp.menu import structured
 
100
from canonical.lazr.utils import smartquote
105
101
from lp.app.browser.launchpad import Hierarchy
106
102
from lp.app.browser.launchpadform import (
107
103
    action,
109
105
    LaunchpadEditFormView,
110
106
    LaunchpadFormView,
111
107
    )
112
 
from lp.app.browser.lazrjs import EnumChoiceWidget
 
108
from lp.app.browser.lazrjs import (
 
109
    EnumChoiceWidget,
 
110
    )
113
111
from lp.app.errors import NotFoundError
114
112
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
115
113
from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
134
132
from lp.code.errors import (
135
133
    BranchCreationForbidden,
136
134
    BranchExists,
137
 
    CannotUpgradeBranch,
138
135
    CodeImportAlreadyRequested,
139
136
    CodeImportAlreadyRunning,
140
137
    CodeImportNotInReviewedState,
443
440
    def initialize(self):
444
441
        self.branch = self.context
445
442
        self.notices = []
446
 
        # Cache permission so private team owner can be rendered.
447
 
        # The security adaptor will do the job also but we don't want or need
448
 
        # the expense of running several complex SQL queries.
449
 
        authorised_people = [self.branch.owner]
450
 
        if self.user is not None:
451
 
            precache_permission_for_objects(
452
 
                self.request, "launchpad.LimitedView", authorised_people)
453
443
        # Replace our context with a decorated branch, if it is not already
454
444
        # decorated.
455
445
        if not isinstance(self.context, DecoratedBranch):
649
639
               (RevisionControlSystems.SVN, RevisionControlSystems.BZR_SVN)
650
640
 
651
641
    @property
652
 
    def url_is_web(self):
653
 
        """True if an imported branch's URL is HTTP or HTTPS."""
654
 
        # You should only be calling this if it's an SVN, BZR, GIT or HG code
655
 
        # import
 
642
    def svn_url_is_web(self):
 
643
        """True if an imported branch's SVN URL is HTTP or HTTPS."""
 
644
        # You should only be calling this if it's an SVN code import
656
645
        assert self.context.code_import
657
646
        url = self.context.code_import.url
658
647
        assert url
724
713
        'lifecycle_status',
725
714
        'whiteboard',
726
715
        ])
727
 
    explicitly_private = copy_field(
728
 
        IBranch['explicitly_private'], readonly=False)
 
716
    private = copy_field(IBranch['private'], readonly=False)
729
717
    reviewer = copy_field(IBranch['reviewer'], required=True)
730
718
    owner = copy_field(IBranch['owner'], readonly=False)
731
719
 
768
756
                    "The branch owner has been changed to %s (%s)"
769
757
                    % (new_owner.displayname, new_owner.name))
770
758
        if 'private' in data:
771
 
            # Read only for display.
772
 
            data.pop('private')
773
 
        if 'explicitly_private' in data:
774
 
            private = data.pop('explicitly_private')
775
 
            if (private != self.context.private
776
 
                and self.context.private == self.context.explicitly_private):
 
759
            private = data.pop('private')
 
760
            if private != self.context.private:
777
761
                # We only want to show notifications if it actually changed.
778
762
                self.context.setPrivate(private, self.user)
779
763
                changed = True
854
838
    def mirror_of_ssh(self):
855
839
        """True if this a mirror branch with an sftp or bzr+ssh URL."""
856
840
        if not self.context.url:
857
 
            return False  # not a mirror branch
 
841
            return False # not a mirror branch
858
842
        uri = URI(self.context.url)
859
843
        return uri.scheme in ('sftp', 'bzr+ssh')
860
844
 
1010
994
 
1011
995
    @action('Upgrade', name='upgrade_branch')
1012
996
    def upgrade_branch_action(self, action, data):
1013
 
        try:
1014
 
            self.context.requestUpgrade(self.user)
1015
 
        except CannotUpgradeBranch as e:
1016
 
            self.request.response.addErrorNotification(e)
 
997
        self.context.requestUpgrade()
1017
998
 
1018
999
 
1019
1000
class BranchEditView(BranchEditFormView, BranchNameValidationMixin):
1020
1001
    """The main branch view for editing the branch attributes."""
1021
1002
 
1022
1003
    field_names = [
1023
 
        'owner', 'name', 'explicitly_private', 'url', 'description',
1024
 
        'lifecycle_status']
 
1004
        'owner', 'name', 'private', 'url', 'description', 'lifecycle_status']
1025
1005
 
1026
1006
    custom_widget('lifecycle_status', LaunchpadRadioWidgetWithDescription)
1027
1007
 
1037
1017
        if branch.private:
1038
1018
            # If the branch is private, and can be public, show the field.
1039
1019
            show_private_field = policy.canBranchesBePublic()
1040
 
 
1041
 
            # If this branch is public but is deemed private because it is
1042
 
            # stacked on a private branch, disable the field.
1043
 
            if not branch.explicitly_private:
1044
 
                show_private_field = False
1045
 
                private_info = Bool(
1046
 
                    __name__="private",
1047
 
                    title=_("Branch is confidential"),
1048
 
                    description=_(
1049
 
                        "This branch is confidential because it is stacked "
1050
 
                        "on a private branch."))
1051
 
                private_info_field = form.Fields(
1052
 
                    private_info, render_context=self.render_context)
1053
 
                self.form_fields = self.form_fields.omit('private')
1054
 
                self.form_fields = private_info_field + self.form_fields
1055
 
                self.form_fields['private'].custom_widget = (
1056
 
                    CustomWidgetFactory(
1057
 
                        CheckBoxWidget, extra='disabled="disabled"'))
1058
1020
        else:
1059
1021
            # If the branch is public, and can be made private, show the
1060
1022
            # field.  Users with special access rights to branches can set
1064
1026
                user_has_special_branch_access(self.user))
1065
1027
 
1066
1028
        if not show_private_field:
1067
 
            self.form_fields = self.form_fields.omit('explicitly_private')
 
1029
            self.form_fields = self.form_fields.omit('private')
1068
1030
 
1069
1031
        # If the user can administer branches, then they should be able to
1070
1032
        # assign the ownership of the branch to any valid person or team.
1155
1117
 
1156
1118
    class schema(Interface):
1157
1119
        use_template(
1158
 
            IBranch, include=['owner', 'name', 'lifecycle_status'])
 
1120
            IBranch, include=['owner', 'name', 'url', 'lifecycle_status'])
 
1121
        branch_type = copy_field(
 
1122
            IBranch['branch_type'], vocabulary=UICreatableBranchType)
1159
1123
 
1160
1124
    for_input = True
1161
 
    field_names = ['owner', 'name', 'lifecycle_status']
 
1125
    field_names = ['owner', 'name', 'branch_type', 'url', 'lifecycle_status']
1162
1126
 
1163
1127
    branch = None
 
1128
    custom_widget('branch_type', LaunchpadRadioWidgetWithDescription)
1164
1129
    custom_widget('lifecycle_status', LaunchpadRadioWidgetWithDescription)
1165
1130
 
1166
1131
    initial_focus_widget = 'name'
1189
1154
        """
1190
1155
        return IPerson(self.context, self.user)
1191
1156
 
 
1157
    def showOptionalMarker(self, field_name):
 
1158
        """Don't show the optional marker for url."""
 
1159
        if field_name == 'url':
 
1160
            return False
 
1161
        else:
 
1162
            return LaunchpadFormView.showOptionalMarker(self, field_name)
 
1163
 
1192
1164
    @action('Register Branch', name='add')
1193
1165
    def add_action(self, action, data):
1194
1166
        """Handle a request to create a new branch for this product."""
1195
1167
        try:
 
1168
            ui_branch_type = data['branch_type']
1196
1169
            namespace = self.target.getNamespace(data['owner'])
1197
1170
            self.branch = namespace.createBranch(
1198
 
                branch_type=BranchType.HOSTED,
 
1171
                branch_type=BranchType.items[ui_branch_type.name],
1199
1172
                name=data['name'],
1200
1173
                registrant=self.user,
1201
 
                url=None,
 
1174
                url=data.get('url'),
1202
1175
                lifecycle_status=data['lifecycle_status'])
 
1176
            if self.branch.branch_type == BranchType.MIRRORED:
 
1177
                self.branch.requestMirror()
1203
1178
        except BranchCreationForbidden:
1204
1179
            self.addError(
1205
1180
                "You are not allowed to create branches in %s." %
1217
1192
                'owner',
1218
1193
                'You are not a member of %s' % owner.displayname)
1219
1194
 
 
1195
        branch_type = data.get('branch_type')
 
1196
        # If branch_type failed to validate, then the rest of the method
 
1197
        # doesn't make any sense.
 
1198
        if branch_type is None:
 
1199
            return
 
1200
 
 
1201
        # If the branch is a MIRRORED branch, then the url
 
1202
        # must be supplied, and if HOSTED the url must *not*
 
1203
        # be supplied.
 
1204
        url = data.get('url')
 
1205
        if branch_type == UICreatableBranchType.MIRRORED:
 
1206
            if url is None:
 
1207
                # If the url is not set due to url validation errors,
 
1208
                # there will be an error set for it.
 
1209
                error = self.getFieldError('url')
 
1210
                if not error:
 
1211
                    self.setFieldError(
 
1212
                        'url',
 
1213
                        'Branch URLs are required for Mirrored branches.')
 
1214
        elif branch_type == UICreatableBranchType.HOSTED:
 
1215
            if url is not None:
 
1216
                self.setFieldError(
 
1217
                    'url',
 
1218
                    'Branch URLs cannot be set for Hosted branches.')
 
1219
        elif branch_type == UICreatableBranchType.REMOTE:
 
1220
            # A remote location can, but doesn't have to be set.
 
1221
            pass
 
1222
        else:
 
1223
            raise AssertionError('Unknown branch type')
 
1224
 
1220
1225
    @property
1221
1226
    def cancel_url(self):
1222
1227
        return canonical_url(self.context)