From 210e30c257fa7bc894611eca9adbf79b6a07433e Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 16 Aug 2013 18:55:35 -0700 Subject: [PATCH] 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 --- src/__phutil_library_map__.php | 10 +- .../PhabricatorApplicationReleeph.php | 3 +- .../branch/ReleephBranchAccessController.php | 38 ++-- .../project/ReleephProjectViewController.php | 191 +++++++++++++++--- .../releeph/query/ReleephBranchQuery.php | 35 ++++ .../query/ReleephBranchSearchEngine.php | 94 +++++++++ .../releeph/view/ReleephProjectView.php | 155 -------------- .../PhabricatorBaseEnglishTranslation.php | 5 + 8 files changed, 319 insertions(+), 212 deletions(-) create mode 100644 src/applications/releeph/query/ReleephBranchSearchEngine.php delete mode 100644 src/applications/releeph/view/ReleephProjectView.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 7c6243cd9e..f36047b3b4 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1946,6 +1946,7 @@ phutil_register_library_map(array( 'ReleephBranchNamePreviewController' => 'applications/releeph/controller/branch/ReleephBranchNamePreviewController.php', 'ReleephBranchPreviewView' => 'applications/releeph/view/branch/ReleephBranchPreviewView.php', 'ReleephBranchQuery' => 'applications/releeph/query/ReleephBranchQuery.php', + 'ReleephBranchSearchEngine' => 'applications/releeph/query/ReleephBranchSearchEngine.php', 'ReleephBranchTemplate' => 'applications/releeph/view/branch/ReleephBranchTemplate.php', 'ReleephBranchViewController' => 'applications/releeph/controller/branch/ReleephBranchViewController.php', 'ReleephCommitFinder' => 'applications/releeph/commitfinder/ReleephCommitFinder.php', @@ -1979,7 +1980,6 @@ phutil_register_library_map(array( 'ReleephProjectListController' => 'applications/releeph/controller/project/ReleephProjectListController.php', 'ReleephProjectQuery' => 'applications/releeph/query/ReleephProjectQuery.php', 'ReleephProjectSearchEngine' => 'applications/releeph/query/ReleephProjectSearchEngine.php', - 'ReleephProjectView' => 'applications/releeph/view/ReleephProjectView.php', 'ReleephProjectViewController' => 'applications/releeph/controller/project/ReleephProjectViewController.php', 'ReleephReasonFieldSpecification' => 'applications/releeph/field/specification/ReleephReasonFieldSpecification.php', 'ReleephRequest' => 'applications/releeph/storage/ReleephRequest.php', @@ -4128,6 +4128,7 @@ phutil_register_library_map(array( 'ReleephBranchNamePreviewController' => 'ReleephController', 'ReleephBranchPreviewView' => 'AphrontFormControl', 'ReleephBranchQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'ReleephBranchSearchEngine' => 'PhabricatorApplicationSearchEngine', 'ReleephBranchViewController' => array( 0 => 'ReleephProjectController', @@ -4172,8 +4173,11 @@ phutil_register_library_map(array( ), 'ReleephProjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'ReleephProjectSearchEngine' => 'PhabricatorApplicationSearchEngine', - 'ReleephProjectView' => 'AphrontView', - 'ReleephProjectViewController' => 'ReleephProjectController', + 'ReleephProjectViewController' => + array( + 0 => 'ReleephProjectController', + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', + ), 'ReleephReasonFieldSpecification' => 'ReleephFieldSpecification', 'ReleephRequest' => array( diff --git a/src/applications/releeph/application/PhabricatorApplicationReleeph.php b/src/applications/releeph/application/PhabricatorApplicationReleeph.php index 1b73f90d9c..138003bcb7 100644 --- a/src/applications/releeph/application/PhabricatorApplicationReleeph.php +++ b/src/applications/releeph/application/PhabricatorApplicationReleeph.php @@ -38,8 +38,7 @@ final class PhabricatorApplicationReleeph extends PhabricatorApplication { '(?:query/(?P[^/]+)/)?' => 'ReleephProjectListController', 'create/' => 'ReleephProjectCreateController', '(?P[1-9]\d*)/' => array( - '' => 'ReleephProjectViewController', - 'closedbranches/' => 'ReleephProjectViewController', + '(?:query/(?P[^/]+)/)?' => 'ReleephProjectViewController', 'edit/' => 'ReleephProjectEditController', 'cutbranch/' => 'ReleephBranchCreateController', 'action/(?P.+)/' => 'ReleephProjectActionController', diff --git a/src/applications/releeph/controller/branch/ReleephBranchAccessController.php b/src/applications/releeph/controller/branch/ReleephBranchAccessController.php index 659fecc649..8ad524aa4d 100644 --- a/src/applications/releeph/controller/branch/ReleephBranchAccessController.php +++ b/src/applications/releeph/controller/branch/ReleephBranchAccessController.php @@ -10,23 +10,28 @@ final class ReleephBranchAccessController extends ReleephProjectController { } public function processRequest() { - $rph_branch = $this->getReleephBranch(); + $branch = $this->getReleephBranch(); $request = $this->getRequest(); - $active_uri = '/releeph/project/'.$rph_branch->getReleephProjectID().'/'; - $inactive_uri = $active_uri.'inactive/'; + $done_uri = '/releeph/project/'.$branch->getReleephProjectID().'/'; switch ($this->action) { case 'close': $is_active = false; - $origin_uri = $active_uri; + $title_text = pht('Close Branch'); + $body_text = pht( + 'Really close the branch "%s"?', + $branch->getBasename()); + $button_text = pht('Close Branch'); break; - case 're-open': $is_active = true; - $origin_uri = $inactive_uri; + $title_text = pht('Reopen Branch'); + $body_text = pht( + 'Really reopen the branch "%s"?', + $branch->getBasename()); + $button_text = pht('Reopen Branch'); break; - default: throw new Exception("Unknown action '{$this->action}'!"); break; @@ -35,26 +40,19 @@ final class ReleephBranchAccessController extends ReleephProjectController { if ($request->isDialogFormPost()) { id(new ReleephBranchEditor()) ->setActor($request->getUser()) - ->setReleephBranch($rph_branch) + ->setReleephBranch($branch) ->changeBranchAccess($is_active ? 1 : 0); - return id(new AphrontRedirectResponse()) - ->setURI($origin_uri); + + return id(new AphrontReloadResponse())->setURI($done_uri); } - $button_text = pht('%s Branch', $this->action); - $text = pht('Really %s the branch: %s?', - $this->action, - $rph_branch->getBasename()); - $message = phutil_tag('p', array(), $text); - - $dialog = new AphrontDialogView(); $dialog ->setUser($request->getUser()) - ->setTitle(pht('Confirm')) - ->appendChild($message) + ->setTitle($title_text) + ->appendChild($body_text) ->addSubmitButton($button_text) - ->addCancelButton($origin_uri); + ->addCancelButton($done_uri); return id(new AphrontDialogResponse())->setDialog($dialog); } diff --git a/src/applications/releeph/controller/project/ReleephProjectViewController.php b/src/applications/releeph/controller/project/ReleephProjectViewController.php index 03ee40ba64..1ae62b0175 100644 --- a/src/applications/releeph/controller/project/ReleephProjectViewController.php +++ b/src/applications/releeph/controller/project/ReleephProjectViewController.php @@ -1,45 +1,172 @@ queryKey = idx($data, 'queryKey'); + } public function processRequest() { - // Load all branches - $releeph_project = $this->getReleephProject(); - $releeph_branches = id(new ReleephBranch()) - ->loadAllWhere('releephProjectID = %d', - $releeph_project->getID()); + $request = $this->getRequest(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($this->queryKey) + ->setSearchEngine( + id(new ReleephBranchSearchEngine()) + ->setProjectID($this->getReleephProject()->getID())) + ->setNavigation($this->buildSideNavView()); - $path = $this->getRequest()->getRequestURI()->getPath(); - $is_open_branches = strpos($path, 'closedbranches/') === false; + return $this->delegateToController($controller); + } - $view = id(new ReleephProjectView()) - ->setShowOpenBranches($is_open_branches) - ->setUser($this->getRequest()->getUser()) - ->setReleephProject($releeph_project) - ->setBranches($releeph_branches); + public function renderResultsList( + array $branches, + PhabricatorSavedQuery $saved) { + assert_instances_of($branches, 'ReleephBranch'); - $crumbs = $this->buildApplicationCrumbs() - ->addCrumb( - id(new PhabricatorCrumbView()) - ->setName($releeph_project->getName()) - ->setHref($releeph_project->getURI())); + $viewer = $this->getRequest()->getUser(); - if ($releeph_project->getIsActive()) { - $crumbs->addAction( - id(new PHUIListItemView()) - ->setHref($releeph_project->getURI('cutbranch')) - ->setName(pht('Cut New Branch')) - ->setIcon('create')); + $projects = mpull($branches, 'getProject'); + $repo_phids = mpull($projects, 'getRepositoryPHID'); + + $repos = id(new PhabricatorRepositoryQuery()) + ->setViewer($viewer) + ->withPHIDs($repo_phids) + ->execute(); + $repos = mpull($repos, null, 'getPHID'); + + $phids = mpull($branches, 'getCreatedByUserPHID'); + $this->loadHandles($phids); + + $requests = array(); + if ($branches) { + $requests = id(new ReleephRequestQuery()) + ->setViewer($viewer) + ->withBranchIDs(mpull($branches, 'getID')) + ->withStatus(ReleephRequestQuery::STATUS_OPEN) + ->execute(); + $requests = mgroup($requests, 'getBranchID'); } - return $this->buildStandardPageResponse( - array( - $crumbs, - $view, - ), - array( - 'title' => $releeph_project->getName() - )); + $list = id(new PhabricatorObjectItemListView()) + ->setUser($viewer); + foreach ($branches as $branch) { + $diffusion_href = null; + $repo = idx($repos, $branch->getProject()->getRepositoryPHID()); + if ($repo) { + $drequest = DiffusionRequest::newFromDictionary( + array( + 'user' => $viewer, + 'repository' => $repo, + )); + + $diffusion_href = $drequest->generateURI( + array( + 'action' => 'branch', + 'branch' => $branch->getName(), + )); + } + + $branch_link = $branch->getName(); + if ($diffusion_href) { + $branch_link = phutil_tag( + 'a', + array( + 'href' => $diffusion_href, + ), + $branch_link); + } + + $item = id(new PhabricatorObjectItemView()) + ->setHeader($branch->getDisplayName()) + ->setHref($branch->getURI()) + ->addAttribute($branch_link); + + $item->addAction( + id(new PHUIListItemView()) + ->setIcon('edit') + ->setHref($branch->getURI('edit/'))); + + if ($branch->getIsActive()) { + $item->setBarColor('blue'); + $item->addAction( + id(new PHUIListItemView()) + ->setIcon('delete') + ->setWorkflow(true) + ->setHref($branch->getURI('close/'))); + } else { + $item->setDisabled(true); + $item->addAction( + id(new PHUIListItemView()) + ->setIcon('enable') + ->setWorkflow(true) + ->setHref($branch->getURI('re-open/'))); + } + + $commit = $branch->getCutPointCommit(); + if ($commit) { + $item->addIcon( + 'none', + phabricator_datetime($commit->getEpoch(), $viewer)); + } + + $open_count = count(idx($requests, $branch->getID(), array())); + if ($open_count) { + $item->setBarColor('orange'); + $item->addIcon( + 'fork', + pht('%d Open Pull Request(s)', new PhutilNumber($open_count))); + } + + $list->addItem($item); + } + + return $list; + } + + public function buildSideNavView($for_app = false) { + $user = $this->getRequest()->getUser(); + + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); + + if ($for_app) { + $nav->addFilter('project/create/', pht('Create Project')); + } + + id(new ReleephBranchSearchEngine()) + ->setProjectID($this->getReleephProject()->getID()) + ->setViewer($user) + ->addNavigationItems($nav->getMenu()); + + $nav->selectFilter(null); + + return $nav; + } + + public function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $project = $this->getReleephProject(); + + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName($project->getName())); + + $crumbs->addAction( + id(new PHUIListItemView()) + ->setHref($project->getURI('cutbranch')) + ->setName(pht('Cut New Branch')) + ->setIcon('create')); + + return $crumbs; } } diff --git a/src/applications/releeph/query/ReleephBranchQuery.php b/src/applications/releeph/query/ReleephBranchQuery.php index 727d5a3617..c1ab888fea 100644 --- a/src/applications/releeph/query/ReleephBranchQuery.php +++ b/src/applications/releeph/query/ReleephBranchQuery.php @@ -5,6 +5,11 @@ final class ReleephBranchQuery private $ids; private $phids; + private $projectIDs; + + const STATUS_ALL = 'status-all'; + const STATUS_OPEN = 'status-open'; + private $status = self::STATUS_ALL; private $needCutPointCommits; @@ -23,6 +28,16 @@ final class ReleephBranchQuery return $this; } + public function withStatus($status) { + $this->status = $status; + return $this; + } + + public function withProjectIDs(array $ids) { + $this->projectIDs = $ids; + return $this; + } + public function loadPage() { $table = new ReleephBranch(); $conn_r = $table->establishConnection('r'); @@ -89,6 +104,26 @@ final class ReleephBranchQuery $this->phids); } + if ($this->projectIDs) { + $where[] = qsprintf( + $conn_r, + 'releephProjectID IN (%Ld)', + $this->projectIDs); + } + + $status = $this->status; + switch ($status) { + case self::STATUS_ALL: + break; + case self::STATUS_OPEN: + $where[] = qsprintf( + $conn_r, + 'isActive = 1'); + break; + default: + throw new Exception("Unknown status constant '{$status}'!"); + } + $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); diff --git a/src/applications/releeph/query/ReleephBranchSearchEngine.php b/src/applications/releeph/query/ReleephBranchSearchEngine.php new file mode 100644 index 0000000000..592e0dab8d --- /dev/null +++ b/src/applications/releeph/query/ReleephBranchSearchEngine.php @@ -0,0 +1,94 @@ +projectID = $project_id; + return $this; + } + + public function getProjectID() { + return $this->projectID; + } + + public function buildSavedQueryFromRequest(AphrontRequest $request) { + $saved = new PhabricatorSavedQuery(); + + $saved->setParameter('active', $request->getStr('active')); + + return $saved; + } + + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { + $query = id(new ReleephBranchQuery()) + ->needCutPointCommits(true) + ->withProjectIDs(array($this->getProjectID())); + + $active = $saved->getParameter('active'); + $value = idx($this->getActiveValues(), $active); + if ($value !== null) { + $query->withStatus($value); + } + + return $query; + } + + public function buildSearchForm( + AphrontFormView $form, + PhabricatorSavedQuery $saved_query) { + + $form->appendChild( + id(new AphrontFormSelectControl()) + ->setName('active') + ->setLabel(pht('Show Branches')) + ->setValue($saved_query->getParameter('active')) + ->setOptions($this->getActiveOptions())); + } + + protected function getURI($path) { + return '/releeph/project/'.$this->getProjectID().'/'.$path; + } + + public function getBuiltinQueryNames() { + $names = array( + 'open' => pht('Open'), + 'all' => pht('All'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'open': + return $query + ->setParameter('active', 'open'); + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + private function getActiveOptions() { + return array( + 'open' => pht('Open Branches'), + 'all' => pht('Open and Closed Branches'), + ); + } + + private function getActiveValues() { + return array( + 'open' => ReleephBranchQuery::STATUS_OPEN, + 'all' => ReleephBranchQuery::STATUS_ALL, + ); + } + +} diff --git a/src/applications/releeph/view/ReleephProjectView.php b/src/applications/releeph/view/ReleephProjectView.php deleted file mode 100644 index 49f17d135d..0000000000 --- a/src/applications/releeph/view/ReleephProjectView.php +++ /dev/null @@ -1,155 +0,0 @@ -showOpenBranches = $active; - return $this; - } - - public function setReleephProject($releeph_project) { - $this->releephProject = $releeph_project; - return $this; - } - - public function setBranches($branches) { - $this->releephBranches = $branches; - return $this; - } - - public function render() { - $releeph_project = $this->releephProject; - - if ($this->showOpenBranches) { - $releeph_branches = mfilter($this->releephBranches, 'getIsActive'); - } else { - $releeph_branches = mfilter($this->releephBranches, 'getIsActive', true); - } - - // Load all relevant PHID handles - $phids = array_merge( - array( - $this->releephProject->getPHID(), - $this->releephProject->getRepositoryPHID(), - ), - mpull($releeph_branches, 'getCreatedByUserPHID'), - mpull($releeph_branches, 'getCutPointCommitPHID'), - $releeph_project->getPushers()); - $handles = id(new PhabricatorObjectHandleData($phids)) - ->setViewer($this->getUser()) - ->loadHandles(); - - // Sort branches, which requires the handles above - $releeph_branches = self::sortBranches($releeph_branches, $handles); - - // The header - $repository_phid = $releeph_project->getRepositoryPHID(); - - $header = hsprintf( - '%s in %s repository', - $releeph_project->getName(), - $handles[$repository_phid]->renderLink()); - - if ($this->showOpenBranches) { - $view_other_link = phutil_tag( - 'a', - array( - 'href' => $releeph_project->getURI('closedbranches/'), - ), - 'View closed branches'); - } else { - $view_other_link = phutil_tag( - 'a', - array( - 'href' => $releeph_project->getURI(), - ), - 'View open branches'); - } - - $header = hsprintf("%s · %s", $header, $view_other_link); - - // The "create branch" button - $create_branch_url = $releeph_project->getURI('cutbranch/'); - - // Pushers info - $pushers_info = array(); - $pushers = $releeph_project->getPushers(); - require_celerity_resource('releeph-project'); - if ($pushers) { - $pushers_info[] = phutil_tag('h2', array(), 'Pushers'); - foreach ($pushers as $user_phid) { - $handle = $handles[$user_phid]; - $div = phutil_tag( - 'div', - array( - 'class' => 'releeph-pusher', - 'style' => 'background-image: url('.$handle->getImageURI().');', - ), - phutil_tag( - 'div', - array( - 'class' => 'releeph-pusher-body', - ), - $handles[$user_phid]->renderLink())); - $pushers_info[] = $div; - } - - $pushers_info[] = hsprintf('
'); - } - - // Put it all together - $panel = id(new AphrontPanelView()) - ->setHeader($header) - ->appendChild(phutil_implode_html('', $pushers_info)); - - foreach ($releeph_branches as $ii => $releeph_branch) { - $box = id(new ReleephBranchBoxView()) - ->setUser($this->user) - ->setHandles($handles) - ->setReleephBranch($releeph_branch) - ->setNamed(); - - if ($ii === 0) { - $box->setLatest(); - } - $panel->appendChild($box); - } - - return $panel->render(); - } - - /** - * Sort branches by the point at which they were cut, newest cut points - * first. - * - * If branches share a cut point, sort newest branch first. - */ - private static function sortBranches($branches, $handles) { - // Group by commit phid - $groups = mgroup($branches, 'getCutPointCommitPHID'); - - // Convert commit phid to a commit timestamp - $ar = array(); - foreach ($groups as $cut_phid => $group) { - $handle = $handles[$cut_phid]; - // Pack (timestamp, group-with-this-timestamp) pairs into $ar - $ar[] = array( - $handle->getTimestamp(), - msort($group, 'getDateCreated') - ); - } - - $branches = array(); - // Sort by timestamp, pull groups, and flatten into one big group - foreach (ipull(isort($ar, 0), 1) as $group) { - $branches = array_merge($branches, $group); - } - - return array_reverse($branches); - } - -} diff --git a/src/infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php b/src/infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php index 06de456159..fee2a0b772 100644 --- a/src/infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php +++ b/src/infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php @@ -662,6 +662,11 @@ abstract class PhabricatorBaseEnglishTranslation 'configuration sources: %s.', ), + '%d Open Pull Request(s)' => array( + '%d Open Pull Request', + '%d Open Pull Requests', + ), + ); }