diff --git a/src/extensions/PhabricatorC4sHome.php b/src/extensions/PhabricatorC4sHome.php index c7c122692..05b6764be 100644 --- a/src/extensions/PhabricatorC4sHome.php +++ b/src/extensions/PhabricatorC4sHome.php @@ -1,706 +1,708 @@ getViewer(); if($request->getPath() == '/home/render/feed') { require_celerity_resource('phabricator-dashboard-css'); $content = $this->getFeedContent($request); return id(new AphrontAjaxResponse()) ->setContent(array( 'panelMarkup' => hsprintf('%s', $content), )); } else if(!$viewer->getUsername() || $request->getPath() == '/public') { $view = $this->publicPage($request, $viewer); } else { $view = $this->userPage($request, $viewer); } $navigation = $this->buildSideNavView($request); $page = $this->newPage() ->setNavigation($navigation) ->appendChild($view); // On mobile, / is the menu and /home is the content $is_content = $request->getURIData('content'); if (!$is_content) { $page->addClass('phabricator-home'); } return $page; } private function userPage($request, $viewer) { $tab_key = $request->getStr('tab'); $order_key = $request->getStr('order', 'committed'); // Get and check order key $repo_order = id(new PhabricatorRepositoryQuery()) ->getBuiltinOrders(); unset($repo_order['callsign']); //FIXME: for some reason, ordering with // the calllsign field doesn't work // and crashes, not very usefull though if(!in_array($order_key, array_keys($repo_order))) { $order_key = 'committed'; } // My repo tab (can_edit + created) $view_my = $this->buildRepositoryPanel( $request, 'my', null, 10, false, $order_key, 'author'); $tab_my = id(new PHUITabView()) ->setName(pht('Your repositories')) ->setKey('my') ->appendChild($view_my); // Contribution tab (can_push) $view_contrib = $this->buildRepositoryPanel( $request, 'contrib', null, 10, false, $order_key, 'contrib'); $tab_contrib = id(new PHUITabView()) ->setName(pht('Contribution')) ->setKey('contrib') ->appendChild($view_contrib); // Tab Repositories $tab_group_repo = id(new PHUITabGroupView()) ->addTab($tab_my) ->addTab($tab_contrib); if(in_array($tab_key, array('my', 'contrib'))) { $tab_group_repo->selectTab($tab_key); } // Box header $actions = id(new PhabricatorActionListView()) ->setViewer($viewer); foreach($repo_order as $orderKey => $orderValue) { $action = id(new PhabricatorActionView()) ->setName($orderValue['name']) ->setHref('?order=' . $orderKey . '#box_repo'); if($order_key == $orderKey) { $action->setSelected(true); } $actions->addAction($action); } $order_link = id(new PHUIButtonView()) ->setText('Order By: ' . $order_key) ->setTag('a') ->setIcon('fa-sort') ->setDropdown(true) ->setDropdownMenu($actions); $header = id(new PHUIHeaderView()) ->setHeader(pht('Repositories')) ->addActionLink($order_link); // Box $anchor = id(new PhabricatorAnchorView()) ->setAnchorName('box_repo'); $tab_repo = id(new PHUIObjectBoxView()) ->setHeader($header) ->setAnchor($anchor) ->addTabGroup($tab_group_repo); // Projects member $view_member = $this->buildProjectsPanel($request, 'member', 'member'); $tab_member = id(new PHUITabView()) ->setName(pht('Member')) ->setKey('member') ->appendChild($view_member); // Projects watcher $view_watcher = $this->buildProjectsPanel($request, 'watcher', 'watcher'); $tab_watcher = id(new PHUITabView()) ->setName(pht('Watcher')) ->setKey('watcher') ->appendChild($view_watcher); // Tab Projects $tab_group_proj = id(new PHUITabGroupView()) ->addTab($tab_member) ->addTab($tab_watcher); if(in_array($tab_key, array('member', 'watcher'))) { $tab_group_proj->selectTab($tab_key); } $anchor = id(new PhabricatorAnchorView()) ->setAnchorName('box_proj'); $tab_proj = id(new PHUIObjectBoxView()) ->setHeaderText('Projects') ->setAnchor($anchor) ->addTabGroup($tab_group_proj); // Columns $col_repo = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( $tab_repo, $tab_proj, )); $col_feed = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( //$this->buildShortcuts(), $this->buildAnnouncement(), $this->buildFeedPanel($request, false), )); $view = id(new AphrontMultiColumnView()) ->addColumn($col_repo) ->addColumn($col_feed) ->setFluidLayout(true); return $view; } private function publicPage($request, $viewer) { $col_feed = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( $this->buildAnnouncement(), $this->buildFeedPanel($request), )); $view_active = $this->buildRepositoryPanel($request, 'active', null, 10); $tab_active = id(new PHUITabView()) ->setName(pht('Recently Active')) ->setKey('active') ->appendChild($view_active); $view_commits = $this->buildRepositoryPanel($request, 'commits', null, 10, true, 'size'); $tab_commits = id(new PHUITabView()) ->setName(pht('Most commits')) ->setKey('commits') ->appendChild($view_commits); $view_users = $this->buildRepositoryPanel($request, 'users', null, 10, true, 'users'); $tab_users = id(new PHUITabView()) ->setName(pht('Most contributors')) ->setKey('users') ->appendChild($view_users); $tab_group = id(new PHUITabGroupView()) ->addTab($tab_active) ->addTab($tab_commits) ->addTab($tab_users); $tab_key = $request->getStr('tab'); if(in_array($tab_key, array('my', 'contrib'))) { $tab_group->selectTab($tab_key); } $anchor = id(new PhabricatorAnchorView()) ->setAnchorName('box_repo'); $tab_view = id(new PHUIObjectBoxView()) ->setHeaderText('Repositories') ->setAnchor($anchor) ->addTabGroup($tab_group); $col_repo = phutil_tag( 'div', array( 'class' => 'homepage-panel', ), array( $this->buildPresentationPanel(), $tab_view, )); $view = id(new AphrontMultiColumnView()) ->addColumn($col_repo) ->addColumn($col_feed) ->setFluidLayout(true); return $view; } private function buildAnnouncement() { $viewer = $this->getViewer(); $dashboard = id(new PhabricatorDashboard()) ->loadOneWhere('name = "Service Announcement" && editPolicy = "admin"'); if(!$dashboard) { return; } $dashboard = id(new PhabricatorDashboardQuery()) ->setViewer($viewer) ->withPHIDs(array($dashboard->getPHID())) ->needPanels(true) ->executeOne(); if(!$dashboard->getPanelPHIDs()) { return; } $panels = id(new PhabricatorDashboardPanelQuery()) ->setViewer($viewer) ->withPHIDs($dashboard->getPanelPHIDs()) ->execute(); $boxes = array(); foreach($panels as $panel) { if($panel->getPanelType() != 'text') { continue; } $remarkup = new PHUIRemarkupView($viewer, $panel->getProperty('text')); $layout = id(new PHUIBoxView()) ->appendChild($remarkup) ->addMargin(PHUI::MARGIN_MEDIUM); $boxes[] = id(new PHUIObjectBoxView()) ->setHeaderText($panel->getName()) ->appendChild($layout); } return $boxes; } private function buildShortcuts() { $icons = array( id(new PHUIIconView()) ->setIcon('fa-briefcase') ->setBackground('bg-grey') ->setTooltip('Create Project') ->addClass('mmr') ->setHref('/project/edit/form/default/'), id(new PHUIIconView()) ->setIcon('fa-code') ->setBackground('bg-grey') ->setTooltip('Create Repository') ->addClass('mmr') ->setHref('/diffusion/edit/form/default/'), id(new PHUIIconView()) ->setIcon('fa-bug') ->setBackground('bg-grey') ->setTooltip('Report a bug') ->addClass('mmr') ->setHref('/maniphest/task/edit/form/8/'), id(new PHUIIconView()) ->setIcon('fa-book') ->setBackground('bg-grey') ->setTooltip('Documentation') ->addClass('mmr') ->setHref('/w/c4science') ); $layout = id(new PHUIBoxView()) ->appendChild($icons) ->addMargin(PHUI::MARGIN_MEDIUM); $box = id(new PHUIObjectBoxView()) ->setHeaderText('Shortcuts') ->appendChild($layout); return $box; } private function getFeedContent($request) { // hack to have global in the request $global = $request->getBool('dashboardID', true); $viewer = $this->getViewer(); $phids = array(); $epoch_min = time() - 7*24*3600; // 7 days $epoch_max = time(); if(!$global) { //User $phids += array($viewer->getPHID()); // Projects $projects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withMemberPHIDs(array($viewer->getPHID())) ->execute(); $project_phids = mpull($projects, 'getPHID'); $phids += array_fuse($project_phids); // Repositories (created by user) $repo_transaction = id(new PhabricatorRepositoryTransactionQuery()) ->setViewer($viewer) ->withAuthorPHIDs(array($viewer->getPHID())) ->withTransactionTypes(array(PhabricatorTransactions::TYPE_CREATE)) ->needComments(false) ->needHandles(false) ->execute(); $repo = array_fuse(mpull($repo_transaction, 'getObjectPHID')); // Repositories (in project user is member of) if(!empty($projects)) { $any = array(); foreach($project_phids as $phid) { $any[] = 'any(' . $phid . ')'; } $datasource = id(new PhabricatorProjectLogicalDatasource()) ->setViewer($viewer); $constraints = $datasource->evaluateTokens($any); $query = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->withEdgeLogicConstraints( PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, $constraints) ->execute(); $repo += array_fuse(mpull($query, 'getPHID')); } // Commits for selected Repositories if(!empty($repo)) { $commits = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->withRepositoryPHIDs($repo) ->withEpochRange($epoch_min, $epoch_max) ->execute(); $phids += array_fuse(mpull($commits, 'getPHID')); } // Phriction pages if(!empty($projects)) { $slugs = array(); foreach($projects as $p) { $slugs[] = PhabricatorProjectWikiCreate::getAllSlugs($p); } $wiki = id(new PhrictionDocumentQuery()) ->setViewer($viewer) ->withSlugsPrefix($slugs) ->withStatus(PhrictionDocumentQuery::STATUS_OPEN) ->execute(); $phids += array_fuse(mpull($wiki, 'getPHID')); } } // Build Feed stories $stories = id(new PhabricatorFeedQuery()) ->setViewer($viewer) ->withFilterPHIDs($phids) ->withEpochInRange($epoch_min, $epoch_max) ->setLimit(12) ->execute(); $view = id(new PhabricatorFeedBuilder($stories)) ->setUser($viewer) ->buildView(); return $view; } private function buildFeedPanel($request, $global=true) { $panel_id = celerity_generate_unique_node_id(); Javelin::initBehavior( 'dashboard-async-panel', array( 'panelID' => $panel_id, 'parentPanelPHIDs' => array(), 'headerMode' => 'none', 'dashboardID' => $global, 'uri' => '/home/render/feed', )); $view_all = id(new PHUIButtonView()) ->setTag('a') ->setIcon(id(new PHUIIconView())->setIcon('fa-list-ul')) ->setText(pht('View All')) ->setHref('/feed/'); $header = id(new PHUIHeaderView()) ->setHeader(pht('Recent Activity')) ->addActionLink($view_all); $feed_box = id(new PHUIBoxView()) ->setID($panel_id) ->addPadding(PHUI::PADDING_LARGE) ->appendChild(pht('Loading...')); $feed = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($feed_box); return $feed; } private function buildProjectsPanel($request, $tab_key, $filter='member', $proj_count=10) { $viewer = $this->getViewer(); $pager = id(new AphrontCursorPagerView()) ->setURI(new PhutilURI('?tab=' . $tab_key . '#box_proj')) ->setPageSize($proj_count); if($tab_key == $request->getStr('tab') && in_array($tab_key, array('member', 'watcher'))) { $pager ->setAfterID($request->getStr('after')) ->setBeforeID($request->getStr('before')); } $projects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->setOrder('newest') ->needImages(true) ->withStatuses(array(PhabricatorProjectStatus::STATUS_ACTIVE)); if($filter == 'member') { $projects->withMemberPHIDs(array($viewer->getPHID())); } else if($filter == 'watcher') { $projects->withWatcherPHIDs(array($viewer->getPHID())); } $projects = $projects->executeWithCursorPager($pager); if (!empty($projects)) { $list = id(new PhabricatorProjectListView()) ->setUser($viewer) ->setProjects($projects); } else { if($filter == 'member') { $str = 'No project found. [[/project/edit/form/default/ | Create a new project]].'; } else if($filter == 'watcher') { $str = 'No project found. [[/project/query/active/ | Watch a project]].'; } $remarkup = new PHUIRemarkupView($viewer, $str); $list = id(new PHUIBoxView()) ->addPadding(PHUI::PADDING_LARGE) ->appendChild($remarkup); } $box = id(new PHUIObjectBoxView()) ->appendChild($list); if($pager->willShowPagingControls()) { $box->appendChild($pager); } return $box; } private function buildRepositoryPanel( $request, $tab_key, $title, $repo_count=5, $compact=false, $order='committed', $filter=null) { $viewer = $this->getViewer(); $pager = id(new AphrontCursorPagerView()) ->setURI(new PhutilURI('?tab=' . $tab_key . '&order=' . $order . '#box_repo')) ->setPageSize($repo_count); if($tab_key == $request->getStr('tab') && in_array($tab_key, array('my', 'contrib'))) { $pager ->setAfterID($request->getStr('after')) ->setBeforeID($request->getStr('before')); } // Get repositories if($order == 'users') { $table = id(new PhabricatorRepositoryCommit()); $repo = queryfx_all( $table->establishConnection('r'), 'SELECT a.name, a.rid, count(*) AS cnt, a.phid FROM (SELECT r.phid as phid, r.name AS name, repositoryID AS rid, authorPHID AS aid FROM repository_commit JOIN repository AS r ON r.id=repositoryID WHERE viewPolicy="public" AND authorPHID IS NOT NULL AND importStatus=15 GROUP BY aid)a GROUP BY a.rid ORDER BY cnt DESC LIMIT %d', $repo_count); } else { $repo = id(new PhabricatorRepositoryQuery()) ->setViewer($viewer) ->setOrder($order) ->needCommitCounts(true); if(!$compact) { $repo ->needMostRecentCommits(true) ->needProjectPHIDs(true) ->needProfileImage(true); } + $capabilities = array(PhabricatorPolicyCapability::CAN_VIEW); if($filter == 'contrib') { - $repo->requireCapabilities(array(DiffusionPushCapability::CAPABILITY)); + $capabilities += array_fuse(array(DiffusionPushCapability::CAPABILITY)); $repo->withCanPush(); } + $repo->requireCapabilities($capabilities); //if(in_array($filter, array('author', 'contrib'))) { // $repo->withCanView(); //} if(in_array($tab_key, array('active', 'commits'))) { $repo->withCanViewPublic(); } if($filter == 'author') { $repo->withAuthors(array($viewer->getPHID())); } else if($viewer->getUsername()) { $repo->withoutAuthors(array($viewer->getPHID())); } $repo = $repo->executeWithCursorPager($pager); } // Create List if(!empty($repo)) { $list = new PHUIObjectItemListView(); } else { $remarkup = new PHUIRemarkupView($viewer, 'No repository found. [[/diffusion/edit/form/default/ | Create a new repository]].'); $list = id(new PHUIBoxView()) ->addPadding(PHUI::PADDING_LARGE) ->appendChild($remarkup); } foreach($repo as $r){ $item = id(new PHUIObjectItemView()); if($order == 'users') { $item ->setHeader($r['name']) ->addIcon('', pht('%s Users', new PhutilNumber($r['cnt']))) ->setHref(pht('/diffusion/%d/', $r['rid'])); } else { if($r->getStatus() == PhabricatorRepository::STATUS_INACTIVE) { $item->setDisabled(true); } $item ->setHeader($r->getName()) ->addIcon('', pht('%s Commit(s)', new PhutilNumber($r->getCommitCount()))) ->setHref($r->getURI()); } if(!$compact) { $item->setImageURI($r->getProfileImageURI()); $desc = id(new PhutilUTF8StringTruncator()) ->setMaximumGlyphs(120) ->truncateString($r->getDetail('description')); $desc = new PHUIRemarkupView($viewer, $desc); $item->setSubHead($desc); if($r->getMostRecentCommit()) { $item->addIcon('', phabricator_date( $r->getMostRecentCommit()->getDateCreated(), $viewer)); } if($r->getProjectPHIDs()) { $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->witHPHIDs($r->getProjectPHIDs()) ->execute(); $project_handles = array_select_keys( $handles, $r->getProjectPHIDs()); if ($project_handles) { $item->addAttribute( id(new PHUIHandleTagListView()) ->setSlim(true) ->setHandles($project_handles)); } } } $list->addItem($item); } if($pager->willShowPagingControls()) { $pager_item = id(new PHUIObjectItemView()) ->appendChild($pager); $list->addItem($pager_item); } $box = id(new PHUIObjectBoxView()) ->setObjectList($list); if($title) { $header = id(new PHUIHeaderView()) ->setHeader($title); $box->setHeader($header); } if($order == 'users') { $repo_phids = ipull($repo, 'phid'); } else { $repo_phids = mpull($repo, 'getPHID'); } return $box; } private function buildPresentationPanel() { $viewer = $this->getViewer(); $content = pht(<<appendChild($remarkup); return $box; } private function buildSideNavView(AphrontRequest $request) { $viewer = $this->getViewer(); $home = id(new PhabricatorHomeApplication()); $engine = id(new PhabricatorHomeProfileMenuEngine()) ->setViewer($viewer) ->setProfileObject($home) ->setCustomPHID($viewer->getPHID()); $is_content = $request->getURIData('content'); if (!$is_content) { $engine->addContentPageClass('phabricator-home'); } return $engine->buildNavigation(); } }