Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/diffusion/controller/DiffusionBrowseController.php
12242 views
1
<?php
2
3
final class DiffusionBrowseController extends DiffusionController {
4
5
private $lintCommit;
6
private $lintMessages;
7
private $corpusButtons = array();
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
$drequest = $this->getDiffusionRequest();
20
21
// Figure out if we're browsing a directory, a file, or a search result
22
// list.
23
24
$grep = $request->getStr('grep');
25
if (phutil_nonempty_string($grep)) {
26
return $this->browseSearch();
27
}
28
29
$pager = id(new PHUIPagerView())
30
->readFromRequest($request);
31
32
$results = DiffusionBrowseResultSet::newFromConduit(
33
$this->callConduitWithDiffusionRequest(
34
'diffusion.browsequery',
35
array(
36
'path' => $drequest->getPath(),
37
'commit' => $drequest->getStableCommit(),
38
'offset' => $pager->getOffset(),
39
'limit' => $pager->getPageSize() + 1,
40
)));
41
42
$reason = $results->getReasonForEmptyResultSet();
43
$is_file = ($reason == DiffusionBrowseResultSet::REASON_IS_FILE);
44
45
if ($is_file) {
46
return $this->browseFile();
47
}
48
49
$paths = $results->getPaths();
50
$paths = $pager->sliceResults($paths);
51
$results->setPaths($paths);
52
53
return $this->browseDirectory($results, $pager);
54
}
55
56
private function browseSearch() {
57
$drequest = $this->getDiffusionRequest();
58
$header = $this->buildHeaderView($drequest);
59
$path = nonempty(basename($drequest->getPath()), '/');
60
61
$search_results = $this->renderSearchResults();
62
$search_form = $this->renderSearchForm($path);
63
64
$search_form = phutil_tag(
65
'div',
66
array(
67
'class' => 'diffusion-mobile-search-form',
68
),
69
$search_form);
70
71
$crumbs = $this->buildCrumbs(
72
array(
73
'branch' => true,
74
'path' => true,
75
'view' => 'browse',
76
));
77
$crumbs->setBorder(true);
78
79
$tabs = $this->buildTabsView('code');
80
81
$view = id(new PHUITwoColumnView())
82
->setHeader($header)
83
->setTabs($tabs)
84
->setFooter(
85
array(
86
$search_form,
87
$search_results,
88
));
89
90
return $this->newPage()
91
->setTitle(
92
array(
93
nonempty(basename($drequest->getPath()), '/'),
94
$drequest->getRepository()->getDisplayName(),
95
))
96
->setCrumbs($crumbs)
97
->appendChild($view);
98
}
99
100
private function browseFile() {
101
$viewer = $this->getViewer();
102
$request = $this->getRequest();
103
$drequest = $this->getDiffusionRequest();
104
$repository = $drequest->getRepository();
105
106
$before = $request->getStr('before');
107
if ($before) {
108
return $this->buildBeforeResponse($before);
109
}
110
111
$path = $drequest->getPath();
112
$params = array(
113
'commit' => $drequest->getCommit(),
114
'path' => $drequest->getPath(),
115
);
116
117
$view = $request->getStr('view');
118
119
$byte_limit = null;
120
if ($view !== 'raw') {
121
$byte_limit = PhabricatorFileStorageEngine::getChunkThreshold();
122
$time_limit = 10;
123
124
$params += array(
125
'timeout' => $time_limit,
126
'byteLimit' => $byte_limit,
127
);
128
}
129
130
$response = $this->callConduitWithDiffusionRequest(
131
'diffusion.filecontentquery',
132
$params);
133
134
$hit_byte_limit = $response['tooHuge'];
135
$hit_time_limit = $response['tooSlow'];
136
137
$file_phid = $response['filePHID'];
138
$show_editor = false;
139
if ($hit_byte_limit) {
140
$corpus = $this->buildErrorCorpus(
141
pht(
142
'This file is larger than %s byte(s), and too large to display '.
143
'in the web UI.',
144
phutil_format_bytes($byte_limit)));
145
} else if ($hit_time_limit) {
146
$corpus = $this->buildErrorCorpus(
147
pht(
148
'This file took too long to load from the repository (more than '.
149
'%s second(s)).',
150
new PhutilNumber($time_limit)));
151
} else {
152
$file = id(new PhabricatorFileQuery())
153
->setViewer($viewer)
154
->withPHIDs(array($file_phid))
155
->executeOne();
156
if (!$file) {
157
throw new Exception(pht('Failed to load content file!'));
158
}
159
160
if ($view === 'raw') {
161
return $file->getRedirectResponse();
162
}
163
164
$data = $file->loadFileData();
165
166
$lfs_ref = $this->getGitLFSRef($repository, $data);
167
if ($lfs_ref) {
168
if ($view == 'git-lfs') {
169
$file = $this->loadGitLFSFile($lfs_ref);
170
171
// Rename the file locally so we generate a better vanity URI for
172
// it. In storage, it just has a name like "lfs-13f9a94c0923...",
173
// since we don't get any hints about possible human-readable names
174
// at upload time.
175
$basename = basename($drequest->getPath());
176
$file->makeEphemeral();
177
$file->setName($basename);
178
179
return $file->getRedirectResponse();
180
}
181
182
$corpus = $this->buildGitLFSCorpus($lfs_ref);
183
} else {
184
$show_editor = true;
185
186
$ref = id(new PhabricatorDocumentRef())
187
->setFile($file);
188
189
$engine = id(new DiffusionDocumentRenderingEngine())
190
->setRequest($request)
191
->setDiffusionRequest($drequest);
192
193
$corpus = $engine->newDocumentView($ref);
194
195
$this->corpusButtons[] = $this->renderFileButton();
196
}
197
}
198
199
$bar = $this->buildButtonBar($drequest, $show_editor);
200
$header = $this->buildHeaderView($drequest);
201
$header->setHeaderIcon('fa-file-code-o');
202
203
$follow = $request->getStr('follow');
204
$follow_notice = null;
205
if ($follow) {
206
$follow_notice = id(new PHUIInfoView())
207
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
208
->setTitle(pht('Unable to Continue'));
209
switch ($follow) {
210
case 'first':
211
$follow_notice->appendChild(
212
pht(
213
'Unable to continue tracing the history of this file because '.
214
'this commit is the first commit in the repository.'));
215
break;
216
case 'created':
217
$follow_notice->appendChild(
218
pht(
219
'Unable to continue tracing the history of this file because '.
220
'this commit created the file.'));
221
break;
222
}
223
}
224
225
$renamed = $request->getStr('renamed');
226
$renamed_notice = null;
227
if ($renamed) {
228
$renamed_notice = id(new PHUIInfoView())
229
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
230
->setTitle(pht('File Renamed'))
231
->appendChild(
232
pht(
233
'File history passes through a rename from "%s" to "%s".',
234
$drequest->getPath(),
235
$renamed));
236
}
237
238
$open_revisions = $this->buildOpenRevisions();
239
$owners_list = $this->buildOwnersList($drequest);
240
241
$crumbs = $this->buildCrumbs(
242
array(
243
'branch' => true,
244
'path' => true,
245
'view' => 'browse',
246
));
247
$crumbs->setBorder(true);
248
249
$basename = basename($this->getDiffusionRequest()->getPath());
250
$tabs = $this->buildTabsView('code');
251
$bar->setRight($this->corpusButtons);
252
253
$view = id(new PHUITwoColumnView())
254
->setHeader($header)
255
->setTabs($tabs)
256
->setFooter(array(
257
$bar,
258
$follow_notice,
259
$renamed_notice,
260
$corpus,
261
$open_revisions,
262
$owners_list,
263
));
264
265
$title = array($basename, $repository->getDisplayName());
266
267
return $this->newPage()
268
->setTitle($title)
269
->setCrumbs($crumbs)
270
->appendChild(
271
array(
272
$view,
273
));
274
275
}
276
277
public function browseDirectory(
278
DiffusionBrowseResultSet $results,
279
PHUIPagerView $pager) {
280
281
$request = $this->getRequest();
282
$drequest = $this->getDiffusionRequest();
283
$repository = $drequest->getRepository();
284
285
$reason = $results->getReasonForEmptyResultSet();
286
287
$this->buildActionButtons($drequest, true);
288
$details = $this->buildPropertyView($drequest);
289
290
$header = $this->buildHeaderView($drequest);
291
$header->setHeaderIcon('fa-folder-open');
292
293
$title = '/';
294
if ($drequest->getPath() !== null) {
295
$title = nonempty(basename($drequest->getPath()), '/');
296
}
297
298
$empty_result = null;
299
$browse_panel = null;
300
if (!$results->isValidResults()) {
301
$empty_result = new DiffusionEmptyResultView();
302
$empty_result->setDiffusionRequest($drequest);
303
$empty_result->setDiffusionBrowseResultSet($results);
304
$empty_result->setView($request->getStr('view'));
305
} else {
306
$browse_table = id(new DiffusionBrowseTableView())
307
->setDiffusionRequest($drequest)
308
->setPaths($results->getPaths())
309
->setUser($request->getUser());
310
311
$icon = 'fa-folder-open';
312
$browse_header = $this->buildPanelHeaderView($title, $icon);
313
314
$browse_panel = id(new PHUIObjectBoxView())
315
->setHeader($browse_header)
316
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
317
->setTable($browse_table)
318
->addClass('diffusion-mobile-view')
319
->setPager($pager);
320
}
321
322
$open_revisions = $this->buildOpenRevisions();
323
$readme = $this->renderDirectoryReadme($results);
324
325
$crumbs = $this->buildCrumbs(
326
array(
327
'branch' => true,
328
'path' => true,
329
'view' => 'browse',
330
));
331
332
$crumbs->setBorder(true);
333
$tabs = $this->buildTabsView('code');
334
$owners_list = $this->buildOwnersList($drequest);
335
$bar = id(new PHUILeftRightView())
336
->setRight($this->corpusButtons)
337
->addClass('diffusion-action-bar');
338
339
$view = id(new PHUITwoColumnView())
340
->setHeader($header)
341
->setTabs($tabs)
342
->setFooter(
343
array(
344
$bar,
345
$empty_result,
346
$browse_panel,
347
$open_revisions,
348
$owners_list,
349
$readme,
350
));
351
352
if ($details) {
353
$view->addPropertySection(pht('Details'), $details);
354
}
355
356
return $this->newPage()
357
->setTitle(array(
358
$title,
359
$repository->getDisplayName(),
360
))
361
->setCrumbs($crumbs)
362
->appendChild(
363
array(
364
$view,
365
));
366
}
367
368
private function renderSearchResults() {
369
$request = $this->getRequest();
370
371
$drequest = $this->getDiffusionRequest();
372
$repository = $drequest->getRepository();
373
$results = array();
374
375
$pager = id(new PHUIPagerView())
376
->readFromRequest($request);
377
378
$search_mode = null;
379
switch ($repository->getVersionControlSystem()) {
380
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
381
$results = array();
382
break;
383
default:
384
if (strlen($this->getRequest()->getStr('grep'))) {
385
$search_mode = 'grep';
386
$query_string = $request->getStr('grep');
387
$results = $this->callConduitWithDiffusionRequest(
388
'diffusion.searchquery',
389
array(
390
'grep' => $query_string,
391
'commit' => $drequest->getStableCommit(),
392
'path' => $drequest->getPath(),
393
'limit' => $pager->getPageSize() + 1,
394
'offset' => $pager->getOffset(),
395
));
396
}
397
break;
398
}
399
$results = $pager->sliceResults($results);
400
401
$table = null;
402
$header = null;
403
if ($search_mode == 'grep') {
404
$table = $this->renderGrepResults($results, $query_string);
405
$title = pht(
406
'File content matching "%s" under "%s"',
407
$query_string,
408
nonempty($drequest->getPath(), '/'));
409
$header = id(new PHUIHeaderView())
410
->setHeader($title)
411
->addClass('diffusion-search-result-header');
412
}
413
414
return array($header, $table, $pager);
415
416
}
417
418
private function renderGrepResults(array $results, $pattern) {
419
$drequest = $this->getDiffusionRequest();
420
require_celerity_resource('phabricator-search-results-css');
421
422
if (!$results) {
423
return id(new PHUIInfoView())
424
->setSeverity(PHUIInfoView::SEVERITY_NODATA)
425
->appendChild(
426
pht(
427
'The pattern you searched for was not found in the content of any '.
428
'files.'));
429
}
430
431
$grouped = array();
432
foreach ($results as $file) {
433
list($path, $line, $string) = $file;
434
$grouped[$path][] = array($line, $string);
435
}
436
437
$view = array();
438
foreach ($grouped as $path => $matches) {
439
$view[] = id(new DiffusionPatternSearchView())
440
->setPath($path)
441
->setMatches($matches)
442
->setPattern($pattern)
443
->setDiffusionRequest($drequest)
444
->render();
445
}
446
447
return $view;
448
}
449
450
private function buildButtonBar(
451
DiffusionRequest $drequest,
452
$show_editor) {
453
454
$viewer = $this->getViewer();
455
$base_uri = $this->getRequest()->getRequestURI();
456
457
$repository = $drequest->getRepository();
458
$path = $drequest->getPath();
459
$line = nonempty((int)$drequest->getLine(), 1);
460
$buttons = array();
461
462
$editor_uri = null;
463
$editor_template = null;
464
465
$link_engine = PhabricatorEditorURIEngine::newForViewer($viewer);
466
if ($link_engine) {
467
$link_engine->setRepository($repository);
468
469
$editor_uri = $link_engine->getURIForPath($path, $line);
470
$editor_template = $link_engine->getURITokensForPath($path);
471
}
472
473
$buttons[] =
474
id(new PHUIButtonView())
475
->setTag('a')
476
->setText(pht('Last Change'))
477
->setColor(PHUIButtonView::GREY)
478
->setHref(
479
$drequest->generateURI(
480
array(
481
'action' => 'change',
482
)))
483
->setIcon('fa-backward');
484
485
if ($editor_uri) {
486
$buttons[] =
487
id(new PHUIButtonView())
488
->setTag('a')
489
->setText(pht('Open File'))
490
->setHref($editor_uri)
491
->setIcon('fa-pencil')
492
->setID('editor_link')
493
->setMetadata(array('template' => $editor_template))
494
->setDisabled(!$editor_uri)
495
->setColor(PHUIButtonView::GREY);
496
}
497
498
$bar = id(new PHUILeftRightView())
499
->setLeft($buttons)
500
->addClass('diffusion-action-bar full-mobile-buttons');
501
return $bar;
502
}
503
504
private function buildOwnersList(DiffusionRequest $drequest) {
505
$viewer = $this->getViewer();
506
507
$have_owners = PhabricatorApplication::isClassInstalledForViewer(
508
'PhabricatorOwnersApplication',
509
$viewer);
510
if (!$have_owners) {
511
return null;
512
}
513
514
$repository = $drequest->getRepository();
515
516
$package_query = id(new PhabricatorOwnersPackageQuery())
517
->setViewer($viewer)
518
->withStatuses(array(PhabricatorOwnersPackage::STATUS_ACTIVE))
519
->withControl(
520
$repository->getPHID(),
521
array(
522
$drequest->getPath(),
523
));
524
525
$package_query->execute();
526
527
$packages = $package_query->getControllingPackagesForPath(
528
$repository->getPHID(),
529
$drequest->getPath());
530
531
$ownership = id(new PHUIObjectItemListView())
532
->setUser($viewer)
533
->setNoDataString(pht('No Owners'));
534
535
if ($packages) {
536
foreach ($packages as $package) {
537
$item = id(new PHUIObjectItemView())
538
->setObject($package)
539
->setObjectName($package->getMonogram())
540
->setHeader($package->getName())
541
->setHref($package->getURI());
542
543
$owners = $package->getOwners();
544
if ($owners) {
545
$owner_list = $viewer->renderHandleList(
546
mpull($owners, 'getUserPHID'));
547
} else {
548
$owner_list = phutil_tag('em', array(), pht('None'));
549
}
550
$item->addAttribute(pht('Owners: %s', $owner_list));
551
552
$auto = $package->getAutoReview();
553
$autoreview_map = PhabricatorOwnersPackage::getAutoreviewOptionsMap();
554
$spec = idx($autoreview_map, $auto, array());
555
$name = idx($spec, 'name', $auto);
556
$item->addIcon('fa-code', $name);
557
558
$rule = $package->newAuditingRule();
559
$item->addIcon($rule->getIconIcon(), $rule->getDisplayName());
560
561
if ($package->isArchived()) {
562
$item->setDisabled(true);
563
}
564
565
$ownership->addItem($item);
566
}
567
}
568
569
$view = id(new PHUIObjectBoxView())
570
->setHeaderText(pht('Owner Packages'))
571
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
572
->addClass('diffusion-mobile-view')
573
->setObjectList($ownership);
574
575
return $view;
576
}
577
578
private function renderFileButton($file_uri = null, $label = null) {
579
580
$base_uri = $this->getRequest()->getRequestURI();
581
582
if ($file_uri) {
583
$text = pht('Download File');
584
$href = $file_uri;
585
$icon = 'fa-download';
586
} else {
587
$text = pht('Raw File');
588
$href = $base_uri->alter('view', 'raw');
589
$icon = 'fa-file-text';
590
}
591
592
if ($label !== null) {
593
$text = $label;
594
}
595
596
$button = id(new PHUIButtonView())
597
->setTag('a')
598
->setText($text)
599
->setHref($href)
600
->setIcon($icon)
601
->setColor(PHUIButtonView::GREY);
602
603
return $button;
604
}
605
606
private function renderGitLFSButton() {
607
$viewer = $this->getViewer();
608
609
$uri = $this->getRequest()->getRequestURI();
610
$href = $uri->alter('view', 'git-lfs');
611
612
$text = pht('Download from Git LFS');
613
$icon = 'fa-download';
614
615
return id(new PHUIButtonView())
616
->setTag('a')
617
->setText($text)
618
->setHref($href)
619
->setIcon($icon)
620
->setColor(PHUIButtonView::GREY);
621
}
622
623
private function buildErrorCorpus($message) {
624
$text = id(new PHUIBoxView())
625
->addPadding(PHUI::PADDING_LARGE)
626
->appendChild($message);
627
628
$header = id(new PHUIHeaderView())
629
->setHeader(pht('Details'));
630
631
$box = id(new PHUIObjectBoxView())
632
->setHeader($header)
633
->appendChild($text);
634
635
return $box;
636
}
637
638
private function buildBeforeResponse($before) {
639
$request = $this->getRequest();
640
$drequest = $this->getDiffusionRequest();
641
642
// NOTE: We need to get the grandparent so we can capture filename changes
643
// in the parent.
644
645
$parent = $this->loadParentCommitOf($before);
646
$old_filename = null;
647
$was_created = false;
648
if ($parent) {
649
$grandparent = $this->loadParentCommitOf($parent);
650
651
if ($grandparent) {
652
$rename_query = new DiffusionRenameHistoryQuery();
653
$rename_query->setRequest($drequest);
654
$rename_query->setOldCommit($grandparent);
655
$rename_query->setViewer($request->getUser());
656
$old_filename = $rename_query->loadOldFilename();
657
$was_created = $rename_query->getWasCreated();
658
}
659
}
660
661
$follow = null;
662
if ($was_created) {
663
// If the file was created in history, that means older commits won't
664
// have it. Since we know it existed at 'before', it must have been
665
// created then; jump there.
666
$target_commit = $before;
667
$follow = 'created';
668
} else if ($parent) {
669
// If we found a parent, jump to it. This is the normal case.
670
$target_commit = $parent;
671
} else {
672
// If there's no parent, this was probably created in the initial commit?
673
// And the "was_created" check will fail because we can't identify the
674
// grandparent. Keep the user at 'before'.
675
$target_commit = $before;
676
$follow = 'first';
677
}
678
679
$path = $drequest->getPath();
680
$renamed = null;
681
if ($old_filename !== null &&
682
$old_filename !== '/'.$path) {
683
$renamed = $path;
684
$path = $old_filename;
685
}
686
687
$line = null;
688
// If there's a follow error, drop the line so the user sees the message.
689
if (!$follow) {
690
$line = $this->getBeforeLineNumber($target_commit);
691
}
692
693
$before_uri = $drequest->generateURI(
694
array(
695
'action' => 'browse',
696
'commit' => $target_commit,
697
'line' => $line,
698
'path' => $path,
699
));
700
701
if ($renamed === null) {
702
$before_uri->removeQueryParam('renamed');
703
} else {
704
$before_uri->replaceQueryParam('renamed', $renamed);
705
}
706
707
if ($follow === null) {
708
$before_uri->removeQueryParam('follow');
709
} else {
710
$before_uri->replaceQueryParam('follow', $follow);
711
}
712
713
return id(new AphrontRedirectResponse())->setURI($before_uri);
714
}
715
716
private function getBeforeLineNumber($target_commit) {
717
$drequest = $this->getDiffusionRequest();
718
$viewer = $this->getViewer();
719
720
$line = $drequest->getLine();
721
if (!$line) {
722
return null;
723
}
724
725
$diff_info = $this->callConduitWithDiffusionRequest(
726
'diffusion.rawdiffquery',
727
array(
728
'commit' => $drequest->getCommit(),
729
'path' => $drequest->getPath(),
730
'againstCommit' => $target_commit,
731
));
732
733
$file_phid = $diff_info['filePHID'];
734
$file = id(new PhabricatorFileQuery())
735
->setViewer($viewer)
736
->withPHIDs(array($file_phid))
737
->executeOne();
738
if (!$file) {
739
throw new Exception(
740
pht(
741
'Failed to load file ("%s") returned by "%s".',
742
$file_phid,
743
'diffusion.rawdiffquery.'));
744
}
745
746
$raw_diff = $file->loadFileData();
747
748
$old_line = 0;
749
$new_line = 0;
750
751
foreach (explode("\n", $raw_diff) as $text) {
752
if ($text[0] == '-' || $text[0] == ' ') {
753
$old_line++;
754
}
755
if ($text[0] == '+' || $text[0] == ' ') {
756
$new_line++;
757
}
758
if ($new_line == $line) {
759
return $old_line;
760
}
761
}
762
763
// We didn't find the target line.
764
return $line;
765
}
766
767
private function loadParentCommitOf($commit) {
768
$drequest = $this->getDiffusionRequest();
769
$user = $this->getRequest()->getUser();
770
771
$before_req = DiffusionRequest::newFromDictionary(
772
array(
773
'user' => $user,
774
'repository' => $drequest->getRepository(),
775
'commit' => $commit,
776
));
777
778
$parents = DiffusionQuery::callConduitWithDiffusionRequest(
779
$user,
780
$before_req,
781
'diffusion.commitparentsquery',
782
array(
783
'commit' => $commit,
784
));
785
786
return head($parents);
787
}
788
789
protected function markupText($text) {
790
$engine = PhabricatorMarkupEngine::newDiffusionMarkupEngine();
791
$engine->setConfig('viewer', $this->getRequest()->getUser());
792
$text = $engine->markupText($text);
793
794
$text = phutil_tag(
795
'div',
796
array(
797
'class' => 'phabricator-remarkup',
798
),
799
$text);
800
801
return $text;
802
}
803
804
protected function buildHeaderView(DiffusionRequest $drequest) {
805
$viewer = $this->getViewer();
806
$repository = $drequest->getRepository();
807
808
$commit_tag = $this->renderCommitHashTag($drequest);
809
810
$path = nonempty($drequest->getPath(), '/');
811
812
$search = $this->renderSearchForm($path);
813
814
$header = id(new PHUIHeaderView())
815
->setUser($viewer)
816
->setHeader($this->renderPathLinks($drequest, $mode = 'browse'))
817
->addActionItem($search)
818
->addTag($commit_tag)
819
->addClass('diffusion-browse-header');
820
821
if (!$repository->isSVN()) {
822
$branch_tag = $this->renderBranchTag($drequest);
823
$header->addTag($branch_tag);
824
}
825
826
return $header;
827
}
828
829
protected function buildPanelHeaderView($title, $icon) {
830
831
$header = id(new PHUIHeaderView())
832
->setHeader($title)
833
->setHeaderIcon($icon)
834
->addClass('diffusion-panel-header-view');
835
836
return $header;
837
838
}
839
840
protected function buildActionButtons(
841
DiffusionRequest $drequest,
842
$is_directory = false) {
843
844
$viewer = $this->getViewer();
845
$repository = $drequest->getRepository();
846
$history_uri = $drequest->generateURI(array('action' => 'history'));
847
$behind_head = $drequest->getSymbolicCommit();
848
$compare = null;
849
$head_uri = $drequest->generateURI(
850
array(
851
'commit' => '',
852
'action' => 'browse',
853
));
854
855
if ($repository->supportsBranchComparison() && $is_directory) {
856
$compare_uri = $drequest->generateURI(array('action' => 'compare'));
857
$compare = id(new PHUIButtonView())
858
->setText(pht('Compare'))
859
->setIcon('fa-code-fork')
860
->setWorkflow(true)
861
->setTag('a')
862
->setHref($compare_uri)
863
->setColor(PHUIButtonView::GREY);
864
$this->corpusButtons[] = $compare;
865
}
866
867
$head = null;
868
if ($behind_head) {
869
$head = id(new PHUIButtonView())
870
->setTag('a')
871
->setText(pht('Back to HEAD'))
872
->setHref($head_uri)
873
->setIcon('fa-home')
874
->setColor(PHUIButtonView::GREY);
875
$this->corpusButtons[] = $head;
876
}
877
878
$history = id(new PHUIButtonView())
879
->setText(pht('History'))
880
->setHref($history_uri)
881
->setTag('a')
882
->setIcon('fa-history')
883
->setColor(PHUIButtonView::GREY);
884
$this->corpusButtons[] = $history;
885
886
}
887
888
protected function buildPropertyView(
889
DiffusionRequest $drequest) {
890
891
$viewer = $this->getViewer();
892
$view = id(new PHUIPropertyListView())
893
->setUser($viewer);
894
895
if ($drequest->getSymbolicType() == 'tag') {
896
$symbolic = $drequest->getSymbolicCommit();
897
$view->addProperty(pht('Tag'), $symbolic);
898
899
$tags = $this->callConduitWithDiffusionRequest(
900
'diffusion.tagsquery',
901
array(
902
'names' => array($symbolic),
903
'needMessages' => true,
904
));
905
$tags = DiffusionRepositoryTag::newFromConduit($tags);
906
907
$tags = mpull($tags, null, 'getName');
908
$tag = idx($tags, $symbolic);
909
910
if ($tag && strlen($tag->getMessage())) {
911
$view->addSectionHeader(
912
pht('Tag Content'), 'fa-tag');
913
$view->addTextContent($this->markupText($tag->getMessage()));
914
}
915
}
916
917
if ($view->hasAnyProperties()) {
918
return $view;
919
}
920
921
return null;
922
}
923
924
private function buildOpenRevisions() {
925
$viewer = $this->getViewer();
926
927
$drequest = $this->getDiffusionRequest();
928
$repository = $drequest->getRepository();
929
$path = $drequest->getPath();
930
931
$recent = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
932
933
$revisions = id(new DifferentialRevisionQuery())
934
->setViewer($viewer)
935
->withPaths(array($path))
936
->withRepositoryPHIDs(array($repository->getPHID()))
937
->withIsOpen(true)
938
->withUpdatedEpochBetween($recent, null)
939
->setOrder(DifferentialRevisionQuery::ORDER_MODIFIED)
940
->setLimit(10)
941
->needReviewers(true)
942
->needFlags(true)
943
->needDrafts(true)
944
->execute();
945
946
if (!$revisions) {
947
return null;
948
}
949
950
$header = id(new PHUIHeaderView())
951
->setHeader(pht('Recent Open Revisions'));
952
953
$list = id(new DifferentialRevisionListView())
954
->setViewer($viewer)
955
->setRevisions($revisions)
956
->setNoBox(true);
957
958
$view = id(new PHUIObjectBoxView())
959
->setHeader($header)
960
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
961
->addClass('diffusion-mobile-view')
962
->appendChild($list);
963
964
return $view;
965
}
966
967
private function getGitLFSRef(PhabricatorRepository $repository, $data) {
968
if (!$repository->canUseGitLFS()) {
969
return null;
970
}
971
972
$lfs_pattern = '(^version https://git-lfs\\.github\\.com/spec/v1[\r\n])';
973
if (!preg_match($lfs_pattern, $data)) {
974
return null;
975
}
976
977
$matches = null;
978
if (!preg_match('(^oid sha256:(.*)$)m', $data, $matches)) {
979
return null;
980
}
981
982
$hash = $matches[1];
983
$hash = trim($hash);
984
985
return id(new PhabricatorRepositoryGitLFSRefQuery())
986
->setViewer($this->getViewer())
987
->withRepositoryPHIDs(array($repository->getPHID()))
988
->withObjectHashes(array($hash))
989
->executeOne();
990
}
991
992
private function buildGitLFSCorpus(PhabricatorRepositoryGitLFSRef $ref) {
993
// TODO: We should probably test if we can load the file PHID here and
994
// show the user an error if we can't, rather than making them click
995
// through to hit an error.
996
997
$title = basename($this->getDiffusionRequest()->getPath());
998
$icon = 'fa-archive';
999
$drequest = $this->getDiffusionRequest();
1000
$this->buildActionButtons($drequest);
1001
$header = $this->buildPanelHeaderView($title, $icon);
1002
1003
$severity = PHUIInfoView::SEVERITY_NOTICE;
1004
1005
$messages = array();
1006
$messages[] = pht(
1007
'This %s file is stored in Git Large File Storage.',
1008
phutil_format_bytes($ref->getByteSize()));
1009
1010
try {
1011
$file = $this->loadGitLFSFile($ref);
1012
$this->corpusButtons[] = $this->renderGitLFSButton();
1013
} catch (Exception $ex) {
1014
$severity = PHUIInfoView::SEVERITY_ERROR;
1015
$messages[] = pht('The data for this file could not be loaded.');
1016
}
1017
1018
$this->corpusButtons[] = $this->renderFileButton(
1019
null, pht('View Raw LFS Pointer'));
1020
1021
$corpus = id(new PHUIObjectBoxView())
1022
->setHeader($header)
1023
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
1024
->addClass('diffusion-mobile-view')
1025
->setCollapsed(true);
1026
1027
if ($messages) {
1028
$corpus->setInfoView(
1029
id(new PHUIInfoView())
1030
->setSeverity($severity)
1031
->setErrors($messages));
1032
}
1033
1034
return $corpus;
1035
}
1036
1037
private function loadGitLFSFile(PhabricatorRepositoryGitLFSRef $ref) {
1038
$viewer = $this->getViewer();
1039
1040
$file = id(new PhabricatorFileQuery())
1041
->setViewer($viewer)
1042
->withPHIDs(array($ref->getFilePHID()))
1043
->executeOne();
1044
if (!$file) {
1045
throw new Exception(
1046
pht(
1047
'Failed to load file object for Git LFS ref "%s"!',
1048
$ref->getObjectHash()));
1049
}
1050
1051
return $file;
1052
}
1053
1054
}
1055
1056