Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/controller/DiffusionRepositoryController.php
12242 views
1
<?php
2
3
final class DiffusionRepositoryController extends DiffusionController {
4
5
private $browseFuture;
6
private $branchButton = null;
7
private $branchFuture;
8
9
public function shouldAllowPublic() {
10
return true;
11
}
12
13
public function handleRequest(AphrontRequest $request) {
14
$response = $this->loadDiffusionContext();
15
if ($response) {
16
return $response;
17
}
18
19
require_celerity_resource('diffusion-css');
20
21
$viewer = $this->getViewer();
22
$drequest = $this->getDiffusionRequest();
23
$repository = $drequest->getRepository();
24
25
$crumbs = $this->buildCrumbs();
26
$crumbs->setBorder(true);
27
28
$header = $this->buildHeaderView($repository);
29
$actions = $this->buildActionList($repository);
30
$description = $this->buildDescriptionView($repository);
31
$locate_file = $this->buildLocateFile();
32
33
// Before we do any work, make sure we're looking at a some content: we're
34
// on a valid branch, and the repository is not empty.
35
$page_has_content = false;
36
$empty_title = null;
37
$empty_message = null;
38
39
// If this VCS supports branches, check that the selected branch actually
40
// exists.
41
if ($drequest->supportsBranches()) {
42
// NOTE: Mercurial may have multiple branch heads with the same name.
43
$ref_cursors = id(new PhabricatorRepositoryRefCursorQuery())
44
->setViewer($viewer)
45
->withRepositoryPHIDs(array($repository->getPHID()))
46
->withRefTypes(array(PhabricatorRepositoryRefCursor::TYPE_BRANCH))
47
->withRefNames(array($drequest->getBranch()))
48
->needPositions(true)
49
->execute();
50
51
// It's possible that this branch previously existed, but has been
52
// deleted. Make sure we have valid cursor positions, not just cursors.
53
$any_positions = false;
54
foreach ($ref_cursors as $ref_cursor) {
55
if ($ref_cursor->getPositions()) {
56
$any_positions = true;
57
break;
58
}
59
}
60
61
if ($any_positions) {
62
// This is a valid branch, so we necessarily have some content.
63
$page_has_content = true;
64
} else {
65
$default = $repository->getDefaultBranch();
66
if ($default != $drequest->getBranch()) {
67
$empty_title = pht('No Such Branch');
68
$empty_message = pht(
69
'There is no branch named "%s" in this repository.',
70
$drequest->getBranch());
71
} else {
72
$empty_title = pht('No Default Branch');
73
$empty_message = pht(
74
'This repository is configured with default branch "%s" but '.
75
'there is no branch with that name in this repository.',
76
$default);
77
}
78
}
79
}
80
81
// If we didn't find any branches, check if there are any commits at all.
82
// This can tailor the message for empty repositories.
83
$any_commit = null;
84
if (!$page_has_content) {
85
$any_commit = id(new DiffusionCommitQuery())
86
->setViewer($viewer)
87
->withRepository($repository)
88
->setLimit(1)
89
->execute();
90
if ($any_commit) {
91
if (!$drequest->supportsBranches()) {
92
$page_has_content = true;
93
}
94
} else {
95
$empty_title = pht('Empty Repository');
96
$empty_message = pht('This repository does not have any commits yet.');
97
}
98
}
99
100
if ($page_has_content) {
101
$content = $this->buildNormalContent($drequest);
102
} else {
103
// If we have a commit somewhere, find branches.
104
// TODO: Evan will replace
105
// $this->buildNormalContent($drequest);
106
$content = id(new PHUIInfoView())
107
->setTitle($empty_title)
108
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
109
->setErrors(array($empty_message));
110
}
111
112
$tabs = $this->buildTabsView('code');
113
114
$clone_uri = $drequest->generateURI(
115
array(
116
'action' => 'clone',
117
));
118
119
if ($repository->isSVN()) {
120
$clone_text = pht('Checkout');
121
} else {
122
$clone_text = pht('Clone');
123
}
124
125
$actions_button = id(new PHUIButtonView())
126
->setTag('a')
127
->setText(pht('Actions'))
128
->setIcon('fa-bars')
129
->addClass('mmr')
130
->setColor(PHUIButtonView::GREY)
131
->setDropdown(true)
132
->setDropdownMenu($actions);
133
134
$clone_button = id(new PHUIButtonView())
135
->setTag('a')
136
->setText($clone_text)
137
->setColor(PHUIButtonView::GREEN)
138
->setIcon('fa-download')
139
->setWorkflow(true)
140
->setHref($clone_uri);
141
142
$bar = id(new PHUILeftRightView())
143
->setLeft($locate_file)
144
->setRight(array($this->branchButton, $actions_button, $clone_button))
145
->addClass('diffusion-action-bar');
146
147
$status_view = null;
148
if ($repository->isReadOnly()) {
149
$status_view = id(new PHUIInfoView())
150
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
151
->setErrors(
152
array(
153
phutil_escape_html_newlines(
154
$repository->getReadOnlyMessageForDisplay()),
155
));
156
}
157
158
$view = id(new PHUITwoColumnView())
159
->setHeader($header)
160
->setFooter(
161
array(
162
$status_view,
163
$bar,
164
$description,
165
$content,
166
));
167
168
if ($page_has_content) {
169
$view->setTabs($tabs);
170
}
171
172
return $this->newPage()
173
->setTitle(
174
array(
175
$repository->getName(),
176
$repository->getDisplayName(),
177
))
178
->setCrumbs($crumbs)
179
->appendChild(array(
180
$view,
181
));
182
}
183
184
185
private function buildNormalContent(DiffusionRequest $drequest) {
186
$request = $this->getRequest();
187
$repository = $drequest->getRepository();
188
189
$commit = $drequest->getCommit();
190
$path = $drequest->getPath();
191
192
$futures = array();
193
194
$browse_pager = id(new PHUIPagerView())
195
->readFromRequest($request);
196
197
$this->browseFuture = $this->callConduitMethod(
198
'diffusion.browsequery',
199
array(
200
'commit' => $commit,
201
'path' => $path,
202
'limit' => $browse_pager->getPageSize() + 1,
203
));
204
$futures[] = $this->browseFuture;
205
206
if ($this->needBranchFuture()) {
207
$branch_limit = $this->getBranchLimit();
208
$this->branchFuture = $this->callConduitMethod(
209
'diffusion.branchquery',
210
array(
211
'closed' => false,
212
'limit' => $branch_limit + 1,
213
));
214
$futures[] = $this->branchFuture;
215
}
216
217
$futures = array_filter($futures);
218
$futures = new FutureIterator($futures);
219
foreach ($futures as $future) {
220
// Just resolve all the futures before continuing.
221
}
222
223
$content = array();
224
225
try {
226
$browse_results = $this->browseFuture->resolve();
227
$browse_results = DiffusionBrowseResultSet::newFromConduit(
228
$browse_results);
229
230
$browse_paths = $browse_results->getPaths();
231
$browse_paths = $browse_pager->sliceResults($browse_paths);
232
233
$browse_exception = null;
234
} catch (Exception $ex) {
235
$browse_results = null;
236
$browse_paths = null;
237
$browse_exception = $ex;
238
}
239
240
if ($browse_results) {
241
$readme = $this->renderDirectoryReadme($browse_results);
242
} else {
243
$readme = null;
244
}
245
246
$content[] = $this->buildBrowseTable(
247
$browse_results,
248
$browse_paths,
249
$browse_exception,
250
$browse_pager);
251
252
if ($readme) {
253
$content[] = $readme;
254
}
255
256
try {
257
$branch_button = $this->buildBranchList($drequest);
258
$this->branchButton = $branch_button;
259
} catch (Exception $ex) {
260
if (!$repository->isImporting()) {
261
$content[] = $this->renderStatusMessage(
262
pht('Unable to Load Branches'),
263
$ex->getMessage());
264
}
265
}
266
267
return $content;
268
}
269
270
private function buildHeaderView(PhabricatorRepository $repository) {
271
$viewer = $this->getViewer();
272
$drequest = $this->getDiffusionRequest();
273
$search = $this->renderSearchForm();
274
275
$header = id(new PHUIHeaderView())
276
->setHeader($repository->getName())
277
->setUser($viewer)
278
->setPolicyObject($repository)
279
->setProfileHeader(true)
280
->setImage($repository->getProfileImageURI())
281
->setImageEditURL('/diffusion/picture/'.$repository->getID().'/')
282
->addActionItem($search)
283
->addClass('diffusion-profile-header');
284
285
if (!$repository->isTracked()) {
286
$header->setStatus('fa-ban', 'dark', pht('Inactive'));
287
} else if ($repository->isReadOnly()) {
288
$header->setStatus('fa-wrench', 'indigo', pht('Under Maintenance'));
289
} else if ($repository->isImporting()) {
290
$ratio = $repository->loadImportProgress();
291
$percentage = sprintf('%.2f%%', 100 * $ratio);
292
$header->setStatus(
293
'fa-clock-o',
294
'indigo',
295
pht('Importing (%s)...', $percentage));
296
} else if ($repository->isPublishingDisabled()) {
297
$header->setStatus('fa-minus', 'bluegrey', pht('Publishing Disabled'));
298
} else {
299
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
300
}
301
302
if (!$repository->isSVN()) {
303
$default = $repository->getDefaultBranch();
304
if ($default != $drequest->getBranch()) {
305
$branch_tag = $this->renderBranchTag($drequest);
306
$header->addTag($branch_tag);
307
}
308
}
309
310
return $header;
311
}
312
313
private function buildActionList(PhabricatorRepository $repository) {
314
$viewer = $this->getViewer();
315
316
$edit_uri = $repository->getPathURI('manage/');
317
$action_view = id(new PhabricatorActionListView())
318
->setUser($viewer)
319
->setObject($repository);
320
321
$action_view->addAction(
322
id(new PhabricatorActionView())
323
->setName(pht('Manage Repository'))
324
->setIcon('fa-cogs')
325
->setHref($edit_uri));
326
327
if ($repository->isHosted()) {
328
$push_uri = $this->getApplicationURI(
329
'pushlog/?repositories='.$repository->getPHID());
330
331
$action_view->addAction(
332
id(new PhabricatorActionView())
333
->setName(pht('View Push Logs'))
334
->setIcon('fa-upload')
335
->setHref($push_uri));
336
337
$pull_uri = $this->getApplicationURI(
338
'synclog/?repositories='.$repository->getPHID());
339
340
$action_view->addAction(
341
id(new PhabricatorActionView())
342
->setName(pht('View Sync Logs'))
343
->setIcon('fa-exchange')
344
->setHref($pull_uri));
345
}
346
347
$pull_uri = $this->getApplicationURI(
348
'pulllog/?repositories='.$repository->getPHID());
349
350
$action_view->addAction(
351
id(new PhabricatorActionView())
352
->setName(pht('View Pull Logs'))
353
->setIcon('fa-download')
354
->setHref($pull_uri));
355
356
return $action_view;
357
}
358
359
private function buildDescriptionView(PhabricatorRepository $repository) {
360
$viewer = $this->getViewer();
361
$view = id(new PHUIPropertyListView())
362
->setUser($viewer);
363
364
$description = $repository->getDetail('description');
365
if (strlen($description)) {
366
$description = new PHUIRemarkupView($viewer, $description);
367
$view->addTextContent($description);
368
return id(new PHUIObjectBoxView())
369
->appendChild($view)
370
->addClass('diffusion-profile-description');
371
}
372
return null;
373
}
374
375
private function buildBranchList(DiffusionRequest $drequest) {
376
$viewer = $this->getViewer();
377
378
if (!$this->needBranchFuture()) {
379
return null;
380
}
381
382
$branches = $this->branchFuture->resolve();
383
if (!$branches) {
384
return null;
385
}
386
387
$limit = $this->getBranchLimit();
388
$more_branches = (count($branches) > $limit);
389
$branches = array_slice($branches, 0, $limit);
390
391
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches);
392
393
$actions = id(new PhabricatorActionListView())
394
->setViewer($viewer);
395
396
foreach ($branches as $branch) {
397
$branch_uri = $drequest->generateURI(
398
array(
399
'action' => 'browse',
400
'branch' => $branch->getShortname(),
401
));
402
$actions->addAction(
403
id(new PhabricatorActionView())
404
->setName($branch->getShortname())
405
->setIcon('fa-code-fork')
406
->setHref($branch_uri));
407
}
408
409
if ($more_branches) {
410
$more_uri = $drequest->generateURI(
411
array(
412
'action' => 'branches',
413
));
414
$actions->addAction(
415
id(new PhabricatorActionView())
416
->setType(PhabricatorActionView::TYPE_DIVIDER));
417
$actions->addAction(
418
id(new PhabricatorActionView())
419
->setName(pht('See More Branches'))
420
->setIcon('fa-external-link')
421
->setHref($more_uri));
422
}
423
424
$button = id(new PHUIButtonView())
425
->setText(pht('Branch: %s', $drequest->getBranch()))
426
->setTag('a')
427
->addClass('mmr')
428
->setIcon('fa-code-fork')
429
->setColor(PHUIButtonView::GREY)
430
->setDropdown(true)
431
->setDropdownMenu($actions);
432
433
return $button;
434
}
435
436
private function buildLocateFile() {
437
$request = $this->getRequest();
438
$viewer = $request->getUser();
439
$drequest = $this->getDiffusionRequest();
440
$repository = $drequest->getRepository();
441
442
$form_box = null;
443
if ($repository->canUsePathTree()) {
444
Javelin::initBehavior(
445
'diffusion-locate-file',
446
array(
447
'controlID' => 'locate-control',
448
'inputID' => 'locate-input',
449
'browseBaseURI' => (string)$drequest->generateURI(
450
array(
451
'action' => 'browse',
452
)),
453
'uri' => (string)$drequest->generateURI(
454
array(
455
'action' => 'pathtree',
456
)),
457
));
458
459
$form = id(new AphrontFormView())
460
->setUser($viewer)
461
->appendChild(
462
id(new AphrontFormTypeaheadControl())
463
->setHardpointID('locate-control')
464
->setID('locate-input')
465
->setPlaceholder(pht('Locate File')));
466
$form_box = id(new PHUIBoxView())
467
->appendChild($form->buildLayoutView())
468
->addClass('diffusion-profile-locate');
469
}
470
return $form_box;
471
}
472
473
private function buildBrowseTable(
474
$browse_results,
475
$browse_paths,
476
$browse_exception,
477
PHUIPagerView $pager) {
478
479
require_celerity_resource('diffusion-icons-css');
480
481
$request = $this->getRequest();
482
$viewer = $request->getUser();
483
$drequest = $this->getDiffusionRequest();
484
$repository = $drequest->getRepository();
485
486
if ($browse_exception) {
487
if ($repository->isImporting()) {
488
// The history table renders a useful message.
489
return null;
490
} else {
491
return $this->renderStatusMessage(
492
pht('Unable to Retrieve Paths'),
493
$browse_exception->getMessage());
494
}
495
}
496
497
$browse_table = id(new DiffusionBrowseTableView())
498
->setUser($viewer)
499
->setDiffusionRequest($drequest);
500
if ($browse_paths) {
501
$browse_table->setPaths($browse_paths);
502
} else {
503
$browse_table->setPaths(array());
504
}
505
506
$browse_uri = $drequest->generateURI(array('action' => 'browse'));
507
$pager->setURI($browse_uri, 'offset');
508
509
$repository_name = $repository->getName();
510
$branch_name = $drequest->getBranch();
511
if (strlen($branch_name)) {
512
$repository_name .= ' ('.$branch_name.')';
513
}
514
515
$header = phutil_tag(
516
'a',
517
array(
518
'href' => $browse_uri,
519
'class' => 'diffusion-view-browse-header',
520
),
521
$repository_name);
522
523
return id(new PHUIObjectBoxView())
524
->setHeaderText($header)
525
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
526
->setTable($browse_table)
527
->addClass('diffusion-mobile-view')
528
->setPager($pager);
529
}
530
531
private function needBranchFuture() {
532
$drequest = $this->getDiffusionRequest();
533
534
if ($drequest->getBranch() === null) {
535
return false;
536
}
537
538
return true;
539
}
540
541
private function getBranchLimit() {
542
return 15;
543
}
544
545
}
546
547