3 final class DiffusionRepositoryController
extends DiffusionController
{
6 private $branchButton = null;
9 public function shouldAllowPublic() {
13 public function handleRequest(AphrontRequest
$request) {
14 $response = $this->loadDiffusionContext();
19 require_celerity_resource('diffusion-css');
21 $viewer = $this->getViewer();
22 $drequest = $this->getDiffusionRequest();
23 $repository = $drequest->getRepository();
25 $crumbs = $this->buildCrumbs();
26 $crumbs->setBorder(true);
28 $header = $this->buildHeaderView($repository);
29 $actions = $this->buildActionList($repository);
30 $description = $this->buildDescriptionView($repository);
31 $locate_file = $this->buildLocateFile();
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;
37 $empty_message = null;
39 // If this VCS supports branches, check that the selected branch actually
41 if ($drequest->supportsBranches()) {
42 // NOTE: Mercurial may have multiple branch heads with the same name.
43 $ref_cursors = id(new PhabricatorRepositoryRefCursorQuery())
45 ->withRepositoryPHIDs(array($repository->getPHID()))
46 ->withRefTypes(array(PhabricatorRepositoryRefCursor
::TYPE_BRANCH
))
47 ->withRefNames(array($drequest->getBranch()))
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;
62 // This is a valid branch, so we necessarily have some content.
63 $page_has_content = true;
65 $default = $repository->getDefaultBranch();
66 if ($default != $drequest->getBranch()) {
67 $empty_title = pht('No Such Branch');
69 'There is no branch named "%s" in this repository.',
70 $drequest->getBranch());
72 $empty_title = pht('No Default Branch');
74 'This repository is configured with default branch "%s" but '.
75 'there is no branch with that name in this repository.',
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.
84 if (!$page_has_content) {
85 $any_commit = id(new DiffusionCommitQuery())
87 ->withRepository($repository)
91 if (!$drequest->supportsBranches()) {
92 $page_has_content = true;
95 $empty_title = pht('Empty Repository');
96 $empty_message = pht('This repository does not have any commits yet.');
100 if ($page_has_content) {
101 $content = $this->buildNormalContent($drequest);
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));
112 $tabs = $this->buildTabsView('code');
114 $clone_uri = $drequest->generateURI(
119 if ($repository->isSVN()) {
120 $clone_text = pht('Checkout');
122 $clone_text = pht('Clone');
125 $actions_button = id(new PHUIButtonView())
127 ->setText(pht('Actions'))
130 ->setColor(PHUIButtonView
::GREY
)
132 ->setDropdownMenu($actions);
134 $clone_button = id(new PHUIButtonView())
136 ->setText($clone_text)
137 ->setColor(PHUIButtonView
::GREEN
)
138 ->setIcon('fa-download')
140 ->setHref($clone_uri);
142 $bar = id(new PHUILeftRightView())
143 ->setLeft($locate_file)
144 ->setRight(array($this->branchButton
, $actions_button, $clone_button))
145 ->addClass('diffusion-action-bar');
148 if ($repository->isReadOnly()) {
149 $status_view = id(new PHUIInfoView())
150 ->setSeverity(PHUIInfoView
::SEVERITY_WARNING
)
153 phutil_escape_html_newlines(
154 $repository->getReadOnlyMessageForDisplay()),
158 $view = id(new PHUITwoColumnView())
168 if ($page_has_content) {
169 $view->setTabs($tabs);
172 return $this->newPage()
175 $repository->getName(),
176 $repository->getDisplayName(),
185 private function buildNormalContent(DiffusionRequest
$drequest) {
186 $request = $this->getRequest();
187 $repository = $drequest->getRepository();
189 $commit = $drequest->getCommit();
190 $path = $drequest->getPath();
194 $browse_pager = id(new PHUIPagerView())
195 ->readFromRequest($request);
197 $this->browseFuture
= $this->callConduitMethod(
198 'diffusion.browsequery',
202 'limit' => $browse_pager->getPageSize() +
1,
204 $futures[] = $this->browseFuture
;
206 if ($this->needBranchFuture()) {
207 $branch_limit = $this->getBranchLimit();
208 $this->branchFuture
= $this->callConduitMethod(
209 'diffusion.branchquery',
212 'limit' => $branch_limit +
1,
214 $futures[] = $this->branchFuture
;
217 $futures = array_filter($futures);
218 $futures = new FutureIterator($futures);
219 foreach ($futures as $future) {
220 // Just resolve all the futures before continuing.
226 $browse_results = $this->browseFuture
->resolve();
227 $browse_results = DiffusionBrowseResultSet
::newFromConduit(
230 $browse_paths = $browse_results->getPaths();
231 $browse_paths = $browse_pager->sliceResults($browse_paths);
233 $browse_exception = null;
234 } catch (Exception
$ex) {
235 $browse_results = null;
236 $browse_paths = null;
237 $browse_exception = $ex;
240 if ($browse_results) {
241 $readme = $this->renderDirectoryReadme($browse_results);
246 $content[] = $this->buildBrowseTable(
253 $content[] = $readme;
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'),
270 private function buildHeaderView(PhabricatorRepository
$repository) {
271 $viewer = $this->getViewer();
272 $drequest = $this->getDiffusionRequest();
273 $search = $this->renderSearchForm();
275 $header = id(new PHUIHeaderView())
276 ->setHeader($repository->getName())
278 ->setPolicyObject($repository)
279 ->setProfileHeader(true)
280 ->setImage($repository->getProfileImageURI())
281 ->setImageEditURL('/diffusion/picture/'.$repository->getID().'/')
282 ->addActionItem($search)
283 ->addClass('diffusion-profile-header');
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);
295 pht('Importing (%s)...', $percentage));
296 } else if ($repository->isPublishingDisabled()) {
297 $header->setStatus('fa-minus', 'bluegrey', pht('Publishing Disabled'));
299 $header->setStatus('fa-check', 'bluegrey', pht('Active'));
302 if (!$repository->isSVN()) {
303 $default = $repository->getDefaultBranch();
304 if ($default != $drequest->getBranch()) {
305 $branch_tag = $this->renderBranchTag($drequest);
306 $header->addTag($branch_tag);
313 private function buildActionList(PhabricatorRepository
$repository) {
314 $viewer = $this->getViewer();
316 $edit_uri = $repository->getPathURI('manage/');
317 $action_view = id(new PhabricatorActionListView())
319 ->setObject($repository);
321 $action_view->addAction(
322 id(new PhabricatorActionView())
323 ->setName(pht('Manage Repository'))
325 ->setHref($edit_uri));
327 if ($repository->isHosted()) {
328 $push_uri = $this->getApplicationURI(
329 'pushlog/?repositories='.$repository->getPHID());
331 $action_view->addAction(
332 id(new PhabricatorActionView())
333 ->setName(pht('View Push Logs'))
334 ->setIcon('fa-upload')
335 ->setHref($push_uri));
337 $pull_uri = $this->getApplicationURI(
338 'synclog/?repositories='.$repository->getPHID());
340 $action_view->addAction(
341 id(new PhabricatorActionView())
342 ->setName(pht('View Sync Logs'))
343 ->setIcon('fa-exchange')
344 ->setHref($pull_uri));
347 $pull_uri = $this->getApplicationURI(
348 'pulllog/?repositories='.$repository->getPHID());
350 $action_view->addAction(
351 id(new PhabricatorActionView())
352 ->setName(pht('View Pull Logs'))
353 ->setIcon('fa-download')
354 ->setHref($pull_uri));
359 private function buildDescriptionView(PhabricatorRepository
$repository) {
360 $viewer = $this->getViewer();
361 $view = id(new PHUIPropertyListView())
364 $description = $repository->getDetail('description');
365 if (strlen($description)) {
366 $description = new PHUIRemarkupView($viewer, $description);
367 $view->addTextContent($description);
368 return id(new PHUIObjectBoxView())
370 ->addClass('diffusion-profile-description');
375 private function buildBranchList(DiffusionRequest
$drequest) {
376 $viewer = $this->getViewer();
378 if (!$this->needBranchFuture()) {
382 $branches = $this->branchFuture
->resolve();
387 $limit = $this->getBranchLimit();
388 $more_branches = (count($branches) > $limit);
389 $branches = array_slice($branches, 0, $limit);
391 $branches = DiffusionRepositoryRef
::loadAllFromDictionaries($branches);
393 $actions = id(new PhabricatorActionListView())
394 ->setViewer($viewer);
396 foreach ($branches as $branch) {
397 $branch_uri = $drequest->generateURI(
399 'action' => 'browse',
400 'branch' => $branch->getShortname(),
403 id(new PhabricatorActionView())
404 ->setName($branch->getShortname())
405 ->setIcon('fa-code-fork')
406 ->setHref($branch_uri));
409 if ($more_branches) {
410 $more_uri = $drequest->generateURI(
412 'action' => 'branches',
415 id(new PhabricatorActionView())
416 ->setType(PhabricatorActionView
::TYPE_DIVIDER
));
418 id(new PhabricatorActionView())
419 ->setName(pht('See More Branches'))
420 ->setIcon('fa-external-link')
421 ->setHref($more_uri));
424 $button = id(new PHUIButtonView())
425 ->setText(pht('Branch: %s', $drequest->getBranch()))
428 ->setIcon('fa-code-fork')
429 ->setColor(PHUIButtonView
::GREY
)
431 ->setDropdownMenu($actions);
436 private function buildLocateFile() {
437 $request = $this->getRequest();
438 $viewer = $request->getUser();
439 $drequest = $this->getDiffusionRequest();
440 $repository = $drequest->getRepository();
443 if ($repository->canUsePathTree()) {
444 Javelin
::initBehavior(
445 'diffusion-locate-file',
447 'controlID' => 'locate-control',
448 'inputID' => 'locate-input',
449 'browseBaseURI' => (string)$drequest->generateURI(
451 'action' => 'browse',
453 'uri' => (string)$drequest->generateURI(
455 'action' => 'pathtree',
459 $form = id(new AphrontFormView())
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');
473 private function buildBrowseTable(
477 PHUIPagerView
$pager) {
479 require_celerity_resource('diffusion-icons-css');
481 $request = $this->getRequest();
482 $viewer = $request->getUser();
483 $drequest = $this->getDiffusionRequest();
484 $repository = $drequest->getRepository();
486 if ($browse_exception) {
487 if ($repository->isImporting()) {
488 // The history table renders a useful message.
491 return $this->renderStatusMessage(
492 pht('Unable to Retrieve Paths'),
493 $browse_exception->getMessage());
497 $browse_table = id(new DiffusionBrowseTableView())
499 ->setDiffusionRequest($drequest);
501 $browse_table->setPaths($browse_paths);
503 $browse_table->setPaths(array());
506 $browse_uri = $drequest->generateURI(array('action' => 'browse'));
507 $pager->setURI($browse_uri, 'offset');
509 $repository_name = $repository->getName();
510 $branch_name = $drequest->getBranch();
511 if (strlen($branch_name)) {
512 $repository_name .= ' ('.$branch_name.')';
515 $header = phutil_tag(
518 'href' => $browse_uri,
519 'class' => 'diffusion-view-browse-header',
523 return id(new PHUIObjectBoxView())
524 ->setHeaderText($header)
525 ->setBackground(PHUIObjectBoxView
::BLUE_PROPERTY
)
526 ->setTable($browse_table)
527 ->addClass('diffusion-mobile-view')
531 private function needBranchFuture() {
532 $drequest = $this->getDiffusionRequest();
534 if ($drequest->getBranch() === null) {
541 private function getBranchLimit() {