diff --git a/src/applications/repository/query/PhabricatorRepositoryQuery.php b/src/applications/repository/query/PhabricatorRepositoryQuery.php index 4f80d8efc..ad3d8ec47 100644 --- a/src/applications/repository/query/PhabricatorRepositoryQuery.php +++ b/src/applications/repository/query/PhabricatorRepositoryQuery.php @@ -1,848 +1,881 @@ canpush = true; return $this; } + // c4science custo + public function withCanView() { + $this->canview = true; + return $this; + } + // c4science custo public function withAuthors(array $authors) { $this->authors = $authors; return $this; } // c4science custo public function withoutAuthors(array $authors) { $this->without_authors = $authors; return $this; } public function withIDs(array $ids) { $this->ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } // c4science customization public function withoutPHIDs(array $phids) { $this->without_phids = $phids; return $this; } public function withCallsigns(array $callsigns) { $this->callsigns = $callsigns; return $this; } public function withIdentifiers(array $identifiers) { $identifiers = array_fuse($identifiers); $ids = array(); $callsigns = array(); $phids = array(); $monograms = array(); $slugs = array(); foreach ($identifiers as $identifier) { if (ctype_digit((string)$identifier)) { $ids[$identifier] = $identifier; continue; } if (preg_match('/^(r[A-Z]+|R[1-9]\d*)\z/', $identifier)) { $monograms[$identifier] = $identifier; continue; } $repository_type = PhabricatorRepositoryRepositoryPHIDType::TYPECONST; if (phid_get_type($identifier) === $repository_type) { $phids[$identifier] = $identifier; continue; } if (preg_match('/^[A-Z]+\z/', $identifier)) { $callsigns[$identifier] = $identifier; continue; } $slugs[$identifier] = $identifier; } $this->numericIdentifiers = $ids; $this->callsignIdentifiers = $callsigns; $this->phidIdentifiers = $phids; $this->monogramIdentifiers = $monograms; $this->slugIdentifiers = $slugs; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function withHosted($hosted) { $this->hosted = $hosted; return $this; } public function withTypes(array $types) { $this->types = $types; return $this; } public function withUUIDs(array $uuids) { $this->uuids = $uuids; return $this; } public function withNameContains($contains) { $this->nameContains = $contains; return $this; } public function withURIs(array $uris) { $this->uris = $uris; return $this; } public function withDatasourceQuery($query) { $this->datasourceQuery = $query; return $this; } public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; } public function withAlmanacServicePHIDs(array $phids) { $this->almanacServicePHIDs = $phids; return $this; } public function needCommitCounts($need_counts) { $this->needCommitCounts = $need_counts; return $this; } public function needMostRecentCommits($need_commits) { $this->needMostRecentCommits = $need_commits; return $this; } public function needProjectPHIDs($need_phids) { $this->needProjectPHIDs = $need_phids; return $this; } public function needURIs($need_uris) { $this->needURIs = $need_uris; return $this; } public function needProfileImage($need) { $this->needProfileImage = $need; return $this; } // c4science customization public function limitCommitCounts($limit) { $this->needCommitCounts(true); $this->limitCommitCounts = $limit; return $this; } public function getBuiltinOrders() { return array( 'committed' => array( 'vector' => array('committed', 'id'), 'name' => pht('Most Recent Commit'), ), 'name' => array( 'vector' => array('name', 'id'), 'name' => pht('Name'), ), 'callsign' => array( 'vector' => array('callsign'), 'name' => pht('Callsign'), ), 'size' => array( 'vector' => array('size', 'id'), 'name' => pht('Size'), ), ) + parent::getBuiltinOrders(); } public function getIdentifierMap() { if ($this->identifierMap === null) { throw new PhutilInvalidStateException('execute'); } return $this->identifierMap; } protected function willExecute() { $this->identifierMap = array(); } public function newResultObject() { return new PhabricatorRepository(); } protected function loadPage() { $table = $this->newResultObject(); $data = $this->loadStandardPageRows($table); $repositories = $table->loadAllFromArray($data); if ($this->needCommitCounts) { $sizes = ipull($data, 'size', 'id'); foreach ($repositories as $id => $repository) { $repository->attachCommitCount(nonempty($sizes[$id], 0)); } } if ($this->needMostRecentCommits) { $commit_ids = ipull($data, 'lastCommitID', 'id'); $commit_ids = array_filter($commit_ids); if ($commit_ids) { $commits = id(new DiffusionCommitQuery()) ->setViewer($this->getViewer()) ->withIDs($commit_ids) ->execute(); } else { $commits = array(); } foreach ($repositories as $id => $repository) { $commit = null; if (idx($commit_ids, $id)) { $commit = idx($commits, $commit_ids[$id]); } $repository->attachMostRecentCommit($commit); } } return $repositories; } protected function willFilterPage(array $repositories) { assert_instances_of($repositories, 'PhabricatorRepository'); // TODO: Denormalize repository status into the PhabricatorRepository // table so we can do this filtering in the database. foreach ($repositories as $key => $repo) { $status = $this->status; switch ($status) { case self::STATUS_OPEN: if (!$repo->isTracked()) { unset($repositories[$key]); } break; case self::STATUS_CLOSED: if ($repo->isTracked()) { unset($repositories[$key]); } break; case self::STATUS_ALL: break; default: throw new Exception("Unknown status '{$status}'!"); } // TODO: This should also be denormalized. $hosted = $this->hosted; switch ($hosted) { case self::HOSTED_PHABRICATOR: if (!$repo->isHosted()) { unset($repositories[$key]); } break; case self::HOSTED_REMOTE: if ($repo->isHosted()) { unset($repositories[$key]); } break; case self::HOSTED_ALL: break; default: throw new Exception(pht("Unknown hosted failed '%s'!", $hosted)); } } // Build the identifierMap if ($this->numericIdentifiers) { foreach ($this->numericIdentifiers as $id) { if (isset($repositories[$id])) { $this->identifierMap[$id] = $repositories[$id]; } } } if ($this->callsignIdentifiers) { $repository_callsigns = mpull($repositories, null, 'getCallsign'); foreach ($this->callsignIdentifiers as $callsign) { if (isset($repository_callsigns[$callsign])) { $this->identifierMap[$callsign] = $repository_callsigns[$callsign]; } } } if ($this->phidIdentifiers) { $repository_phids = mpull($repositories, null, 'getPHID'); foreach ($this->phidIdentifiers as $phid) { if (isset($repository_phids[$phid])) { $this->identifierMap[$phid] = $repository_phids[$phid]; } } } if ($this->monogramIdentifiers) { $monogram_map = array(); foreach ($repositories as $repository) { foreach ($repository->getAllMonograms() as $monogram) { $monogram_map[$monogram] = $repository; } } foreach ($this->monogramIdentifiers as $monogram) { if (isset($monogram_map[$monogram])) { $this->identifierMap[$monogram] = $monogram_map[$monogram]; } } } if ($this->slugIdentifiers) { $slug_map = array(); foreach ($repositories as $repository) { $slug = $repository->getRepositorySlug(); if ($slug === null) { continue; } $normal = phutil_utf8_strtolower($slug); $slug_map[$normal] = $repository; } foreach ($this->slugIdentifiers as $slug) { $normal = phutil_utf8_strtolower($slug); if (isset($slug_map[$normal])) { $this->identifierMap[$slug] = $slug_map[$normal]; } } } return $repositories; } protected function didFilterPage(array $repositories) { if ($this->needProjectPHIDs) { $type_project = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($repositories, 'getPHID')) ->withEdgeTypes(array($type_project)); $edge_query->execute(); foreach ($repositories as $repository) { $project_phids = $edge_query->getDestinationPHIDs( array( $repository->getPHID(), )); $repository->attachProjectPHIDs($project_phids); } } $viewer = $this->getViewer(); if ($this->needURIs) { $uris = id(new PhabricatorRepositoryURIQuery()) ->setViewer($viewer) ->withRepositories($repositories) ->execute(); $uri_groups = mgroup($uris, 'getRepositoryPHID'); foreach ($repositories as $repository) { $repository_uris = idx($uri_groups, $repository->getPHID(), array()); $repository->attachURIs($repository_uris); } } if ($this->needProfileImage) { $default = null; $file_phids = mpull($repositories, 'getProfileImagePHID'); $file_phids = array_filter($file_phids); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setParentQuery($this) ->setViewer($this->getViewer()) ->withPHIDs($file_phids) ->execute(); $files = mpull($files, null, 'getPHID'); } else { $files = array(); } foreach ($repositories as $repository) { $file = idx($files, $repository->getProfileImagePHID()); if (!$file) { if (!$default) { $default = PhabricatorFile::loadBuiltin( $this->getViewer(), 'repo/code.png'); } $file = $default; } $repository->attachProfileImageFile($file); } } return $repositories; } protected function getPrimaryTableAlias() { return 'r'; } public function getOrderableColumns() { return parent::getOrderableColumns() + array( 'committed' => array( 'table' => 's', 'column' => 'epoch', 'type' => 'int', 'null' => 'tail', ), 'callsign' => array( 'table' => 'r', 'column' => 'callsign', 'type' => 'string', 'unique' => true, 'reverse' => true, ), 'name' => array( 'table' => 'r', 'column' => 'name', 'type' => 'string', 'reverse' => true, ), 'size' => array( 'table' => 's', 'column' => 'size', 'type' => 'int', 'null' => 'tail', ), ); } protected function willExecuteCursorQuery( PhabricatorCursorPagedPolicyAwareQuery $query) { $vector = $this->getOrderVector(); if ($vector->containsKey('committed')) { $query->needMostRecentCommits(true); } if ($vector->containsKey('size')) { $query->needCommitCounts(true); } } protected function getPagingValueMap($cursor, array $keys) { $repository = $this->loadCursorObject($cursor); $map = array( 'id' => $repository->getID(), 'callsign' => $repository->getCallsign(), 'name' => $repository->getName(), ); foreach ($keys as $key) { switch ($key) { case 'committed': $commit = $repository->getMostRecentCommit(); if ($commit) { $map[$key] = $commit->getEpoch(); } else { $map[$key] = null; } break; case 'size': $count = $repository->getCommitCount(); if ($count) { $map[$key] = $count; } else { $map[$key] = null; } break; } } return $map; } protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) { $parts = parent::buildSelectClauseParts($conn); $parts[] = 'r.*'; if ($this->shouldJoinSummaryTable()) { $parts[] = 's.*'; } return $parts; } protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->shouldJoinSummaryTable()) { $joins[] = qsprintf( $conn, 'LEFT JOIN %T s ON r.id = s.repositoryID', PhabricatorRepository::TABLE_SUMMARY); } if ($this->shouldJoinURITable()) { $joins[] = qsprintf( $conn, 'LEFT JOIN %T uri ON r.phid = uri.repositoryPHID', id(new PhabricatorRepositoryURIIndex())->getTableName()); } // c4science custo if($this->shouldJoinTransactionTable()) { $joins[] = qsprintf( $conn, 'LEFT JOIN repository_transaction t ON ' . 'r.phid = t.objectPHID and transactionType = %s', PhabricatorTransactions::TYPE_CREATE); } + // c4science custo + if($this->canview) { + + // PLCY + $joins[] = qsprintf( + $conn, + 'LEFT JOIN phabricator_policy.policy p ON r.viewPolicy = p.phid'); + + // PROJ + $joins[] = qsprintf( + $conn, + 'LEFT JOIN phabricator_project.edge e ON r.viewPolicy = e.src ' + . 'AND e.type=%s', + PhabricatorProjectProjectHasMemberEdgeType::EDGECONST); + } + // c4science custo if($this->canpush) { // PLCY $joins[] = qsprintf( $conn, 'LEFT JOIN phabricator_policy.policy p ON r.pushPolicy = p.phid'); // PROJ $joins[] = qsprintf( $conn, 'LEFT JOIN phabricator_project.edge e ON r.pushPolicy = e.src ' . 'AND e.type=%s', PhabricatorProjectProjectHasMemberEdgeType::EDGECONST); } return $joins; } protected function shouldGroupQueryResultRows() { if ($this->shouldJoinURITable()) { return true; } return parent::shouldGroupQueryResultRows(); } private function shouldJoinURITable() { return ($this->uris !== null); } // c4science customization private function shouldJoinTransactionTable() { return ($this->authors !== null || $this->without_authors !== null); } private function shouldJoinSummaryTable() { if ($this->needCommitCounts) { return true; } if ($this->needMostRecentCommits) { return true; } $vector = $this->getOrderVector(); if ($vector->containsKey('committed')) { return true; } if ($vector->containsKey('size')) { return true; } return false; } protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( $conn, 'r.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, 'r.phid IN (%Ls)', $this->phids); } // C4science customization if ($this->without_phids !== null) { $where[] = qsprintf( $conn, 'r.phid NOT IN (%Ls)', $this->without_phids); } // C4science customization if ($this->without_authors !== null) { $where[] = qsprintf( $conn, 't.authorPHID NOT IN (%Ls)', $this->without_authors); } // C4science customization if ($this->authors !== null) { $where[] = qsprintf( $conn, 't.authorPHID IN (%Ls)', $this->authors); } + // c4science customization + if($this->canview) { + $viewer = $this->getViewer()->getPHID(); + $where[] = qsprintf( + $conn, + '(e.dst=%s OR p.rules LIKE %~ OR t.authorPHID=%s)', + $viewer, $viewer, $viewer); + } + // c4science customization if($this->canpush) { $viewer = $this->getViewer()->getPHID(); // This is a rough check, the rule can also be deny to thes user // and this doesn't take into account Projects in the policy. // The policy aware query will take care of filtering after though. $where[] = qsprintf( $conn, '(e.dst=%s OR p.rules LIKE %~)', $viewer, $viewer); } if ($this->callsigns !== null) { $where[] = qsprintf( $conn, 'r.callsign IN (%Ls)', $this->callsigns); } // c4science customization if($this->needCommitCounts && $this->limitCommitCounts > 0) { $where[] = qsprintf( $conn, 's.size >= %d', $this->limitCommitCounts); } if ($this->numericIdentifiers || $this->callsignIdentifiers || $this->phidIdentifiers || $this->monogramIdentifiers || $this->slugIdentifiers) { $identifier_clause = array(); if ($this->numericIdentifiers) { $identifier_clause[] = qsprintf( $conn, 'r.id IN (%Ld)', $this->numericIdentifiers); } if ($this->callsignIdentifiers) { $identifier_clause[] = qsprintf( $conn, 'r.callsign IN (%Ls)', $this->callsignIdentifiers); } if ($this->phidIdentifiers) { $identifier_clause[] = qsprintf( $conn, 'r.phid IN (%Ls)', $this->phidIdentifiers); } if ($this->monogramIdentifiers) { $monogram_callsigns = array(); $monogram_ids = array(); foreach ($this->monogramIdentifiers as $identifier) { if ($identifier[0] == 'r') { $monogram_callsigns[] = substr($identifier, 1); } else { $monogram_ids[] = substr($identifier, 1); } } if ($monogram_ids) { $identifier_clause[] = qsprintf( $conn, 'r.id IN (%Ld)', $monogram_ids); } if ($monogram_callsigns) { $identifier_clause[] = qsprintf( $conn, 'r.callsign IN (%Ls)', $monogram_callsigns); } } if ($this->slugIdentifiers) { $identifier_clause[] = qsprintf( $conn, 'r.repositorySlug IN (%Ls)', $this->slugIdentifiers); } $where = array('('.implode(' OR ', $identifier_clause).')'); } if ($this->types) { $where[] = qsprintf( $conn, 'r.versionControlSystem IN (%Ls)', $this->types); } if ($this->uuids) { $where[] = qsprintf( $conn, 'r.uuid IN (%Ls)', $this->uuids); } if (strlen($this->nameContains)) { $where[] = qsprintf( $conn, 'r.name LIKE %~', $this->nameContains); } if (strlen($this->datasourceQuery)) { // This handles having "rP" match callsigns starting with "P...". $query = trim($this->datasourceQuery); if (preg_match('/^r/', $query)) { $callsign = substr($query, 1); } else { $callsign = $query; } $where[] = qsprintf( $conn, 'r.name LIKE %> OR r.callsign LIKE %> OR r.repositorySlug LIKE %>', $query, $callsign, $query); } if ($this->slugs !== null) { $where[] = qsprintf( $conn, 'r.repositorySlug IN (%Ls)', $this->slugs); } if ($this->uris !== null) { $try_uris = $this->getNormalizedURIs(); $try_uris = array_fuse($try_uris); $where[] = qsprintf( $conn, 'uri.repositoryURI IN (%Ls)', $try_uris); } if ($this->almanacServicePHIDs !== null) { $where[] = qsprintf( $conn, 'r.almanacServicePHID IN (%Ls)', $this->almanacServicePHIDs); } return $where; } public function getQueryApplicationClass() { return 'PhabricatorDiffusionApplication'; } private function getNormalizedURIs() { $normalized_uris = array(); // Since we don't know which type of repository this URI is in the general // case, just generate all the normalizations. We could refine this in some // cases: if the query specifies VCS types, or the URI is a git-style URI // or an `svn+ssh` URI, we could deduce how to normalize it. However, this // would be more complicated and it's not clear if it matters in practice. $types = PhabricatorRepositoryURINormalizer::getAllURITypes(); foreach ($this->uris as $uri) { foreach ($types as $type) { $normalized_uri = new PhabricatorRepositoryURINormalizer($type, $uri); $normalized_uris[] = $normalized_uri->getNormalizedURI(); } } return array_unique($normalized_uris); } } diff --git a/src/extensions/PhabricatorC4sHome.php b/src/extensions/PhabricatorC4sHome.php index c6541951b..eef5cb9ec 100644 --- a/src/extensions/PhabricatorC4sHome.php +++ b/src/extensions/PhabricatorC4sHome.php @@ -1,698 +1,699 @@ 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); } if($filter == 'contrib') { $repo->requireCapabilities(array(DiffusionPushCapability::CAPABILITY)); $repo->withCanPush(); } if($filter == 'author') { $repo->withAuthors(array($viewer->getPHID())); + $repo->withCanView(); } 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(); } }