@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.

Use ApplicationSearch for Releeph branch lists

Summary:
Releeph branch lists in project views have a bunch of custom UI right now; give them more standard UI and ApplicationSearch.

This drops a small piece of functionality: we now show only a total open request count instead of a detailed enumeration of each request status. I assume this is reasonable (that is, the important piece is "is there something to do on this branch?"), but we can muck with it if the more detailed status is important.

Test Plan: {F54344}

Reviewers: btrahan

Reviewed By: btrahan

CC: LegNeato, aran

Maniphest Tasks: T3656

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

+318 -211
+7 -3
src/__phutil_library_map__.php
··· 1946 1946 'ReleephBranchNamePreviewController' => 'applications/releeph/controller/branch/ReleephBranchNamePreviewController.php', 1947 1947 'ReleephBranchPreviewView' => 'applications/releeph/view/branch/ReleephBranchPreviewView.php', 1948 1948 'ReleephBranchQuery' => 'applications/releeph/query/ReleephBranchQuery.php', 1949 + 'ReleephBranchSearchEngine' => 'applications/releeph/query/ReleephBranchSearchEngine.php', 1949 1950 'ReleephBranchTemplate' => 'applications/releeph/view/branch/ReleephBranchTemplate.php', 1950 1951 'ReleephBranchViewController' => 'applications/releeph/controller/branch/ReleephBranchViewController.php', 1951 1952 'ReleephCommitFinder' => 'applications/releeph/commitfinder/ReleephCommitFinder.php', ··· 1979 1980 'ReleephProjectListController' => 'applications/releeph/controller/project/ReleephProjectListController.php', 1980 1981 'ReleephProjectQuery' => 'applications/releeph/query/ReleephProjectQuery.php', 1981 1982 'ReleephProjectSearchEngine' => 'applications/releeph/query/ReleephProjectSearchEngine.php', 1982 - 'ReleephProjectView' => 'applications/releeph/view/ReleephProjectView.php', 1983 1983 'ReleephProjectViewController' => 'applications/releeph/controller/project/ReleephProjectViewController.php', 1984 1984 'ReleephReasonFieldSpecification' => 'applications/releeph/field/specification/ReleephReasonFieldSpecification.php', 1985 1985 'ReleephRequest' => 'applications/releeph/storage/ReleephRequest.php', ··· 4128 4128 'ReleephBranchNamePreviewController' => 'ReleephController', 4129 4129 'ReleephBranchPreviewView' => 'AphrontFormControl', 4130 4130 'ReleephBranchQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4131 + 'ReleephBranchSearchEngine' => 'PhabricatorApplicationSearchEngine', 4131 4132 'ReleephBranchViewController' => 4132 4133 array( 4133 4134 0 => 'ReleephProjectController', ··· 4172 4173 ), 4173 4174 'ReleephProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4174 4175 'ReleephProjectSearchEngine' => 'PhabricatorApplicationSearchEngine', 4175 - 'ReleephProjectView' => 'AphrontView', 4176 - 'ReleephProjectViewController' => 'ReleephProjectController', 4176 + 'ReleephProjectViewController' => 4177 + array( 4178 + 0 => 'ReleephProjectController', 4179 + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 4180 + ), 4177 4181 'ReleephReasonFieldSpecification' => 'ReleephFieldSpecification', 4178 4182 'ReleephRequest' => 4179 4183 array(
+1 -2
src/applications/releeph/application/PhabricatorApplicationReleeph.php
··· 38 38 '(?:query/(?P<queryKey>[^/]+)/)?' => 'ReleephProjectListController', 39 39 'create/' => 'ReleephProjectCreateController', 40 40 '(?P<projectID>[1-9]\d*)/' => array( 41 - '' => 'ReleephProjectViewController', 42 - 'closedbranches/' => 'ReleephProjectViewController', 41 + '(?:query/(?P<queryKey>[^/]+)/)?' => 'ReleephProjectViewController', 43 42 'edit/' => 'ReleephProjectEditController', 44 43 'cutbranch/' => 'ReleephBranchCreateController', 45 44 'action/(?P<action>.+)/' => 'ReleephProjectActionController',
+18 -20
src/applications/releeph/controller/branch/ReleephBranchAccessController.php
··· 10 10 } 11 11 12 12 public function processRequest() { 13 - $rph_branch = $this->getReleephBranch(); 13 + $branch = $this->getReleephBranch(); 14 14 $request = $this->getRequest(); 15 15 16 - $active_uri = '/releeph/project/'.$rph_branch->getReleephProjectID().'/'; 17 - $inactive_uri = $active_uri.'inactive/'; 16 + $done_uri = '/releeph/project/'.$branch->getReleephProjectID().'/'; 18 17 19 18 switch ($this->action) { 20 19 case 'close': 21 20 $is_active = false; 22 - $origin_uri = $active_uri; 21 + $title_text = pht('Close Branch'); 22 + $body_text = pht( 23 + 'Really close the branch "%s"?', 24 + $branch->getBasename()); 25 + $button_text = pht('Close Branch'); 23 26 break; 24 - 25 27 case 're-open': 26 28 $is_active = true; 27 - $origin_uri = $inactive_uri; 29 + $title_text = pht('Reopen Branch'); 30 + $body_text = pht( 31 + 'Really reopen the branch "%s"?', 32 + $branch->getBasename()); 33 + $button_text = pht('Reopen Branch'); 28 34 break; 29 - 30 35 default: 31 36 throw new Exception("Unknown action '{$this->action}'!"); 32 37 break; ··· 35 40 if ($request->isDialogFormPost()) { 36 41 id(new ReleephBranchEditor()) 37 42 ->setActor($request->getUser()) 38 - ->setReleephBranch($rph_branch) 43 + ->setReleephBranch($branch) 39 44 ->changeBranchAccess($is_active ? 1 : 0); 40 - return id(new AphrontRedirectResponse()) 41 - ->setURI($origin_uri); 45 + 46 + return id(new AphrontReloadResponse())->setURI($done_uri); 42 47 } 43 48 44 - $button_text = pht('%s Branch', $this->action); 45 - $text = pht('Really %s the branch: %s?', 46 - $this->action, 47 - $rph_branch->getBasename()); 48 - $message = phutil_tag('p', array(), $text); 49 - 50 - 51 49 $dialog = new AphrontDialogView(); 52 50 $dialog 53 51 ->setUser($request->getUser()) 54 - ->setTitle(pht('Confirm')) 55 - ->appendChild($message) 52 + ->setTitle($title_text) 53 + ->appendChild($body_text) 56 54 ->addSubmitButton($button_text) 57 - ->addCancelButton($origin_uri); 55 + ->addCancelButton($done_uri); 58 56 59 57 return id(new AphrontDialogResponse())->setDialog($dialog); 60 58 }
+158 -31
src/applications/releeph/controller/project/ReleephProjectViewController.php
··· 1 1 <?php 2 2 3 - final class ReleephProjectViewController extends ReleephProjectController { 3 + final class ReleephProjectViewController extends ReleephProjectController 4 + implements PhabricatorApplicationSearchResultsControllerInterface { 5 + 6 + private $queryKey; 7 + 8 + public function shouldAllowPublic() { 9 + return true; 10 + } 11 + 12 + public function willProcessRequest(array $data) { 13 + parent::willProcessRequest($data); 14 + $this->queryKey = idx($data, 'queryKey'); 15 + } 4 16 5 17 public function processRequest() { 6 - // Load all branches 7 - $releeph_project = $this->getReleephProject(); 8 - $releeph_branches = id(new ReleephBranch()) 9 - ->loadAllWhere('releephProjectID = %d', 10 - $releeph_project->getID()); 18 + $request = $this->getRequest(); 19 + $controller = id(new PhabricatorApplicationSearchController($request)) 20 + ->setQueryKey($this->queryKey) 21 + ->setSearchEngine( 22 + id(new ReleephBranchSearchEngine()) 23 + ->setProjectID($this->getReleephProject()->getID())) 24 + ->setNavigation($this->buildSideNavView()); 25 + 26 + return $this->delegateToController($controller); 27 + } 28 + 29 + public function renderResultsList( 30 + array $branches, 31 + PhabricatorSavedQuery $saved) { 32 + assert_instances_of($branches, 'ReleephBranch'); 33 + 34 + $viewer = $this->getRequest()->getUser(); 35 + 36 + $projects = mpull($branches, 'getProject'); 37 + $repo_phids = mpull($projects, 'getRepositoryPHID'); 38 + 39 + $repos = id(new PhabricatorRepositoryQuery()) 40 + ->setViewer($viewer) 41 + ->withPHIDs($repo_phids) 42 + ->execute(); 43 + $repos = mpull($repos, null, 'getPHID'); 44 + 45 + $phids = mpull($branches, 'getCreatedByUserPHID'); 46 + $this->loadHandles($phids); 47 + 48 + $requests = array(); 49 + if ($branches) { 50 + $requests = id(new ReleephRequestQuery()) 51 + ->setViewer($viewer) 52 + ->withBranchIDs(mpull($branches, 'getID')) 53 + ->withStatus(ReleephRequestQuery::STATUS_OPEN) 54 + ->execute(); 55 + $requests = mgroup($requests, 'getBranchID'); 56 + } 11 57 12 - $path = $this->getRequest()->getRequestURI()->getPath(); 13 - $is_open_branches = strpos($path, 'closedbranches/') === false; 58 + $list = id(new PhabricatorObjectItemListView()) 59 + ->setUser($viewer); 60 + foreach ($branches as $branch) { 61 + $diffusion_href = null; 62 + $repo = idx($repos, $branch->getProject()->getRepositoryPHID()); 63 + if ($repo) { 64 + $drequest = DiffusionRequest::newFromDictionary( 65 + array( 66 + 'user' => $viewer, 67 + 'repository' => $repo, 68 + )); 69 + 70 + $diffusion_href = $drequest->generateURI( 71 + array( 72 + 'action' => 'branch', 73 + 'branch' => $branch->getName(), 74 + )); 75 + } 14 76 15 - $view = id(new ReleephProjectView()) 16 - ->setShowOpenBranches($is_open_branches) 17 - ->setUser($this->getRequest()->getUser()) 18 - ->setReleephProject($releeph_project) 19 - ->setBranches($releeph_branches); 77 + $branch_link = $branch->getName(); 78 + if ($diffusion_href) { 79 + $branch_link = phutil_tag( 80 + 'a', 81 + array( 82 + 'href' => $diffusion_href, 83 + ), 84 + $branch_link); 85 + } 20 86 21 - $crumbs = $this->buildApplicationCrumbs() 22 - ->addCrumb( 23 - id(new PhabricatorCrumbView()) 24 - ->setName($releeph_project->getName()) 25 - ->setHref($releeph_project->getURI())); 87 + $item = id(new PhabricatorObjectItemView()) 88 + ->setHeader($branch->getDisplayName()) 89 + ->setHref($branch->getURI()) 90 + ->addAttribute($branch_link); 26 91 27 - if ($releeph_project->getIsActive()) { 28 - $crumbs->addAction( 92 + $item->addAction( 29 93 id(new PHUIListItemView()) 30 - ->setHref($releeph_project->getURI('cutbranch')) 31 - ->setName(pht('Cut New Branch')) 32 - ->setIcon('create')); 94 + ->setIcon('edit') 95 + ->setHref($branch->getURI('edit/'))); 96 + 97 + if ($branch->getIsActive()) { 98 + $item->setBarColor('blue'); 99 + $item->addAction( 100 + id(new PHUIListItemView()) 101 + ->setIcon('delete') 102 + ->setWorkflow(true) 103 + ->setHref($branch->getURI('close/'))); 104 + } else { 105 + $item->setDisabled(true); 106 + $item->addAction( 107 + id(new PHUIListItemView()) 108 + ->setIcon('enable') 109 + ->setWorkflow(true) 110 + ->setHref($branch->getURI('re-open/'))); 111 + } 112 + 113 + $commit = $branch->getCutPointCommit(); 114 + if ($commit) { 115 + $item->addIcon( 116 + 'none', 117 + phabricator_datetime($commit->getEpoch(), $viewer)); 118 + } 119 + 120 + $open_count = count(idx($requests, $branch->getID(), array())); 121 + if ($open_count) { 122 + $item->setBarColor('orange'); 123 + $item->addIcon( 124 + 'fork', 125 + pht('%d Open Pull Request(s)', new PhutilNumber($open_count))); 126 + } 127 + 128 + $list->addItem($item); 33 129 } 34 130 35 - return $this->buildStandardPageResponse( 36 - array( 37 - $crumbs, 38 - $view, 39 - ), 40 - array( 41 - 'title' => $releeph_project->getName() 42 - )); 131 + return $list; 132 + } 133 + 134 + public function buildSideNavView($for_app = false) { 135 + $user = $this->getRequest()->getUser(); 136 + 137 + $nav = new AphrontSideNavFilterView(); 138 + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 139 + 140 + if ($for_app) { 141 + $nav->addFilter('project/create/', pht('Create Project')); 142 + } 143 + 144 + id(new ReleephBranchSearchEngine()) 145 + ->setProjectID($this->getReleephProject()->getID()) 146 + ->setViewer($user) 147 + ->addNavigationItems($nav->getMenu()); 148 + 149 + $nav->selectFilter(null); 150 + 151 + return $nav; 152 + } 153 + 154 + public function buildApplicationCrumbs() { 155 + $crumbs = parent::buildApplicationCrumbs(); 156 + 157 + $project = $this->getReleephProject(); 158 + 159 + $crumbs->addCrumb( 160 + id(new PhabricatorCrumbView()) 161 + ->setName($project->getName())); 162 + 163 + $crumbs->addAction( 164 + id(new PHUIListItemView()) 165 + ->setHref($project->getURI('cutbranch')) 166 + ->setName(pht('Cut New Branch')) 167 + ->setIcon('create')); 168 + 169 + return $crumbs; 43 170 } 44 171 45 172 }
+35
src/applications/releeph/query/ReleephBranchQuery.php
··· 5 5 6 6 private $ids; 7 7 private $phids; 8 + private $projectIDs; 9 + 10 + const STATUS_ALL = 'status-all'; 11 + const STATUS_OPEN = 'status-open'; 12 + private $status = self::STATUS_ALL; 8 13 9 14 private $needCutPointCommits; 10 15 ··· 20 25 21 26 public function needCutPointCommits($need_commits) { 22 27 $this->needCutPointCommits = $need_commits; 28 + return $this; 29 + } 30 + 31 + public function withStatus($status) { 32 + $this->status = $status; 33 + return $this; 34 + } 35 + 36 + public function withProjectIDs(array $ids) { 37 + $this->projectIDs = $ids; 23 38 return $this; 24 39 } 25 40 ··· 87 102 $conn_r, 88 103 'phid IN (%Ls)', 89 104 $this->phids); 105 + } 106 + 107 + if ($this->projectIDs) { 108 + $where[] = qsprintf( 109 + $conn_r, 110 + 'releephProjectID IN (%Ld)', 111 + $this->projectIDs); 112 + } 113 + 114 + $status = $this->status; 115 + switch ($status) { 116 + case self::STATUS_ALL: 117 + break; 118 + case self::STATUS_OPEN: 119 + $where[] = qsprintf( 120 + $conn_r, 121 + 'isActive = 1'); 122 + break; 123 + default: 124 + throw new Exception("Unknown status constant '{$status}'!"); 90 125 } 91 126 92 127 $where[] = $this->buildPagingClause($conn_r);
+94
src/applications/releeph/query/ReleephBranchSearchEngine.php
··· 1 + <?php 2 + 3 + final class ReleephBranchSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + private $projectID; 7 + 8 + public function setProjectID($project_id) { 9 + $this->projectID = $project_id; 10 + return $this; 11 + } 12 + 13 + public function getProjectID() { 14 + return $this->projectID; 15 + } 16 + 17 + public function buildSavedQueryFromRequest(AphrontRequest $request) { 18 + $saved = new PhabricatorSavedQuery(); 19 + 20 + $saved->setParameter('active', $request->getStr('active')); 21 + 22 + return $saved; 23 + } 24 + 25 + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 26 + $query = id(new ReleephBranchQuery()) 27 + ->needCutPointCommits(true) 28 + ->withProjectIDs(array($this->getProjectID())); 29 + 30 + $active = $saved->getParameter('active'); 31 + $value = idx($this->getActiveValues(), $active); 32 + if ($value !== null) { 33 + $query->withStatus($value); 34 + } 35 + 36 + return $query; 37 + } 38 + 39 + public function buildSearchForm( 40 + AphrontFormView $form, 41 + PhabricatorSavedQuery $saved_query) { 42 + 43 + $form->appendChild( 44 + id(new AphrontFormSelectControl()) 45 + ->setName('active') 46 + ->setLabel(pht('Show Branches')) 47 + ->setValue($saved_query->getParameter('active')) 48 + ->setOptions($this->getActiveOptions())); 49 + } 50 + 51 + protected function getURI($path) { 52 + return '/releeph/project/'.$this->getProjectID().'/'.$path; 53 + } 54 + 55 + public function getBuiltinQueryNames() { 56 + $names = array( 57 + 'open' => pht('Open'), 58 + 'all' => pht('All'), 59 + ); 60 + 61 + return $names; 62 + } 63 + 64 + public function buildSavedQueryFromBuiltin($query_key) { 65 + 66 + $query = $this->newSavedQuery(); 67 + $query->setQueryKey($query_key); 68 + 69 + switch ($query_key) { 70 + case 'open': 71 + return $query 72 + ->setParameter('active', 'open'); 73 + case 'all': 74 + return $query; 75 + } 76 + 77 + return parent::buildSavedQueryFromBuiltin($query_key); 78 + } 79 + 80 + private function getActiveOptions() { 81 + return array( 82 + 'open' => pht('Open Branches'), 83 + 'all' => pht('Open and Closed Branches'), 84 + ); 85 + } 86 + 87 + private function getActiveValues() { 88 + return array( 89 + 'open' => ReleephBranchQuery::STATUS_OPEN, 90 + 'all' => ReleephBranchQuery::STATUS_ALL, 91 + ); 92 + } 93 + 94 + }
-155
src/applications/releeph/view/ReleephProjectView.php
··· 1 - <?php 2 - 3 - final class ReleephProjectView extends AphrontView { 4 - 5 - private $showOpenBranches = true; 6 - private $releephProject; 7 - private $releephBranches; 8 - 9 - public function setShowOpenBranches($active) { 10 - $this->showOpenBranches = $active; 11 - return $this; 12 - } 13 - 14 - public function setReleephProject($releeph_project) { 15 - $this->releephProject = $releeph_project; 16 - return $this; 17 - } 18 - 19 - public function setBranches($branches) { 20 - $this->releephBranches = $branches; 21 - return $this; 22 - } 23 - 24 - public function render() { 25 - $releeph_project = $this->releephProject; 26 - 27 - if ($this->showOpenBranches) { 28 - $releeph_branches = mfilter($this->releephBranches, 'getIsActive'); 29 - } else { 30 - $releeph_branches = mfilter($this->releephBranches, 'getIsActive', true); 31 - } 32 - 33 - // Load all relevant PHID handles 34 - $phids = array_merge( 35 - array( 36 - $this->releephProject->getPHID(), 37 - $this->releephProject->getRepositoryPHID(), 38 - ), 39 - mpull($releeph_branches, 'getCreatedByUserPHID'), 40 - mpull($releeph_branches, 'getCutPointCommitPHID'), 41 - $releeph_project->getPushers()); 42 - $handles = id(new PhabricatorObjectHandleData($phids)) 43 - ->setViewer($this->getUser()) 44 - ->loadHandles(); 45 - 46 - // Sort branches, which requires the handles above 47 - $releeph_branches = self::sortBranches($releeph_branches, $handles); 48 - 49 - // The header 50 - $repository_phid = $releeph_project->getRepositoryPHID(); 51 - 52 - $header = hsprintf( 53 - '%s in %s repository', 54 - $releeph_project->getName(), 55 - $handles[$repository_phid]->renderLink()); 56 - 57 - if ($this->showOpenBranches) { 58 - $view_other_link = phutil_tag( 59 - 'a', 60 - array( 61 - 'href' => $releeph_project->getURI('closedbranches/'), 62 - ), 63 - 'View closed branches'); 64 - } else { 65 - $view_other_link = phutil_tag( 66 - 'a', 67 - array( 68 - 'href' => $releeph_project->getURI(), 69 - ), 70 - 'View open branches'); 71 - } 72 - 73 - $header = hsprintf("%s &middot; %s", $header, $view_other_link); 74 - 75 - // The "create branch" button 76 - $create_branch_url = $releeph_project->getURI('cutbranch/'); 77 - 78 - // Pushers info 79 - $pushers_info = array(); 80 - $pushers = $releeph_project->getPushers(); 81 - require_celerity_resource('releeph-project'); 82 - if ($pushers) { 83 - $pushers_info[] = phutil_tag('h2', array(), 'Pushers'); 84 - foreach ($pushers as $user_phid) { 85 - $handle = $handles[$user_phid]; 86 - $div = phutil_tag( 87 - 'div', 88 - array( 89 - 'class' => 'releeph-pusher', 90 - 'style' => 'background-image: url('.$handle->getImageURI().');', 91 - ), 92 - phutil_tag( 93 - 'div', 94 - array( 95 - 'class' => 'releeph-pusher-body', 96 - ), 97 - $handles[$user_phid]->renderLink())); 98 - $pushers_info[] = $div; 99 - } 100 - 101 - $pushers_info[] = hsprintf('<div style="clear: both;"></div>'); 102 - } 103 - 104 - // Put it all together 105 - $panel = id(new AphrontPanelView()) 106 - ->setHeader($header) 107 - ->appendChild(phutil_implode_html('', $pushers_info)); 108 - 109 - foreach ($releeph_branches as $ii => $releeph_branch) { 110 - $box = id(new ReleephBranchBoxView()) 111 - ->setUser($this->user) 112 - ->setHandles($handles) 113 - ->setReleephBranch($releeph_branch) 114 - ->setNamed(); 115 - 116 - if ($ii === 0) { 117 - $box->setLatest(); 118 - } 119 - $panel->appendChild($box); 120 - } 121 - 122 - return $panel->render(); 123 - } 124 - 125 - /** 126 - * Sort branches by the point at which they were cut, newest cut points 127 - * first. 128 - * 129 - * If branches share a cut point, sort newest branch first. 130 - */ 131 - private static function sortBranches($branches, $handles) { 132 - // Group by commit phid 133 - $groups = mgroup($branches, 'getCutPointCommitPHID'); 134 - 135 - // Convert commit phid to a commit timestamp 136 - $ar = array(); 137 - foreach ($groups as $cut_phid => $group) { 138 - $handle = $handles[$cut_phid]; 139 - // Pack (timestamp, group-with-this-timestamp) pairs into $ar 140 - $ar[] = array( 141 - $handle->getTimestamp(), 142 - msort($group, 'getDateCreated') 143 - ); 144 - } 145 - 146 - $branches = array(); 147 - // Sort by timestamp, pull groups, and flatten into one big group 148 - foreach (ipull(isort($ar, 0), 1) as $group) { 149 - $branches = array_merge($branches, $group); 150 - } 151 - 152 - return array_reverse($branches); 153 - } 154 - 155 - }
+5
src/infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php
··· 662 662 'configuration sources: %s.', 663 663 ), 664 664 665 + '%d Open Pull Request(s)' => array( 666 + '%d Open Pull Request', 667 + '%d Open Pull Requests', 668 + ), 669 + 665 670 ); 666 671 } 667 672