~launchpad-pqm/launchpad/devel

« back to all changes in this revision

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

  • Committer: Danilo Segan
  • Date: 2011-04-22 14:02:29 UTC
  • mto: This revision was merged to the branch mainline in revision 12910.
  • Revision ID: danilo@canonical.com-20110422140229-zhq4d4c2k8jpglhf
Ignore hidden files when building combined JS file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 
1
# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
4
# pylint: disable-msg=C0322,F0401
32
32
    'latest_proposals_for_each_branch',
33
33
    ]
34
34
 
35
 
from functools import wraps
36
35
import operator
37
36
 
38
37
from lazr.delegates import delegates
 
38
from lazr.lifecycle.event import ObjectModifiedEvent
39
39
from lazr.restful.interface import copy_field
40
40
from lazr.restful.interfaces import (
41
 
    IJSONRequestCache,
42
41
    IWebServiceClientRequest,
43
42
    )
44
43
import simplejson
48
47
    getMultiAdapter,
49
48
    getUtility,
50
49
    )
 
50
from zope.event import notify as zope_notify
51
51
from zope.formlib import form
52
52
from zope.interface import (
53
53
    implements,
64
64
    SimpleVocabulary,
65
65
    )
66
66
 
67
 
from lp import _
 
67
from canonical.config import config
 
68
from canonical.launchpad import _
 
69
from canonical.launchpad.interfaces.message import IMessageSet
 
70
from canonical.launchpad.webapp import (
 
71
    canonical_url,
 
72
    ContextMenu,
 
73
    enabled_with_permission,
 
74
    LaunchpadView,
 
75
    Link,
 
76
    Navigation,
 
77
    stepthrough,
 
78
    stepto,
 
79
    )
 
80
from canonical.launchpad.webapp.authorization import check_permission
 
81
from canonical.launchpad.webapp.breadcrumb import Breadcrumb
 
82
from canonical.launchpad.webapp.interfaces import IPrimaryContext
 
83
from canonical.launchpad.webapp.menu import NavigationMenu
68
84
from lp.app.browser.launchpadform import (
69
85
    action,
70
86
    custom_widget,
71
87
    LaunchpadEditFormView,
72
88
    LaunchpadFormView,
73
 
    )
 
89
   )
74
90
from lp.app.browser.lazrjs import (
75
91
    TextAreaEditorWidget,
76
92
    vocabulary_to_choice_edit_items,
77
93
    )
78
94
from lp.app.browser.tales import DateTimeFormatterAPI
79
 
from lp.app.longpoll import subscribe
80
 
from lp.code.adapters.branch import BranchMergeProposalNoPreviewDiffDelta
 
95
from lp.code.adapters.branch import BranchMergeProposalDelta
81
96
from lp.code.browser.codereviewcomment import CodeReviewDisplayComment
82
97
from lp.code.browser.decorations import DecoratedBranch
83
98
from lp.code.enums import (
86
101
    CodeReviewNotificationLevel,
87
102
    CodeReviewVote,
88
103
    )
89
 
from lp.code.errors import (
90
 
    BranchMergeProposalExists,
91
 
    ClaimReviewFailed,
92
 
    InvalidBranchMergeProposal,
93
 
    WrongBranchMergeProposal,
94
 
    )
 
104
from lp.code.errors import WrongBranchMergeProposal
95
105
from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
96
106
from lp.code.interfaces.codereviewcomment import ICodeReviewComment
97
107
from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
101
111
    IComment,
102
112
    IConversation,
103
113
    )
104
 
from lp.services.config import config
105
114
from lp.services.features import getFeatureFlag
106
115
from lp.services.fields import (
107
116
    Summary,
108
117
    Whiteboard,
109
118
    )
110
 
from lp.services.messages.interfaces.message import IMessageSet
111
119
from lp.services.propertycache import cachedproperty
112
 
from lp.services.webapp import (
113
 
    canonical_url,
114
 
    ContextMenu,
115
 
    enabled_with_permission,
116
 
    LaunchpadView,
117
 
    Link,
118
 
    Navigation,
119
 
    stepthrough,
120
 
    stepto,
121
 
    )
122
 
from lp.services.webapp.authorization import check_permission
123
 
from lp.services.webapp.breadcrumb import Breadcrumb
124
 
from lp.services.webapp.interfaces import IPrimaryContext
125
 
from lp.services.webapp.menu import (
126
 
    NavigationMenu,
127
 
    structured,
128
 
    )
129
120
 
130
121
 
131
122
def latest_proposals_for_each_branch(proposals):
170
161
 
171
162
def notify(func):
172
163
    """Decorate a view method to send a notification."""
173
 
    @wraps(func)
 
164
 
174
165
    def decorator(view, *args, **kwargs):
175
 
        with BranchMergeProposalNoPreviewDiffDelta.monitor(view.context):
176
 
            return func(view, *args, **kwargs)
 
166
        snapshot = BranchMergeProposalDelta.snapshot(view.context)
 
167
        result = func(view, *args, **kwargs)
 
168
        zope_notify(ObjectModifiedEvent(view.context, snapshot, []))
 
169
        return result
177
170
    return decorator
178
171
 
179
172
 
534
527
 
535
528
    @cachedproperty
536
529
    def diff_oversized(self):
537
 
        """Return True if the preview_diff is over the configured size limit.
 
530
        """Return True if the review_diff is over the configured size limit.
