@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Frame workboard empty/create states inside profile menu

Summary:
Ref T10054. Uncreated workboards feel a little awkward right now because you lose the menu. Instead, keep the menu.

I also plan to:

- add a "[X] Make the workboard the default view for this project." checkbox; and
- resolve T6961.

...which will touch this workflow, so modernize/straighten it out.

Test Plan:
Viewed workboard, no access state, empty state. Created empty board, imported board.

{F1066973}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T6961, T10054

Differential Revision: https://secure.phabricator.com/D15091

+142 -105
+142 -105
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 19 19 20 20 public function handleRequest(AphrontRequest $request) { 21 21 $viewer = $request->getUser(); 22 - $id = $request->getURIData('id'); 23 22 24 - $show_hidden = $request->getBool('hidden'); 25 - $this->showHidden = $show_hidden; 26 - 27 - $project = id(new PhabricatorProjectQuery()) 28 - ->setViewer($viewer) 29 - ->needImages(true); 30 - $id = $request->getURIData('id'); 31 - $slug = $request->getURIData('slug'); 32 - if ($slug) { 33 - $project->withSlugs(array($slug)); 34 - } else { 35 - $project->withIDs(array($id)); 36 - } 37 - $project = $project->executeOne(); 38 - if (!$project) { 39 - return new Aphront404Response(); 40 - } 41 - 42 - $this->setProject($project); 43 - $this->id = $project->getID(); 44 - 45 - $sort_key = $request->getStr('order'); 46 - switch ($sort_key) { 47 - case PhabricatorProjectColumn::ORDER_NATURAL: 48 - case PhabricatorProjectColumn::ORDER_PRIORITY: 49 - break; 50 - default: 51 - $sort_key = PhabricatorProjectColumn::DEFAULT_ORDER; 52 - break; 23 + $response = $this->loadProject(); 24 + if ($response) { 25 + return $response; 53 26 } 54 - $this->sortKey = $sort_key; 55 27 56 - $column_query = id(new PhabricatorProjectColumnQuery()) 57 - ->setViewer($viewer) 58 - ->withProjectPHIDs(array($project->getPHID())); 59 - if (!$show_hidden) { 60 - $column_query->withStatuses( 61 - array(PhabricatorProjectColumn::STATUS_ACTIVE)); 62 - } 28 + $project = $this->getProject(); 63 29 64 - $columns = $column_query->execute(); 65 - $columns = mpull($columns, null, 'getSequence'); 30 + $this->readRequestState(); 31 + $columns = $this->loadColumns($project); 66 32 67 33 // TODO: Expand the checks here if we add the ability 68 34 // to hide the Backlog column ··· 72 38 $project, 73 39 PhabricatorPolicyCapability::CAN_EDIT); 74 40 if (!$can_edit) { 75 - return $this->noAccessDialog($project); 41 + $content = $this->buildNoAccessContent($project); 42 + } else { 43 + $content = $this->buildInitializeContent($project); 76 44 } 77 - switch ($request->getStr('initialize-type')) { 78 - case 'backlog-only': 79 - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 80 - $column = PhabricatorProjectColumn::initializeNewColumn($viewer) 81 - ->setSequence(0) 82 - ->setProperty('isDefault', true) 83 - ->setProjectPHID($project->getPHID()) 84 - ->save(); 85 - $column->attachProject($project); 86 - $columns[0] = $column; 87 - unset($unguarded); 88 - break; 89 - case 'import': 90 - return id(new AphrontRedirectResponse()) 91 - ->setURI( 92 - $this->getApplicationURI('board/'.$project->getID().'/import/')); 93 - break; 94 - default: 95 - return $this->initializeWorkboardDialog($project); 96 - break; 45 + 46 + if ($content instanceof AphrontResponse) { 47 + return $content; 97 48 } 98 - } 49 + 50 + $nav = $this->getProfileMenu(); 51 + $nav->selectFilter(PhabricatorProject::PANEL_WORKBOARD); 99 52 100 - ksort($columns); 53 + $crumbs = $this->buildApplicationCrumbs(); 54 + $crumbs->addTextCrumb(pht('Workboard')); 55 + 56 + return $this->newPage() 57 + ->setTitle( 58 + array( 59 + pht('Workboard'), 60 + $project->getName(), 61 + )) 62 + ->setNavigation($nav) 63 + ->setCrumbs($crumbs) 64 + ->appendChild($content); 65 + } 101 66 102 67 $board_uri = $this->getApplicationURI('board/'.$project->getID().'/'); 103 68 ··· 350 315 351 316 $sort_menu = $this->buildSortMenu( 352 317 $viewer, 353 - $sort_key); 318 + $this->sortKey); 354 319 355 320 $filter_menu = $this->buildFilterMenu( 356 321 $viewer, ··· 358 323 $engine, 359 324 $query_key); 360 325 361 - $manage_menu = $this->buildManageMenu($project, $show_hidden); 326 + $manage_menu = $this->buildManageMenu($project, $this->showHidden); 362 327 363 328 $header_link = phutil_tag( 364 329 'a', ··· 402 367 )); 403 368 } 404 369 370 + private function readRequestState() { 371 + $request = $this->getRequest(); 372 + $project = $this->getProject(); 373 + 374 + $this->showHidden = $request->getBool('hidden'); 375 + $this->id = $project->getID(); 376 + 377 + $sort_key = $request->getStr('order'); 378 + switch ($sort_key) { 379 + case PhabricatorProjectColumn::ORDER_NATURAL: 380 + case PhabricatorProjectColumn::ORDER_PRIORITY: 381 + break; 382 + default: 383 + $sort_key = PhabricatorProjectColumn::DEFAULT_ORDER; 384 + break; 385 + } 386 + $this->sortKey = $sort_key; 387 + } 388 + 389 + private function loadColumns(PhabricatorProject $project) { 390 + $viewer = $this->getViewer(); 391 + 392 + $column_query = id(new PhabricatorProjectColumnQuery()) 393 + ->setViewer($viewer) 394 + ->withProjectPHIDs(array($project->getPHID())); 395 + 396 + if (!$this->showHidden) { 397 + $column_query->withStatuses( 398 + array(PhabricatorProjectColumn::STATUS_ACTIVE)); 399 + } 400 + 401 + $columns = $column_query->execute(); 402 + $columns = mpull($columns, null, 'getSequence'); 403 + ksort($columns); 404 + 405 + return $columns; 406 + } 407 + 405 408 private function buildSortMenu( 406 409 PhabricatorUser $viewer, 407 410 $sort_key) { ··· 697 700 return $column_button; 698 701 } 699 702 700 - private function initializeWorkboardDialog(PhabricatorProject $project) { 701 - 702 - $instructions = pht('This workboard has not been setup yet.'); 703 - $new_selector = id(new AphrontFormRadioButtonControl()) 704 - ->setName('initialize-type') 705 - ->setValue('backlog-only') 706 - ->addButton( 707 - 'backlog-only', 708 - pht('New Empty Board'), 709 - pht('Create a new board with just a backlog column.')) 710 - ->addButton( 711 - 'import', 712 - pht('Import Columns'), 713 - pht('Import board columns from another project.')); 714 - 715 - 716 - $cancel_uri = $this->getApplicationURI('profile/'.$project->getID().'/'); 717 - 718 - return $this->newDialog() 719 - ->setTitle(pht('New Workboard')) 720 - ->addSubmitButton('Continue') 721 - ->addCancelButton($cancel_uri) 722 - ->appendParagraph($instructions) 723 - ->appendChild($new_selector); 724 - } 725 - 726 - private function noAccessDialog(PhabricatorProject $project) { 727 - 728 - $instructions = pht('This workboard has not been setup yet.'); 729 - 730 - $dialog = id(new AphrontDialogView()) 731 - ->setUser($this->getRequest()->getUser()) 732 - ->setTitle(pht('No Workboard')) 733 - ->addCancelButton($this->getApplicationURI('view/'.$project->getID().'/')) 734 - ->appendParagraph($instructions); 735 - 736 - return id(new AphrontDialogResponse()) 737 - ->setDialog($dialog); 738 - } 739 - 740 703 741 704 /** 742 705 * Add current state parameters (like order and the visibility of hidden ··· 783 746 } 784 747 785 748 return $create_uri; 749 + } 750 + 751 + 752 + private function buildInitializeContent(PhabricatorProject $project) { 753 + $request = $this->getRequest(); 754 + $viewer = $this->getViewer(); 755 + 756 + $type = $request->getStr('initialize-type'); 757 + 758 + $id = $project->getID(); 759 + 760 + $profile_uri = $this->getApplicationURI("profile/{$id}/"); 761 + $board_uri = $this->getApplicationURI("board/{$id}/"); 762 + $import_uri = $this->getApplicationURI("board/{$id}/import/"); 763 + 764 + switch ($type) { 765 + case 'backlog-only': 766 + $column = PhabricatorProjectColumn::initializeNewColumn($viewer) 767 + ->setSequence(0) 768 + ->setProperty('isDefault', true) 769 + ->setProjectPHID($project->getPHID()) 770 + ->save(); 771 + 772 + return id(new AphrontRedirectResponse()) 773 + ->setURI($board_uri); 774 + case 'import': 775 + return id(new AphrontRedirectResponse()) 776 + ->setURI($import_uri); 777 + } 778 + 779 + $new_selector = id(new AphrontFormRadioButtonControl()) 780 + ->setName('initialize-type') 781 + ->setValue('backlog-only') 782 + ->addButton( 783 + 'backlog-only', 784 + pht('New Empty Board'), 785 + pht('Create a new board with just a backlog column.')) 786 + ->addButton( 787 + 'import', 788 + pht('Import Columns'), 789 + pht('Import board columns from another project.')); 790 + 791 + $form = id(new AphrontFormView()) 792 + ->setUser($viewer) 793 + ->appendRemarkupInstructions( 794 + pht('The workboard for this project has not been created yet.')) 795 + ->appendControl($new_selector) 796 + ->appendControl( 797 + id(new AphrontFormSubmitControl()) 798 + ->addCancelButton($profile_uri) 799 + ->setValue(pht('Create Workboard'))); 800 + 801 + $box = id(new PHUIObjectBoxView()) 802 + ->setHeaderText(pht('Create Workboard')) 803 + ->setForm($form); 804 + 805 + return $box; 806 + } 807 + 808 + private function buildNoAccessContent(PhabricatorProject $project) { 809 + $viewer = $this->getViewer(); 810 + 811 + $id = $project->getID(); 812 + 813 + $profile_uri = $this->getApplicationURI("profile/{$id}/"); 814 + 815 + return $this->newDialog() 816 + ->setTitle(pht('Unable to Create Workboard')) 817 + ->appendParagraph( 818 + pht( 819 + 'The workboard for this project has not been created yet, '. 820 + 'but you do not have permission to create it. Only users '. 821 + 'who can edit this project can create a workboard for it.')) 822 + ->addCancelButton($profile_uri); 786 823 } 787 824 788 825 }