Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php
12256 views
1
<?php
2
3
final class DifferentialRevisionAcceptTransaction
4
extends DifferentialRevisionReviewTransaction {
5
6
const TRANSACTIONTYPE = 'differential.revision.accept';
7
const ACTIONKEY = 'accept';
8
9
protected function getRevisionActionLabel(
10
DifferentialRevision $revision,
11
PhabricatorUser $viewer) {
12
return pht('Accept Revision');
13
}
14
15
protected function getRevisionActionDescription(
16
DifferentialRevision $revision,
17
PhabricatorUser $viewer) {
18
return pht('These changes will be approved.');
19
}
20
21
public function getIcon() {
22
return 'fa-check-circle-o';
23
}
24
25
public function getColor() {
26
return 'green';
27
}
28
29
protected function getRevisionActionOrder() {
30
return 500;
31
}
32
33
public function getActionName() {
34
return pht('Accepted');
35
}
36
37
public function getCommandKeyword() {
38
$accept_key = 'differential.enable-email-accept';
39
$allow_email_accept = PhabricatorEnv::getEnvConfig($accept_key);
40
if (!$allow_email_accept) {
41
return null;
42
}
43
44
return 'accept';
45
}
46
47
public function getCommandAliases() {
48
return array();
49
}
50
51
public function getCommandSummary() {
52
return pht('Accept a revision.');
53
}
54
55
protected function getActionOptions(
56
PhabricatorUser $viewer,
57
DifferentialRevision $revision,
58
$include_accepted = false) {
59
60
$reviewers = $revision->getReviewers();
61
62
$options = array();
63
$value = array();
64
65
// Put the viewer's user reviewer first, if it exists, so that "Accept as
66
// yourself" is always at the top.
67
$head = array();
68
$tail = array();
69
foreach ($reviewers as $key => $reviewer) {
70
if ($reviewer->isUser()) {
71
$head[$key] = $reviewer;
72
} else {
73
$tail[$key] = $reviewer;
74
}
75
}
76
$reviewers = $head + $tail;
77
78
$diff_phid = $this->getActiveDiffPHID($revision);
79
$reviewer_phids = array();
80
81
// If the viewer isn't a reviewer, add them to the list of options first.
82
// This happens when you navigate to some revision you aren't involved in:
83
// you can accept and become a reviewer.
84
85
$viewer_phid = $viewer->getPHID();
86
if ($viewer_phid) {
87
if (!isset($reviewers[$viewer_phid])) {
88
$reviewer_phids[$viewer_phid] = $viewer_phid;
89
}
90
}
91
92
$default_unchecked = array();
93
foreach ($reviewers as $reviewer) {
94
$reviewer_phid = $reviewer->getReviewerPHID();
95
96
if (!$reviewer->hasAuthority($viewer)) {
97
// If the viewer doesn't have authority to act on behalf of a reviewer,
98
// we check if they can accept by force.
99
if ($revision->canReviewerForceAccept($viewer, $reviewer)) {
100
$default_unchecked[$reviewer_phid] = true;
101
} else {
102
continue;
103
}
104
}
105
106
if (!$include_accepted) {
107
if ($reviewer->isAccepted($diff_phid)) {
108
// If a reviewer is already in a full "accepted" state, don't
109
// include that reviewer as an option unless we're listing all
110
// reviewers, including reviewers who have already accepted.
111
continue;
112
}
113
}
114
115
$reviewer_phids[$reviewer_phid] = $reviewer_phid;
116
}
117
118
$handles = $viewer->loadHandles($reviewer_phids);
119
120
$head = array();
121
$tail = array();
122
foreach ($reviewer_phids as $reviewer_phid) {
123
$is_force = isset($default_unchecked[$reviewer_phid]);
124
125
if ($is_force) {
126
$tail[] = $reviewer_phid;
127
128
$options[$reviewer_phid] = pht(
129
'Force accept as %s',
130
$viewer->renderHandle($reviewer_phid));
131
} else {
132
$head[] = $reviewer_phid;
133
$value[] = $reviewer_phid;
134
135
$options[$reviewer_phid] = pht(
136
'Accept as %s',
137
$viewer->renderHandle($reviewer_phid));
138
}
139
}
140
141
// Reorder reviewers so "force accept" reviewers come at the end.
142
$options =
143
array_select_keys($options, $head) +
144
array_select_keys($options, $tail);
145
146
return array($options, $value);
147
}
148
149
public function generateOldValue($object) {
150
$actor = $this->getActor();
151
return $this->isViewerFullyAccepted($object, $actor);
152
}
153
154
public function applyExternalEffects($object, $value) {
155
$status = DifferentialReviewerStatus::STATUS_ACCEPTED;
156
$actor = $this->getActor();
157
$this->applyReviewerEffect($object, $actor, $value, $status);
158
}
159
160
protected function validateAction($object, PhabricatorUser $viewer) {
161
if ($object->isClosed()) {
162
throw new Exception(
163
pht(
164
'You can not accept this revision because it has already been '.
165
'closed. Only open revisions can be accepted.'));
166
}
167
168
if ($object->isDraft() || !$object->getShouldBroadcast()) {
169
throw new Exception(
170
pht('You can not accept a draft revision.'));
171
}
172
173
$config_key = 'differential.allow-self-accept';
174
if (!PhabricatorEnv::getEnvConfig($config_key)) {
175
if ($this->isViewerRevisionAuthor($object, $viewer)) {
176
throw new Exception(
177
pht(
178
'You can not accept this revision because you are the revision '.
179
'author. You can only accept revisions you do not own. You can '.
180
'change this behavior by adjusting the "%s" setting in Config.',
181
$config_key));
182
}
183
}
184
185
if ($this->isViewerFullyAccepted($object, $viewer)) {
186
throw new Exception(
187
pht(
188
'You can not accept this revision because you have already '.
189
'accepted it.'));
190
}
191
}
192
193
protected function validateOptionValue($object, $actor, array $value) {
194
if (!$value) {
195
throw new Exception(
196
pht(
197
'When accepting a revision, you must accept on behalf of at '.
198
'least one reviewer.'));
199
}
200
201
// NOTE: We're including reviewers who have already been accepted in this
202
// check. Legitimate users may race one another to accept on behalf of
203
// packages. If we get a form submission which includes a reviewer which
204
// someone has already accepted, that's fine. See T12757.
205
206
list($options) = $this->getActionOptions($actor, $object, true);
207
foreach ($value as $phid) {
208
if (!isset($options[$phid])) {
209
throw new Exception(
210
pht(
211
'Reviewer "%s" is not a valid reviewer which you have authority '.
212
'to accept on behalf of.',
213
$phid));
214
}
215
}
216
}
217
218
public function getTitle() {
219
$new = $this->getNewValue();
220
if (is_array($new) && $new) {
221
return pht(
222
'%s accepted this revision as %s reviewer(s): %s.',
223
$this->renderAuthor(),
224
phutil_count($new),
225
$this->renderHandleList($new));
226
} else {
227
return pht(
228
'%s accepted this revision.',
229
$this->renderAuthor());
230
}
231
}
232
233
public function getTitleForFeed() {
234
return pht(
235
'%s accepted %s.',
236
$this->renderAuthor(),
237
$this->renderObject());
238
}
239
240
public function getTransactionTypeForConduit($xaction) {
241
return 'accept';
242
}
243
244
public function getFieldValuesForConduit($object, $data) {
245
return array();
246
}
247
248
}
249
250