538
531
 
539
532
        The diff can be over the limit in two ways.  If the diff is oversized
540
533
        in bytes it will be cut off at the Diff.text method.  If the number of
541
534
        lines is over the max_format_lines, then it is cut off at the fmt:diff
542
535
        processing.
543
536
        """
544
 
        preview_diff = self.preview_diff
545
 
        if preview_diff is None:
 
537
        review_diff = self.preview_diff
 
538
        if review_diff is None:
546
539
            return False
 
540
        if review_diff.oversized:
 
541
            return True
547
542
        diff_text = self.preview_diff_text
548
543
        return diff_text.count('\n') >= config.diff.max_format_lines
549
544
 
594
589
 
595
590
    implements(IBranchMergeProposalActionMenu)
596
591
 
 
592
    label = "Proposal to merge branch"
597
593
    schema = ClaimButton
598
594
 
599
 
    def initialize(self):
600
 
        super(BranchMergeProposalView, self).initialize()
601
 
        cache = IJSONRequestCache(self.request)
602
 
        cache.objects.update({
603
 
            'branch_diff_link':
604
 
                'https://%s/+loggerhead/%s/diff/' % (
605
 
                    config.launchpad.code_domain,
606
 
                    self.context.source_branch.unique_name),
607
 
            })
608
 
        if getFeatureFlag("longpoll.merge_proposals.enabled"):
609
 
            cache.objects['merge_proposal_event_key'] = (
610
 
                subscribe(self.context).event_key)
611
 
 
612
595
    @action('Claim', name='claim')
613
596
    def claim_action(self, action, data):
614
597
        """Claim this proposal."""
615
598
        request = self.context.getVoteReference(data['review_id'])
616
599
        if request is not None:
617
 
            try:
618
 
                request.claimReview(self.user)
619
 
            except ClaimReviewFailed as e:
620
 
                self.request.response.addErrorNotification(unicode(e))
 
600
            request.claimReview(self.user)
621
601
        self.next_url = canonical_url(self.context)
622
602
 
623
603
    @property
676
656
        return result
677
657
 
678
658
    @property
679
 
    def label(self):
680
 
        return "Merge %s into %s" % (
681
 
            self.context.source_branch.bzr_identity,
682
 
            self.context.target_branch.bzr_identity)
683
 
 
684
 
    @property
685
659
    def pending_diff(self):
686
660
        return (
687
661
            self.context.next_preview_diff_job is not None or
695
669
        """
696
670
        if self.context.preview_diff is not None:
697
671
            return self.context.preview_diff
 
672
        if self.context.review_diff is not None:
 
673
            return self.context.review_diff.diff
698
674
        return None
699
675
 
700
676
    @property
771
747
 
772
748
    def __init__(self, context, user, users_vote):
773
749
        self.context = context
 
750
        proposal = self.context.branch_merge_proposal
774
751
        self.can_change_review = (user == context.reviewer)
 
752
        self.trusted = proposal.target_branch.isPersonTrustedReviewer(
 
753
            context.reviewer)
775
754
        if user is None:
776
755
            self.user_can_review = False
777
756
        else:
788
767
        else:
789
768
            self.user_can_reassign = False
790
769
 
791
 
    @cachedproperty
792
 
    def trusted(self):
793
 
        """ Is the person a trusted reviewer."""
794
 
        proposal = self.context.branch_merge_proposal
795
 
        return proposal.target_branch.isPersonTrustedReviewer(
796
 
            self.context.reviewer)
797
 
 
798
770
    @property
799
771
    def show_date_requested(self):
800
772
        """Show the requested date if the reviewer is not the requester."""
850
822
        """Return the decorated votes for the proposal."""
851
823
        users_vote = self.context.getUsersVoteReference(self.user)
852
824
        return [DecoratedCodeReviewVoteReference(vote, self.user, users_vote)
853
 
                for vote in self.context.votes
854
 
                if check_permission('launchpad.LimitedView', vote.reviewer)]
 
825
                for vote in self.context.votes]
855
826
 
856
827
    @cachedproperty
857
828
    def current_reviews(self):
1038
1009
    @action('Resubmit', name='resubmit')
1039
1010
    def resubmit_action(self, action, data):
1040
1011
        """Resubmit this proposal."""
1041
 
        try:
1042
 
            proposal = self.context.resubmit(
1043
 
                self.user, data['source_branch'], data['target_branch'],
1044
 
                data['prerequisite_branch'], data['description'],
1045
 
                data['break_link'])
1046
 
        except BranchMergeProposalExists as e:
1047
 
            message = structured(
1048
 
                'Cannot resubmit because <a href="%(url)s">a similar merge'
1049
 
                ' proposal</a> is already active.',
1050
 
                url=canonical_url(e.existing_proposal))
1051
 
            self.request.response.addErrorNotification(message)
1052
 
            self.next_url = canonical_url(self.context)
1053
 
            return None
1054
 
        except InvalidBranchMergeProposal as e:
1055
 
            self.addError(str(e))
1056
 
            return None
 
1012
        proposal = self.context.resubmit(
 
1013
            self.user, data['source_branch'], data['target_branch'],
 
1014
            data['prerequisite_branch'], data['description'],
 
1015
            data['break_link'])
1057
1016
        self.next_url = canonical_url(proposal)
1058
1017
        return proposal
1059
1018