Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/owners/xaction/PhabricatorOwnersPackagePathsTransaction.php
12256 views
1
<?php
2
3
final class PhabricatorOwnersPackagePathsTransaction
4
extends PhabricatorOwnersPackageTransactionType {
5
6
const TRANSACTIONTYPE = 'owners.paths';
7
8
public function generateOldValue($object) {
9
$paths = $object->getPaths();
10
return mpull($paths, 'getRef');
11
}
12
13
public function generateNewValue($object, $value) {
14
$new = $value;
15
16
foreach ($new as $key => $info) {
17
$info['excluded'] = (int)idx($info, 'excluded');
18
19
// The input has one "path" key with the display path.
20
// Move it to "display", then normalize the value in "path".
21
22
$display_path = $info['path'];
23
$raw_path = rtrim($display_path, '/').'/';
24
25
$info['path'] = $raw_path;
26
$info['display'] = $display_path;
27
28
$new[$key] = $info;
29
}
30
31
return $new;
32
}
33
34
public function getTransactionHasEffect($object, $old, $new) {
35
list($add, $rem) = PhabricatorOwnersPath::getTransactionValueChanges(
36
$old,
37
$new);
38
39
return ($add || $rem);
40
}
41
42
public function validateTransactions($object, array $xactions) {
43
$errors = array();
44
45
if (!$xactions) {
46
return $errors;
47
}
48
49
$old = mpull($object->getPaths(), 'getRef');
50
foreach ($xactions as $xaction) {
51
$new = $xaction->getNewValue();
52
53
// Check that we have a list of paths.
54
if (!is_array($new)) {
55
$errors[] = $this->newInvalidError(
56
pht('Path specification must be a list of paths.'),
57
$xaction);
58
continue;
59
}
60
61
// Check that each item in the list is formatted properly.
62
$type_exception = null;
63
foreach ($new as $key => $value) {
64
try {
65
PhutilTypeSpec::checkMap(
66
$value,
67
array(
68
'repositoryPHID' => 'string',
69
'path' => 'string',
70
'excluded' => 'optional wild',
71
));
72
} catch (PhutilTypeCheckException $ex) {
73
$errors[] = $this->newInvalidError(
74
pht(
75
'Path specification list contains invalid value '.
76
'in key "%s": %s.',
77
$key,
78
$ex->getMessage()),
79
$xaction);
80
$type_exception = $ex;
81
}
82
}
83
84
if ($type_exception) {
85
continue;
86
}
87
88
// Check that any new paths reference legitimate repositories which
89
// the viewer has permission to see.
90
list($rem, $add) = PhabricatorOwnersPath::getTransactionValueChanges(
91
$old,
92
$new);
93
94
if ($add) {
95
$repository_phids = ipull($add, 'repositoryPHID');
96
97
$repositories = id(new PhabricatorRepositoryQuery())
98
->setViewer($this->getActor())
99
->withPHIDs($repository_phids)
100
->execute();
101
$repositories = mpull($repositories, null, 'getPHID');
102
103
foreach ($add as $ref) {
104
$repository_phid = $ref['repositoryPHID'];
105
if (isset($repositories[$repository_phid])) {
106
continue;
107
}
108
109
$errors[] = $this->newInvalidError(
110
pht(
111
'Path specification list references repository PHID "%s", '.
112
'but that is not a valid, visible repository.',
113
$repository_phid));
114
}
115
}
116
}
117
118
return $errors;
119
}
120
121
public function applyExternalEffects($object, $value) {
122
$old = $this->generateOldValue($object);
123
$new = $value;
124
125
$paths = $object->getPaths();
126
127
// We store paths in a normalized format with a trailing slash, regardless
128
// of whether the user enters "path/to/file.c" or "src/backend/". Normalize
129
// paths now.
130
131
$display_map = array();
132
$seen_map = array();
133
foreach ($new as $key => $spec) {
134
$raw_path = $spec['path'];
135
$display_path = $spec['display'];
136
137
// If the user entered two paths in the same repository which normalize
138
// to the same value (like "src/main.c" and "src/main.c/"), discard the
139
// duplicates.
140
$repository_phid = $spec['repositoryPHID'];
141
if (isset($seen_map[$repository_phid][$raw_path])) {
142
unset($new[$key]);
143
continue;
144
}
145
146
$new[$key]['path'] = $raw_path;
147
$display_map[$raw_path] = $display_path;
148
$seen_map[$repository_phid][$raw_path] = true;
149
}
150
151
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
152
list($rem, $add) = $diffs;
153
154
$set = PhabricatorOwnersPath::getSetFromTransactionValue($rem);
155
foreach ($paths as $path) {
156
$ref = $path->getRef();
157
if (PhabricatorOwnersPath::isRefInSet($ref, $set)) {
158
$path->delete();
159
continue;
160
}
161
162
// If the user has changed the display value for a path but the raw
163
// storage value hasn't changed, update the display value.
164
165
if (isset($display_map[$path->getPath()])) {
166
$path
167
->setPathDisplay($display_map[$path->getPath()])
168
->save();
169
continue;
170
}
171
}
172
173
foreach ($add as $ref) {
174
$path = PhabricatorOwnersPath::newFromRef($ref)
175
->setPackageID($object->getID())
176
->setPathDisplay($display_map[$ref['path']])
177
->save();
178
}
179
}
180
181
public function getTitle() {
182
// TODO: Flesh this out.
183
return pht(
184
'%s updated paths for this package.',
185
$this->renderAuthor());
186
}
187
188
public function hasChangeDetailView() {
189
return true;
190
}
191
192
public function newChangeDetailView() {
193
$old = $this->getOldValue();
194
$new = $this->getNewValue();
195
196
$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
197
list($rem, $add) = $diffs;
198
199
$rows = array();
200
foreach ($rem as $ref) {
201
$rows[] = array(
202
'class' => 'diff-removed',
203
'change' => '-',
204
) + $ref;
205
}
206
207
foreach ($add as $ref) {
208
$rows[] = array(
209
'class' => 'diff-added',
210
'change' => '+',
211
) + $ref;
212
}
213
214
$rowc = array();
215
foreach ($rows as $key => $row) {
216
$rowc[] = $row['class'];
217
218
if (array_key_exists('display', $row)) {
219
$display_path = $row['display'];
220
} else {
221
$display_path = $row['path'];
222
}
223
224
$rows[$key] = array(
225
$row['change'],
226
$row['excluded'] ? pht('Exclude') : pht('Include'),
227
$this->renderHandle($row['repositoryPHID']),
228
$display_path,
229
);
230
}
231
232
$table = id(new AphrontTableView($rows))
233
->setViewer($this->getViewer())
234
->setRowClasses($rowc)
235
->setHeaders(
236
array(
237
null,
238
pht('Type'),
239
pht('Repository'),
240
pht('Path'),
241
))
242
->setColumnClasses(
243
array(
244
null,
245
null,
246
null,
247
'wide',
248
));
249
250
return $table;
251
}
252
253
}
254
255