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', + ), + ); }