Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / diffusion / controller / DiffusionRepositoryController.php
blobad3518b81e5f15a4b35307ecad462ffdc7c42f8a
1 <?php
3 final class DiffusionRepositoryController extends DiffusionController {
5 private $browseFuture;
6 private $branchButton = null;
7 private $branchFuture;
9 public function shouldAllowPublic() {
10 return true;
13 public function handleRequest(AphrontRequest $request) {
14 $response = $this->loadDiffusionContext();
15 if ($response) {
16 return $response;
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;
36 $empty_title = null;
37 $empty_message = null;
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();
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;
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);
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;
94 } else {
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);
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));
112 $tabs = $this->buildTabsView('code');
114 $clone_uri = $drequest->generateURI(
115 array(
116 'action' => 'clone',
119 if ($repository->isSVN()) {
120 $clone_text = pht('Checkout');
121 } else {
122 $clone_text = pht('Clone');
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);
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);
142 $bar = id(new PHUILeftRightView())
143 ->setLeft($locate_file)
144 ->setRight(array($this->branchButton, $actions_button, $clone_button))
145 ->addClass('diffusion-action-bar');
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()),
158 $view = id(new PHUITwoColumnView())
159 ->setHeader($header)
160 ->setFooter(
161 array(
162 $status_view,
163 $bar,
164 $description,
165 $content,
168 if ($page_has_content) {
169 $view->setTabs($tabs);
172 return $this->newPage()
173 ->setTitle(
174 array(
175 $repository->getName(),
176 $repository->getDisplayName(),
178 ->setCrumbs($crumbs)
179 ->appendChild(array(
180 $view,
185 private function buildNormalContent(DiffusionRequest $drequest) {
186 $request = $this->getRequest();
187 $repository = $drequest->getRepository();
189 $commit = $drequest->getCommit();
190 $path = $drequest->getPath();
192 $futures = array();
194 $browse_pager = id(new PHUIPagerView())
195 ->readFromRequest($request);
197 $this->browseFuture = $this->callConduitMethod(
198 'diffusion.browsequery',
199 array(
200 'commit' => $commit,
201 'path' => $path,
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',
210 array(
211 'closed' => false,
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.
223 $content = array();
225 try {
226 $browse_results = $this->browseFuture->resolve();
227 $browse_results = DiffusionBrowseResultSet::newFromConduit(
228 $browse_results);
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);
242 } else {
243 $readme = null;
246 $content[] = $this->buildBrowseTable(
247 $browse_results,
248 $browse_paths,
249 $browse_exception,
250 $browse_pager);
252 if ($readme) {
253 $content[] = $readme;
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());
267 return $content;
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())
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');
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'));
302 if (!$repository->isSVN()) {
303 $default = $repository->getDefaultBranch();
304 if ($default != $drequest->getBranch()) {
305 $branch_tag = $this->renderBranchTag($drequest);
306 $header->addTag($branch_tag);
310 return $header;
313 private function buildActionList(PhabricatorRepository $repository) {
314 $viewer = $this->getViewer();
316 $edit_uri = $repository->getPathURI('manage/');
317 $action_view = id(new PhabricatorActionListView())
318 ->setUser($viewer)
319 ->setObject($repository);
321 $action_view->addAction(
322 id(new PhabricatorActionView())
323 ->setName(pht('Manage Repository'))
324 ->setIcon('fa-cogs')
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));
356 return $action_view;
359 private function buildDescriptionView(PhabricatorRepository $repository) {
360 $viewer = $this->getViewer();
361 $view = id(new PHUIPropertyListView())
362 ->setUser($viewer);
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');
372 return null;
375 private function buildBranchList(DiffusionRequest $drequest) {
376 $viewer = $this->getViewer();
378 if (!$this->needBranchFuture()) {
379 return null;
382 $branches = $this->branchFuture->resolve();
383 if (!$branches) {
384 return null;
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(
398 array(
399 'action' => 'browse',
400 'branch' => $branch->getShortname(),
402 $actions->addAction(
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(
411 array(
412 'action' => 'branches',
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));
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);
433 return $button;
436 private function buildLocateFile() {
437 $request = $this->getRequest();
438 $viewer = $request->getUser();
439 $drequest = $this->getDiffusionRequest();
440 $repository = $drequest->getRepository();
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',
453 'uri' => (string)$drequest->generateURI(
454 array(
455 'action' => 'pathtree',
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');
470 return $form_box;
473 private function buildBrowseTable(
474 $browse_results,
475 $browse_paths,
476 $browse_exception,
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.
489 return null;
490 } else {
491 return $this->renderStatusMessage(
492 pht('Unable to Retrieve Paths'),
493 $browse_exception->getMessage());
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());
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(
516 'a',
517 array(
518 'href' => $browse_uri,
519 'class' => 'diffusion-view-browse-header',
521 $repository_name);
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);
531 private function needBranchFuture() {
532 $drequest = $this->getDiffusionRequest();
534 if ($drequest->getBranch() === null) {
535 return false;
538 return true;
541 private function getBranchLimit() {
542 return 15;