diff --git a/src/applications/diffusion/controller/DiffusionBranchTableController.php b/src/applications/diffusion/controller/DiffusionBranchTableController.php index 388eb9d568..4f0c494110 100644 --- a/src/applications/diffusion/controller/DiffusionBranchTableController.php +++ b/src/applications/diffusion/controller/DiffusionBranchTableController.php @@ -1,74 +1,74 @@ <?php final class DiffusionBranchTableController extends DiffusionController { public function shouldAllowPublic() { return true; } protected function processDiffusionRequest(AphrontRequest $request) { $drequest = $this->getDiffusionRequest(); $viewer = $request->getUser(); $repository = $drequest->getRepository(); $pager = new AphrontPagerView(); $pager->setURI($request->getRequestURI(), 'offset'); $pager->setOffset($request->getInt('offset')); // TODO: Add support for branches that contain commit $branches = $this->callConduitWithDiffusionRequest( 'diffusion.branchquery', array( 'offset' => $pager->getOffset(), 'limit' => $pager->getPageSize() + 1, )); $branches = $pager->sliceResults($branches); $branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches); $content = null; if (!$branches) { $content = $this->renderStatusMessage( pht('No Branches'), pht('This repository has no branches.')); } else { $commits = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->withIdentifiers(mpull($branches, 'getCommitIdentifier')) ->withRepository($repository) ->execute(); $view = id(new DiffusionBranchTableView()) ->setUser($viewer) ->setBranches($branches) ->setCommits($commits) ->setDiffusionRequest($drequest); - $panel = id(new AphrontPanelView()) - ->setNoBackground(true) - ->appendChild($view) - ->appendChild($pager); + $panel = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Branches')) + ->appendChild($view); $content = $panel; } $crumbs = $this->buildCrumbs( array( 'branches' => true, )); return $this->buildApplicationPage( array( $crumbs, $content, + $pager, ), array( 'title' => array( pht('Branches'), 'r'.$repository->getCallsign(), ), )); } } diff --git a/src/applications/diffusion/controller/DiffusionBrowseDirectoryController.php b/src/applications/diffusion/controller/DiffusionBrowseDirectoryController.php index 2be77a134e..833e108648 100644 --- a/src/applications/diffusion/controller/DiffusionBrowseDirectoryController.php +++ b/src/applications/diffusion/controller/DiffusionBrowseDirectoryController.php @@ -1,106 +1,106 @@ <?php final class DiffusionBrowseDirectoryController extends DiffusionBrowseController { private $browseQueryResults; public function setBrowseQueryResults(DiffusionBrowseResultSet $results) { $this->browseQueryResults = $results; return $this; } public function getBrowseQueryResults() { return $this->browseQueryResults; } protected function processDiffusionRequest(AphrontRequest $request) { $drequest = $this->diffusionRequest; $results = $this->getBrowseQueryResults(); $reason = $results->getReasonForEmptyResultSet(); $content = array(); $actions = $this->buildActionView($drequest); $properties = $this->buildPropertyView($drequest, $actions); $object_box = id(new PHUIObjectBoxView()) ->setHeader($this->buildHeaderView($drequest)) ->addPropertyList($properties); $content[] = $object_box; $content[] = $this->renderSearchForm($collapsed = true); if (!$results->isValidResults()) { $empty_result = new DiffusionEmptyResultView(); $empty_result->setDiffusionRequest($drequest); $empty_result->setDiffusionBrowseResultSet($results); $empty_result->setView($request->getStr('view')); $content[] = $empty_result; } else { $phids = array(); foreach ($results->getPaths() as $result) { $data = $result->getLastCommitData(); if ($data) { if ($data->getCommitDetail('authorPHID')) { $phids[$data->getCommitDetail('authorPHID')] = true; } } } $phids = array_keys($phids); $handles = $this->loadViewerHandles($phids); $browse_table = new DiffusionBrowseTableView(); $browse_table->setDiffusionRequest($drequest); $browse_table->setHandles($handles); $browse_table->setPaths($results->getPaths()); $browse_table->setUser($request->getUser()); - $browse_panel = new AphrontPanelView(); + $browse_panel = new PHUIObjectBoxView(); + $browse_panel->setHeaderText($drequest->getPath(), '/'); $browse_panel->appendChild($browse_table); - $browse_panel->setNoBackground(); $content[] = $browse_panel; } $content[] = $this->buildOpenRevisions(); $readme_path = $results->getReadmePath(); if ($readme_path) { $readme_content = $this->callConduitWithDiffusionRequest( 'diffusion.filecontentquery', array( 'path' => $readme_path, 'commit' => $drequest->getStableCommit(), )); if ($readme_content) { $content[] = id(new DiffusionReadmeView()) ->setUser($this->getViewer()) ->setPath($readme_path) ->setContent($readme_content['corpus']); } } $crumbs = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'browse', )); return $this->buildApplicationPage( array( $crumbs, $content, ), array( 'title' => array( nonempty(basename($drequest->getPath()), '/'), $drequest->getRepository()->getCallsign().' Repository', ), )); } } diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php index 01a96a6456..d55fdfbdf4 100644 --- a/src/applications/diffusion/controller/DiffusionCommitController.php +++ b/src/applications/diffusion/controller/DiffusionCommitController.php @@ -1,1081 +1,1084 @@ <?php final class DiffusionCommitController extends DiffusionController { const CHANGES_LIMIT = 100; private $auditAuthorityPHIDs; private $highlightedAudits; public function shouldAllowPublic() { return true; } protected function shouldLoadDiffusionRequest() { return false; } protected function processDiffusionRequest(AphrontRequest $request) { $user = $request->getUser(); // This controller doesn't use blob/path stuff, just pass the dictionary // in directly instead of using the AphrontRequest parsing mechanism. $data = $request->getURIMap(); $data['user'] = $user; $drequest = DiffusionRequest::newFromDictionary($data); $this->diffusionRequest = $drequest; if ($request->getStr('diff')) { return $this->buildRawDiffResponse($drequest); } $repository = $drequest->getRepository(); $callsign = $repository->getCallsign(); $content = array(); $commit = id(new DiffusionCommitQuery()) ->setViewer($request->getUser()) ->withRepository($repository) ->withIdentifiers(array($drequest->getCommit())) ->needCommitData(true) ->needAuditRequests(true) ->executeOne(); $crumbs = $this->buildCrumbs(array( 'commit' => true, )); if (!$commit) { $exists = $this->callConduitWithDiffusionRequest( 'diffusion.existsquery', array('commit' => $drequest->getCommit())); if (!$exists) { return new Aphront404Response(); } $error = id(new PHUIInfoView()) ->setTitle(pht('Commit Still Parsing')) ->appendChild( pht( 'Failed to load the commit because the commit has not been '. 'parsed yet.')); return $this->buildApplicationPage( array( $crumbs, $error, ), array( 'title' => pht('Commit Still Parsing'), )); } $audit_requests = $commit->getAudits(); $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user); $commit_data = $commit->getCommitData(); $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub'); $changesets = null; if ($is_foreign) { $subpath = $commit_data->getCommitDetail('svn-subpath'); $error_panel = new PHUIInfoView(); $error_panel->setTitle(pht('Commit Not Tracked')); $error_panel->setSeverity(PHUIInfoView::SEVERITY_WARNING); $error_panel->appendChild( pht("This Diffusion repository is configured to track only one ". "subdirectory of the entire Subversion repository, and this commit ". "didn't affect the tracked subdirectory ('%s'), so no ". "information is available.", $subpath)); $content[] = $error_panel; } else { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $engine->setConfig('viewer', $user); require_celerity_resource('phabricator-remarkup-css'); $parents = $this->callConduitWithDiffusionRequest( 'diffusion.commitparentsquery', array('commit' => $drequest->getCommit())); if ($parents) { $parents = id(new DiffusionCommitQuery()) ->setViewer($user) ->withRepository($repository) ->withIdentifiers($parents) ->execute(); } $headsup_view = id(new PHUIHeaderView()) ->setHeader(nonempty($commit->getSummary(), pht('Commit Detail'))); $headsup_actions = $this->renderHeadsupActionList($commit, $repository); $commit_properties = $this->loadCommitProperties( $commit, $commit_data, $parents, $audit_requests); $property_list = id(new PHUIPropertyListView()) ->setHasKeyboardShortcuts(true) ->setUser($user) ->setObject($commit); foreach ($commit_properties as $key => $value) { $property_list->addProperty($key, $value); } $message = $commit_data->getCommitMessage(); $revision = $commit->getCommitIdentifier(); $message = $this->linkBugtraq($message); $message = $engine->markupText($message); $property_list->invokeWillRenderEvent(); $property_list->setActionList($headsup_actions); $detail_list = new PHUIPropertyListView(); $detail_list->addSectionHeader( pht('Description'), PHUIPropertyListView::ICON_SUMMARY); $detail_list->addTextContent( phutil_tag( 'div', array( 'class' => 'diffusion-commit-message phabricator-remarkup', ), $message)); $object_box = id(new PHUIObjectBoxView()) ->setHeader($headsup_view) ->addPropertyList($property_list) ->addPropertyList($detail_list); $content[] = $object_box; } $content[] = $this->buildComments($commit); $hard_limit = 1000; if ($commit->isImported()) { $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest( $drequest); $change_query->setLimit($hard_limit + 1); $changes = $change_query->loadChanges(); } else { $changes = array(); } $was_limited = (count($changes) > $hard_limit); if ($was_limited) { $changes = array_slice($changes, 0, $hard_limit); } $content[] = $this->buildMergesTable($commit); $highlighted_audits = $commit->getAuthorityAudits( $user, $this->auditAuthorityPHIDs); $owners_paths = array(); if ($highlighted_audits) { $packages = id(new PhabricatorOwnersPackage())->loadAllWhere( 'phid IN (%Ls)', mpull($highlighted_audits, 'getAuditorPHID')); if ($packages) { $owners_paths = id(new PhabricatorOwnersPath())->loadAllWhere( 'repositoryPHID = %s AND packageID IN (%Ld)', $repository->getPHID(), mpull($packages, 'getID')); } } $change_table = new DiffusionCommitChangeTableView(); $change_table->setDiffusionRequest($drequest); $change_table->setPathChanges($changes); $change_table->setOwnersPaths($owners_paths); $count = count($changes); $bad_commit = null; if ($count == 0) { $bad_commit = queryfx_one( id(new PhabricatorRepository())->establishConnection('r'), 'SELECT * FROM %T WHERE fullCommitName = %s', PhabricatorRepository::TABLE_BADCOMMIT, 'r'.$callsign.$commit->getCommitIdentifier()); } if ($bad_commit) { $content[] = $this->renderStatusMessage( pht('Bad Commit'), $bad_commit['description']); } else if ($is_foreign) { // Don't render anything else. } else if (!$commit->isImported()) { $content[] = $this->renderStatusMessage( pht('Still Importing...'), pht( 'This commit is still importing. Changes will be visible once '. 'the import finishes.')); } else if (!count($changes)) { $content[] = $this->renderStatusMessage( pht('Empty Commit'), pht( 'This commit is empty and does not affect any paths.')); } else if ($was_limited) { $content[] = $this->renderStatusMessage( pht('Enormous Commit'), pht( 'This commit is enormous, and affects more than %d files. '. 'Changes are not shown.', $hard_limit)); } else { // The user has clicked "Show All Changes", and we should show all the // changes inline even if there are more than the soft limit. $show_all_details = $request->getBool('show_all'); $change_panel = new PHUIObjectBoxView(); $header = new PHUIHeaderView(); $header->setHeader('Changes ('.number_format($count).')'); $change_panel->setID('toc'); if ($count > self::CHANGES_LIMIT && !$show_all_details) { $icon = id(new PHUIIconView()) ->setIconFont('fa-files-o'); $button = id(new PHUIButtonView()) ->setText(pht('Show All Changes')) ->setHref('?show_all=true') ->setTag('a') ->setIcon($icon); $warning_view = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_WARNING) ->setTitle('Very Large Commit') ->appendChild( pht('This commit is very large. Load each file individually.')); $change_panel->setErrorView($warning_view); $header->addActionLink($button); } $change_panel->appendChild($change_table); $change_panel->setHeader($header); $content[] = $change_panel; $changesets = DiffusionPathChange::convertToDifferentialChangesets( $user, $changes); $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $vcs_supports_directory_changes = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $vcs_supports_directory_changes = false; break; default: throw new Exception('Unknown VCS.'); } $references = array(); foreach ($changesets as $key => $changeset) { $file_type = $changeset->getFileType(); if ($file_type == DifferentialChangeType::FILE_DIRECTORY) { if (!$vcs_supports_directory_changes) { unset($changesets[$key]); continue; } } $references[$key] = $drequest->generateURI( array( 'action' => 'rendering-ref', 'path' => $changeset->getFilename(), )); } // TODO: Some parts of the views still rely on properties of the // DifferentialChangeset. Make the objects ephemeral to make sure we don't // accidentally save them, and then set their ID to the appropriate ID for // this application (the path IDs). $path_ids = array_flip(mpull($changes, 'getPath')); foreach ($changesets as $changeset) { $changeset->makeEphemeral(); $changeset->setID($path_ids[$changeset->getFilename()]); } if ($count <= self::CHANGES_LIMIT || $show_all_details) { $visible_changesets = $changesets; } else { $visible_changesets = array(); $inlines = PhabricatorAuditInlineComment::loadDraftAndPublishedComments( $user, $commit->getPHID()); $path_ids = mpull($inlines, null, 'getPathID'); foreach ($changesets as $key => $changeset) { if (array_key_exists($changeset->getID(), $path_ids)) { $visible_changesets[$key] = $changeset; } } } $change_list_title = DiffusionView::nameCommit( $repository, $commit->getCommitIdentifier()); $change_list = new DifferentialChangesetListView(); $change_list->setTitle($change_list_title); $change_list->setChangesets($changesets); $change_list->setVisibleChangesets($visible_changesets); $change_list->setRenderingReferences($references); $change_list->setRenderURI('/diffusion/'.$callsign.'/diff/'); $change_list->setRepository($repository); $change_list->setUser($user); // TODO: Try to setBranch() to something reasonable here? $change_list->setStandaloneURI( '/diffusion/'.$callsign.'/diff/'); $change_list->setRawFileURIs( // TODO: Implement this, somewhat tricky if there's an octopus merge // or whatever? null, '/diffusion/'.$callsign.'/diff/?view=r'); $change_list->setInlineCommentControllerURI( '/diffusion/inline/edit/'.phutil_escape_uri($commit->getPHID()).'/'); $change_references = array(); foreach ($changesets as $key => $changeset) { $change_references[$changeset->getID()] = $references[$key]; } $change_table->setRenderingReferences($change_references); $content[] = $change_list->render(); } $content[] = $this->renderAddCommentPanel($commit, $audit_requests); $commit_id = 'r'.$callsign.$commit->getCommitIdentifier(); $short_name = DiffusionView::nameCommit( $repository, $commit->getCommitIdentifier()); $prefs = $user->loadPreferences(); $pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE; $pref_collapse = PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED; $show_filetree = $prefs->getPreference($pref_filetree); $collapsed = $prefs->getPreference($pref_collapse); if ($changesets && $show_filetree) { $nav = id(new DifferentialChangesetFileTreeSideNavBuilder()) ->setTitle($short_name) ->setBaseURI(new PhutilURI('/'.$commit_id)) ->build($changesets) ->setCrumbs($crumbs) ->setCollapsed((bool)$collapsed) ->appendChild($content); $content = $nav; } else { $content = array($crumbs, $content); } return $this->buildApplicationPage( $content, array( 'title' => $commit_id, 'pageObjects' => array($commit->getPHID()), )); } private function loadCommitProperties( PhabricatorRepositoryCommit $commit, PhabricatorRepositoryCommitData $data, array $parents, array $audit_requests) { assert_instances_of($parents, 'PhabricatorRepositoryCommit'); $viewer = $this->getRequest()->getUser(); $commit_phid = $commit->getPHID(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array($commit_phid)) ->withEdgeTypes(array( DiffusionCommitHasTaskEdgeType::EDGECONST, DiffusionCommitHasRevisionEdgeType::EDGECONST, DiffusionCommitRevertsCommitEdgeType::EDGECONST, DiffusionCommitRevertedByCommitEdgeType::EDGECONST, )); $edges = $edge_query->execute(); $task_phids = array_keys( $edges[$commit_phid][DiffusionCommitHasTaskEdgeType::EDGECONST]); $revision_phid = key( $edges[$commit_phid][DiffusionCommitHasRevisionEdgeType::EDGECONST]); $reverts_phids = array_keys( $edges[$commit_phid][DiffusionCommitRevertsCommitEdgeType::EDGECONST]); $reverted_by_phids = array_keys( $edges[$commit_phid][DiffusionCommitRevertedByCommitEdgeType::EDGECONST]); $phids = $edge_query->getDestinationPHIDs(array($commit_phid)); if ($data->getCommitDetail('authorPHID')) { $phids[] = $data->getCommitDetail('authorPHID'); } if ($data->getCommitDetail('reviewerPHID')) { $phids[] = $data->getCommitDetail('reviewerPHID'); } if ($data->getCommitDetail('committerPHID')) { $phids[] = $data->getCommitDetail('committerPHID'); } if ($parents) { foreach ($parents as $parent) { $phids[] = $parent->getPHID(); } } // NOTE: We should never normally have more than a single push log, but // it can occur naturally if a commit is pushed, then the branch it was // on is deleted, then the commit is pushed again (or through other similar // chains of events). This should be rare, but does not indicate a bug // or data issue. // NOTE: We never query push logs in SVN because the commiter is always // the pusher and the commit time is always the push time; the push log // is redundant and we save a query by skipping it. $push_logs = array(); if ($repository->isHosted() && !$repository->isSVN()) { $push_logs = id(new PhabricatorRepositoryPushLogQuery()) ->setViewer($viewer) ->withRepositoryPHIDs(array($repository->getPHID())) ->withNewRefs(array($commit->getCommitIdentifier())) ->withRefTypes(array(PhabricatorRepositoryPushLog::REFTYPE_COMMIT)) ->execute(); foreach ($push_logs as $log) { $phids[] = $log->getPusherPHID(); } } $handles = array(); if ($phids) { $handles = $this->loadViewerHandles($phids); } $props = array(); if ($commit->getAuditStatus()) { $status = PhabricatorAuditCommitStatusConstants::getStatusName( $commit->getAuditStatus()); $tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_STATE) ->setName($status); switch ($commit->getAuditStatus()) { case PhabricatorAuditCommitStatusConstants::NEEDS_AUDIT: $tag->setBackgroundColor(PHUITagView::COLOR_ORANGE); break; case PhabricatorAuditCommitStatusConstants::CONCERN_RAISED: $tag->setBackgroundColor(PHUITagView::COLOR_RED); break; case PhabricatorAuditCommitStatusConstants::PARTIALLY_AUDITED: $tag->setBackgroundColor(PHUITagView::COLOR_BLUE); break; case PhabricatorAuditCommitStatusConstants::FULLY_AUDITED: $tag->setBackgroundColor(PHUITagView::COLOR_GREEN); break; } $props['Status'] = $tag; } if ($audit_requests) { $user_requests = array(); $other_requests = array(); foreach ($audit_requests as $audit_request) { if ($audit_request->isUser()) { $user_requests[] = $audit_request; } else { $other_requests[] = $audit_request; } } if ($user_requests) { $props['Auditors'] = $this->renderAuditStatusView( $user_requests); } if ($other_requests) { $props['Project/Package Auditors'] = $this->renderAuditStatusView( $other_requests); } } $author_phid = $data->getCommitDetail('authorPHID'); $author_name = $data->getAuthorName(); if (!$repository->isSVN()) { $authored_info = id(new PHUIStatusItemView()); // TODO: In Git, a distinct authorship date is available. When present, // we should show it here. if ($author_phid) { $authored_info->setTarget($handles[$author_phid]->renderLink()); } else if (strlen($author_name)) { $authored_info->setTarget($author_name); } $props['Authored'] = id(new PHUIStatusListView()) ->addItem($authored_info); } $committed_info = id(new PHUIStatusItemView()) ->setNote(phabricator_datetime($commit->getEpoch(), $viewer)); $committer_phid = $data->getCommitDetail('committerPHID'); $committer_name = $data->getCommitDetail('committer'); if ($committer_phid) { $committed_info->setTarget($handles[$committer_phid]->renderLink()); } else if (strlen($committer_name)) { $committed_info->setTarget($committer_name); } else if ($author_phid) { $committed_info->setTarget($handles[$author_phid]->renderLink()); } else if (strlen($author_name)) { $committed_info->setTarget($author_name); } $props['Committed'] = id(new PHUIStatusListView()) ->addItem($committed_info); if ($push_logs) { $pushed_list = new PHUIStatusListView(); foreach ($push_logs as $push_log) { $pushed_item = id(new PHUIStatusItemView()) ->setTarget($handles[$push_log->getPusherPHID()]->renderLink()) ->setNote(phabricator_datetime($push_log->getEpoch(), $viewer)); $pushed_list->addItem($pushed_item); } $props['Pushed'] = $pushed_list; } $reviewer_phid = $data->getCommitDetail('reviewerPHID'); if ($reviewer_phid) { $props['Reviewer'] = $handles[$reviewer_phid]->renderLink(); } if ($revision_phid) { $props['Differential Revision'] = $handles[$revision_phid]->renderLink(); } if ($parents) { $parent_links = array(); foreach ($parents as $parent) { $parent_links[] = $handles[$parent->getPHID()]->renderLink(); } $props['Parents'] = phutil_implode_html(" \xC2\xB7 ", $parent_links); } $props['Branches'] = phutil_tag( 'span', array( 'id' => 'commit-branches', ), pht('Unknown')); $props['Tags'] = phutil_tag( 'span', array( 'id' => 'commit-tags', ), pht('Unknown')); $callsign = $repository->getCallsign(); $root = '/diffusion/'.$callsign.'/commit/'.$commit->getCommitIdentifier(); Javelin::initBehavior( 'diffusion-commit-branches', array( $root.'/branches/' => 'commit-branches', $root.'/tags/' => 'commit-tags', )); $refs = $this->buildRefs($drequest); if ($refs) { $props['References'] = $refs; } if ($reverts_phids) { $this->loadHandles($reverts_phids); $props[pht('Reverts')] = $this->renderHandlesForPHIDs($reverts_phids); } if ($reverted_by_phids) { $this->loadHandles($reverted_by_phids); $props[pht('Reverted By')] = $this->renderHandlesForPHIDs( $reverted_by_phids); } if ($task_phids) { $task_list = array(); foreach ($task_phids as $phid) { $task_list[] = $handles[$phid]->renderLink(); } $task_list = phutil_implode_html(phutil_tag('br'), $task_list); $props['Tasks'] = $task_list; } return $props; } private function buildComments(PhabricatorRepositoryCommit $commit) { $timeline = $this->buildTransactionTimeline( $commit, new PhabricatorAuditTransactionQuery()); $commit->willRenderTimeline($timeline, $this->getRequest()); return $timeline; } private function renderAddCommentPanel( PhabricatorRepositoryCommit $commit, array $audit_requests) { assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest'); $request = $this->getRequest(); $user = $request->getUser(); if (!$user->isLoggedIn()) { return id(new PhabricatorApplicationTransactionCommentView()) ->setUser($user) ->setRequestURI($request->getRequestURI()); } $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $pane_id = celerity_generate_unique_node_id(); Javelin::initBehavior( 'differential-keyboard-navigation', array( 'haunt' => $pane_id, )); $draft = id(new PhabricatorDraft())->loadOneWhere( 'authorPHID = %s AND draftKey = %s', $user->getPHID(), 'diffusion-audit-'.$commit->getID()); if ($draft) { $draft = $draft->getDraft(); } else { $draft = null; } $actions = $this->getAuditActions($commit, $audit_requests); $form = id(new AphrontFormView()) ->setUser($user) ->setAction('/audit/addcomment/') ->addHiddenInput('commit', $commit->getPHID()) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Action')) ->setName('action') ->setID('audit-action') ->setOptions($actions)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Add Auditors')) ->setName('auditors') ->setControlID('add-auditors') ->setControlStyle('display: none') ->setID('add-auditors-tokenizer') ->setDisableBehavior(true)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Add CCs')) ->setName('ccs') ->setControlID('add-ccs') ->setControlStyle('display: none') ->setID('add-ccs-tokenizer') ->setDisableBehavior(true)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setLabel(pht('Comments')) ->setName('content') ->setValue($draft) ->setID('audit-content') ->setUser($user)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue(pht('Submit'))); $header = new PHUIHeaderView(); $header->setHeader( $is_serious ? pht('Audit Commit') : pht('Creative Accounting')); require_celerity_resource('phabricator-transaction-view-css'); $mailable_source = new PhabricatorMetaMTAMailableDatasource(); $auditor_source = new DiffusionAuditorDatasource(); Javelin::initBehavior( 'differential-add-reviewers-and-ccs', array( 'dynamic' => array( 'add-auditors-tokenizer' => array( 'actions' => array('add_auditors' => 1), 'src' => $auditor_source->getDatasourceURI(), 'row' => 'add-auditors', 'placeholder' => $auditor_source->getPlaceholderText(), ), 'add-ccs-tokenizer' => array( 'actions' => array('add_ccs' => 1), 'src' => $mailable_source->getDatasourceURI(), 'row' => 'add-ccs', 'placeholder' => $mailable_source->getPlaceholderText(), ), ), 'select' => 'audit-action', )); Javelin::initBehavior('differential-feedback-preview', array( 'uri' => '/audit/preview/'.$commit->getID().'/', 'preview' => 'audit-preview', 'content' => 'audit-content', 'action' => 'audit-action', 'previewTokenizers' => array( 'auditors' => 'add-auditors-tokenizer', 'ccs' => 'add-ccs-tokenizer', ), 'inline' => 'inline-comment-preview', 'inlineuri' => '/diffusion/inline/preview/'.$commit->getPHID().'/', )); $loading = phutil_tag_div( 'aphront-panel-preview-loading-text', pht('Loading preview...')); $preview_panel = phutil_tag_div( 'aphront-panel-preview aphront-panel-flush', array( phutil_tag('div', array('id' => 'audit-preview'), $loading), phutil_tag('div', array('id' => 'inline-comment-preview')), )); // TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css'); $anchor = id(new PhabricatorAnchorView()) ->setAnchorName('comment') ->setNavigationMarker(true) ->render(); $comment_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($form); return phutil_tag( 'div', array( 'id' => $pane_id, ), phutil_tag_div( 'differential-add-comment-panel', array($anchor, $comment_box, $preview_panel))); } /** * Return a map of available audit actions for rendering into a <select />. * This shows the user valid actions, and does not show nonsense/invalid * actions (like closing an already-closed commit, or resigning from a commit * you have no association with). */ private function getAuditActions( PhabricatorRepositoryCommit $commit, array $audit_requests) { assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest'); $user = $this->getRequest()->getUser(); $user_is_author = ($commit->getAuthorPHID() == $user->getPHID()); $user_request = null; foreach ($audit_requests as $audit_request) { if ($audit_request->getAuditorPHID() == $user->getPHID()) { $user_request = $audit_request; break; } } $actions = array(); $actions[PhabricatorAuditActionConstants::COMMENT] = true; $actions[PhabricatorAuditActionConstants::ADD_CCS] = true; $actions[PhabricatorAuditActionConstants::ADD_AUDITORS] = true; // We allow you to accept your own commits. A use case here is that you // notice an issue with your own commit and "Raise Concern" as an indicator // to other auditors that you're on top of the issue, then later resolve it // and "Accept". You can not accept on behalf of projects or packages, // however. $actions[PhabricatorAuditActionConstants::ACCEPT] = true; $actions[PhabricatorAuditActionConstants::CONCERN] = true; // To resign, a user must have authority on some request and not be the // commit's author. if (!$user_is_author) { $may_resign = false; $authority_map = array_fill_keys($this->auditAuthorityPHIDs, true); foreach ($audit_requests as $request) { if (empty($authority_map[$request->getAuditorPHID()])) { continue; } $may_resign = true; break; } // If the user has already resigned, don't show "Resign...". $status_resigned = PhabricatorAuditStatusConstants::RESIGNED; if ($user_request) { if ($user_request->getAuditStatus() == $status_resigned) { $may_resign = false; } } if ($may_resign) { $actions[PhabricatorAuditActionConstants::RESIGN] = true; } } $status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED; $concern_raised = ($commit->getAuditStatus() == $status_concern); $can_close_option = PhabricatorEnv::getEnvConfig( 'audit.can-author-close-audit'); if ($can_close_option && $user_is_author && $concern_raised) { $actions[PhabricatorAuditActionConstants::CLOSE] = true; } foreach ($actions as $constant => $ignored) { $actions[$constant] = PhabricatorAuditActionConstants::getActionName($constant); } return $actions; } private function buildMergesTable(PhabricatorRepositoryCommit $commit) { $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: // These aren't supported under SVN. return null; } $limit = 50; $merges = $this->callConduitWithDiffusionRequest( 'diffusion.mergedcommitsquery', array( 'commit' => $drequest->getCommit(), 'limit' => $limit + 1, )); if (!$merges) { return null; } $merges = DiffusionPathChange::newFromConduit($merges); $caption = null; if (count($merges) > $limit) { $merges = array_slice($merges, 0, $limit); - $caption = - "This commit merges more than {$limit} changes. Only the first ". - "{$limit} are shown."; + $caption = new PHUIInfoView(); + $caption->setSeverity(PHUIInfoView::SEVERITY_NOTICE); + $caption->appendChild( + pht('This commit merges more than %d changes. Only the first '. + '%d are shown.', $limit)); } $history_table = new DiffusionHistoryTableView(); $history_table->setUser($this->getRequest()->getUser()); $history_table->setDiffusionRequest($drequest); $history_table->setHistory($merges); $history_table->loadRevisions(); $phids = $history_table->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $history_table->setHandles($handles); - $panel = new AphrontPanelView(); - $panel->setHeader(pht('Merged Changes')); - $panel->setCaption($caption); + $panel = new PHUIObjectBoxView(); + $panel->setHeaderText(pht('Merged Changes')); $panel->appendChild($history_table); - $panel->setNoBackground(); + if ($caption) { + $panel->setErrorView($caption); + } return $panel; } private function renderHeadsupActionList( PhabricatorRepositoryCommit $commit, PhabricatorRepository $repository) { $request = $this->getRequest(); $user = $request->getUser(); $actions = id(new PhabricatorActionListView()) ->setUser($user) ->setObject($commit) ->setObjectURI($request->getRequestURI()); $can_edit = PhabricatorPolicyFilter::hasCapability( $user, $commit, PhabricatorPolicyCapability::CAN_EDIT); $uri = '/diffusion/'.$repository->getCallsign().'/commit/'. $commit->getCommitIdentifier().'/edit/'; $action = id(new PhabricatorActionView()) ->setName(pht('Edit Commit')) ->setHref($uri) ->setIcon('fa-pencil') ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit); $actions->addAction($action); require_celerity_resource('phabricator-object-selector-css'); require_celerity_resource('javelin-behavior-phabricator-object-selector'); $maniphest = 'PhabricatorManiphestApplication'; if (PhabricatorApplication::isClassInstalled($maniphest)) { $action = id(new PhabricatorActionView()) ->setName(pht('Edit Maniphest Tasks')) ->setIcon('fa-anchor') ->setHref('/search/attach/'.$commit->getPHID().'/TASK/edge/') ->setWorkflow(true) ->setDisabled(!$can_edit); $actions->addAction($action); } $action = id(new PhabricatorActionView()) ->setName(pht('Download Raw Diff')) ->setHref($request->getRequestURI()->alter('diff', true)) ->setIcon('fa-download'); $actions->addAction($action); return $actions; } private function buildRefs(DiffusionRequest $request) { // this is git-only, so save a conduit round trip and just get out of // here if the repository isn't git $type_git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT; $repository = $request->getRepository(); if ($repository->getVersionControlSystem() != $type_git) { return null; } $results = $this->callConduitWithDiffusionRequest( 'diffusion.refsquery', array('commit' => $request->getCommit())); $ref_links = array(); foreach ($results as $ref_data) { $ref_links[] = phutil_tag('a', array('href' => $ref_data['href']), $ref_data['ref']); } return phutil_implode_html(', ', $ref_links); } private function buildRawDiffResponse(DiffusionRequest $drequest) { $raw_diff = $this->callConduitWithDiffusionRequest( 'diffusion.rawdiffquery', array( 'commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), )); $file = PhabricatorFile::buildFromFileDataOrHash( $raw_diff, array( 'name' => $drequest->getCommit().'.diff', 'ttl' => (60 * 60 * 24), 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, )); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $file->attachToObject($drequest->getRepository()->getPHID()); unset($unguarded); return $file->getRedirectResponse(); } private function renderAuditStatusView(array $audit_requests) { assert_instances_of($audit_requests, 'PhabricatorRepositoryAuditRequest'); $phids = mpull($audit_requests, 'getAuditorPHID'); $this->loadHandles($phids); $authority_map = array_fill_keys($this->auditAuthorityPHIDs, true); $view = new PHUIStatusListView(); foreach ($audit_requests as $request) { $code = $request->getAuditStatus(); $item = new PHUIStatusItemView(); $item->setIcon( PhabricatorAuditStatusConstants::getStatusIcon($code), PhabricatorAuditStatusConstants::getStatusColor($code), PhabricatorAuditStatusConstants::getStatusName($code)); $note = array(); foreach ($request->getAuditReasons() as $reason) { $note[] = phutil_tag('div', array(), $reason); } $item->setNote($note); $auditor_phid = $request->getAuditorPHID(); $target = $this->getHandle($auditor_phid)->renderLink(); $item->setTarget($target); if (isset($authority_map[$auditor_phid])) { $item->setHighlighted(true); } $view->addItem($item); } return $view; } private function linkBugtraq($corpus) { $url = PhabricatorEnv::getEnvConfig('bugtraq.url'); if (!strlen($url)) { return $corpus; } $regexes = PhabricatorEnv::getEnvConfig('bugtraq.logregex'); if (!$regexes) { return $corpus; } $parser = id(new PhutilBugtraqParser()) ->setBugtraqPattern("[[ {$url} | %BUGID% ]]") ->setBugtraqCaptureExpression(array_shift($regexes)); $select = array_shift($regexes); if ($select) { $parser->setBugtraqSelectExpression($select); } return $parser->processCorpus($corpus); } } diff --git a/src/applications/diffusion/controller/DiffusionExternalController.php b/src/applications/diffusion/controller/DiffusionExternalController.php index fdffd91da4..228cc53e3e 100644 --- a/src/applications/diffusion/controller/DiffusionExternalController.php +++ b/src/applications/diffusion/controller/DiffusionExternalController.php @@ -1,141 +1,145 @@ <?php final class DiffusionExternalController extends DiffusionController { public function shouldAllowPublic() { return true; } protected function shouldLoadDiffusionRequest() { return false; } protected function processDiffusionRequest(AphrontRequest $request) { $uri = $request->getStr('uri'); $id = $request->getStr('id'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->execute(); if ($uri) { $uri_path = id(new PhutilURI($uri))->getPath(); $matches = array(); // Try to figure out which tracked repository this external lives in by // comparing repository metadata. We look for an exact match, but accept // a partial match. foreach ($repositories as $key => $repository) { $remote_uri = new PhutilURI($repository->getRemoteURI()); if ($remote_uri->getPath() == $uri_path) { $matches[$key] = 1; } if ($repository->getPublicCloneURI() == $uri) { $matches[$key] = 2; } if ($repository->getRemoteURI() == $uri) { $matches[$key] = 3; } } arsort($matches); $best_match = head_key($matches); if ($best_match) { $repository = $repositories[$best_match]; $redirect = DiffusionRequest::generateDiffusionURI( array( 'action' => 'browse', 'callsign' => $repository->getCallsign(), 'branch' => $repository->getDefaultBranch(), 'commit' => $id, )); return id(new AphrontRedirectResponse())->setURI($redirect); } } // TODO: This is a rare query but does a table scan, add a key? $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 'commitIdentifier = %s', $id); if (empty($commits)) { $desc = null; if ($uri) { $desc = $uri.', at '; } $desc .= $id; $content = id(new PHUIInfoView()) ->setTitle(pht('Unknown External')) ->setSeverity(PHUIInfoView::SEVERITY_WARNING) ->appendChild(phutil_tag( 'p', array(), pht('This external (%s) does not appear in any tracked '. 'repository. It may exist in an untracked repository that '. 'Diffusion does not know about.', $desc))); } else if (count($commits) == 1) { $commit = head($commits); $repo = $repositories[$commit->getRepositoryID()]; $redirect = DiffusionRequest::generateDiffusionURI( array( 'action' => 'browse', 'callsign' => $repo->getCallsign(), 'branch' => $repo->getDefaultBranch(), 'commit' => $commit->getCommitIdentifier(), )); return id(new AphrontRedirectResponse())->setURI($redirect); } else { $rows = array(); foreach ($commits as $commit) { $repo = $repositories[$commit->getRepositoryID()]; $href = DiffusionRequest::generateDiffusionURI( array( 'action' => 'browse', 'callsign' => $repo->getCallsign(), 'branch' => $repo->getDefaultBranch(), 'commit' => $commit->getCommitIdentifier(), )); $rows[] = array( phutil_tag( 'a', array( 'href' => $href, ), 'r'.$repo->getCallsign().$commit->getCommitIdentifier()), $commit->loadCommitData()->getSummary(), ); } $table = new AphrontTableView($rows); $table->setHeaders( array( pht('Commit'), pht('Description'), )); $table->setColumnClasses( array( 'pri', 'wide', )); - $content = new AphrontPanelView(); - $content->setHeader(pht('Multiple Matching Commits')); - $content->setCaption( - pht('This external reference matches multiple known commits.')); + $caption = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) + ->appendChild( + pht('This external reference matches multiple known commits.')); + + $content = new PHUIObjectBoxView(); + $content->setHeaderText(pht('Multiple Matching Commits')); + $content->setErrorView($caption); $content->appendChild($table); } return $this->buildApplicationPage( $content, array( 'title' => pht('Unresolvable External'), )); } } diff --git a/src/applications/diffusion/controller/DiffusionHistoryController.php b/src/applications/diffusion/controller/DiffusionHistoryController.php index 2ac87542f5..36667c5dfe 100644 --- a/src/applications/diffusion/controller/DiffusionHistoryController.php +++ b/src/applications/diffusion/controller/DiffusionHistoryController.php @@ -1,173 +1,173 @@ <?php final class DiffusionHistoryController extends DiffusionController { public function shouldAllowPublic() { return true; } protected function processDiffusionRequest(AphrontRequest $request) { $drequest = $this->diffusionRequest; $viewer = $request->getUser(); $repository = $drequest->getRepository(); $page_size = $request->getInt('pagesize', 100); $offset = $request->getInt('offset', 0); $params = array( 'commit' => $drequest->getCommit(), 'path' => $drequest->getPath(), 'offset' => $offset, 'limit' => $page_size + 1, ); if (!$request->getBool('copies')) { $params['needDirectChanges'] = true; $params['needChildChanges'] = true; } $history_results = $this->callConduitWithDiffusionRequest( 'diffusion.historyquery', $params); $history = DiffusionPathChange::newFromConduit( $history_results['pathChanges']); $pager = new AphrontPagerView(); $pager->setPageSize($page_size); $pager->setOffset($offset); $history = $pager->sliceResults($history); $pager->setURI($request->getRequestURI(), 'offset'); $show_graph = !strlen($drequest->getPath()); $content = array(); $history_table = new DiffusionHistoryTableView(); $history_table->setUser($request->getUser()); $history_table->setDiffusionRequest($drequest); $history_table->setHistory($history); $history_table->loadRevisions(); $phids = $history_table->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $history_table->setHandles($handles); if ($show_graph) { $history_table->setParents($history_results['parents']); $history_table->setIsHead($offset == 0); } - $history_panel = new AphrontPanelView(); + $history_panel = new PHUIObjectBoxView(); + $history_panel->setHeaderText(pht('History')); $history_panel->appendChild($history_table); - $history_panel->appendChild($pager); - $history_panel->setNoBackground(); $content[] = $history_panel; $header = id(new PHUIHeaderView()) ->setUser($viewer) ->setPolicyObject($repository) ->setHeader($this->renderPathLinks($drequest, $mode = 'history')); $actions = $this->buildActionView($drequest); $properties = $this->buildPropertyView($drequest, $actions); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); $crumbs = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'history', )); return $this->buildApplicationPage( array( $crumbs, $object_box, $content, + $pager, ), array( 'title' => array( pht('History'), pht('%s Repository', $drequest->getRepository()->getCallsign()), ), )); } private function buildActionView(DiffusionRequest $drequest) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $browse_uri = $drequest->generateURI( array( 'action' => 'browse', )); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Browse Content')) ->setHref($browse_uri) ->setIcon('fa-files-o')); // TODO: Sometimes we do have a change view, we need to look at the most // recent history entry to figure it out. $request = $this->getRequest(); if ($request->getBool('copies')) { $branch_name = pht('Hide Copies/Branches'); $branch_uri = $request->getRequestURI() ->alter('offset', null) ->alter('copies', null); } else { $branch_name = pht('Show Copies/Branches'); $branch_uri = $request->getRequestURI() ->alter('offset', null) ->alter('copies', true); } $view->addAction( id(new PhabricatorActionView()) ->setName($branch_name) ->setIcon('fa-code-fork') ->setHref($branch_uri)); return $view; } protected function buildPropertyView( DiffusionRequest $drequest, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $stable_commit = $drequest->getStableCommit(); $callsign = $drequest->getRepository()->getCallsign(); $view->addProperty( pht('Commit'), phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => 'commit', 'commit' => $stable_commit, )), ), $drequest->getRepository()->formatCommitName($stable_commit))); return $view; } } diff --git a/src/applications/diffusion/controller/DiffusionLintController.php b/src/applications/diffusion/controller/DiffusionLintController.php index 8e00727729..5f51d31fe7 100644 --- a/src/applications/diffusion/controller/DiffusionLintController.php +++ b/src/applications/diffusion/controller/DiffusionLintController.php @@ -1,344 +1,342 @@ <?php final class DiffusionLintController extends DiffusionController { public function shouldAllowPublic() { return true; } protected function processDiffusionRequest(AphrontRequest $request) { $user = $request->getUser(); $drequest = $this->diffusionRequest; if ($request->getStr('lint') !== null) { $controller = new DiffusionLintDetailsController(); $controller->setDiffusionRequest($drequest); $controller->setCurrentApplication($this->getCurrentApplication()); return $this->delegateToController($controller); } $owners = array(); if (!$drequest) { if (!$request->getArr('owner')) { $owners = array($user->getPHID()); } else { $owners = array(head($request->getArr('owner'))); } $owner_handles = $this->loadViewerHandles($owners); } $codes = $this->loadLintCodes($owners); if ($codes && !$drequest) { // TODO: Build some real Query classes for this stuff. $branches = id(new PhabricatorRepositoryBranch())->loadAllWhere( 'id IN (%Ld)', array_unique(ipull($codes, 'branchID'))); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($user) ->withIDs(mpull($branches, 'getRepositoryID')) ->execute(); $drequests = array(); foreach ($branches as $id => $branch) { if (empty($repositories[$branch->getRepositoryID()])) { continue; } $drequests[$id] = DiffusionRequest::newFromDictionary(array( 'user' => $user, 'repository' => $repositories[$branch->getRepositoryID()], 'branch' => $branch->getName(), )); } } $rows = array(); $total = 0; foreach ($codes as $code) { if (!$this->diffusionRequest) { $drequest = idx($drequests, $code['branchID']); } if (!$drequest) { continue; } $total += $code['n']; $href_lint = $drequest->generateURI(array( 'action' => 'lint', 'lint' => $code['code'], )); $href_browse = $drequest->generateURI(array( 'action' => 'browse', 'lint' => $code['code'], )); $href_repo = $drequest->generateURI(array('action' => 'lint')); $rows[] = array( phutil_tag('a', array('href' => $href_lint), $code['n']), phutil_tag('a', array('href' => $href_browse), $code['files']), phutil_tag('a', array('href' => $href_repo), $drequest->getCallsign()), ArcanistLintSeverity::getStringForSeverity($code['maxSeverity']), $code['code'], $code['maxName'], $code['maxDescription'], ); } $table = id(new AphrontTableView($rows)) ->setHeaders(array( pht('Problems'), pht('Files'), pht('Repository'), pht('Severity'), pht('Code'), pht('Name'), pht('Example'), )) ->setColumnVisibility(array(true, true, !$this->diffusionRequest)) ->setColumnClasses(array('n', 'n', '', '', 'pri', '', '')); $content = array(); - $link = null; if (!$this->diffusionRequest) { $form = id(new AphrontFormView()) ->setUser($user) ->setMethod('GET') ->appendChild( id(new AphrontFormTokenizerControl()) ->setDatasource(new PhabricatorPeopleDatasource()) ->setLimit(1) ->setName('owner') ->setLabel(pht('Owner')) ->setValue($owner_handles)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Filter')); $content[] = id(new AphrontListFilterView())->appendChild($form); } - $content[] = id(new AphrontPanelView()) - ->setNoBackground(true) - ->setCaption($link) + $content[] = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Lint')) ->appendChild($table); $title = array('Lint'); $crumbs = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'lint', )); if ($this->diffusionRequest) { $title[] = $drequest->getCallsign(); } else { $crumbs->addTextCrumb(pht('All Lint')); } if ($this->diffusionRequest) { $branch = $drequest->loadBranch(); $header = id(new PHUIHeaderView()) ->setHeader($this->renderPathLinks($drequest, 'lint')) ->setUser($user) ->setPolicyObject($drequest->getRepository()); $actions = $this->buildActionView($drequest); $properties = $this->buildPropertyView( $drequest, $branch, $total, $actions); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addPropertyList($properties); } else { $object_box = null; } return $this->buildApplicationPage( array( $crumbs, $object_box, $content, ), array( 'title' => $title, )); } private function loadLintCodes(array $owner_phids) { $drequest = $this->diffusionRequest; $conn = id(new PhabricatorRepository())->establishConnection('r'); $where = array('1 = 1'); if ($drequest) { $branch = $drequest->loadBranch(); if (!$branch) { return array(); } $where[] = qsprintf($conn, 'branchID = %d', $branch->getID()); if ($drequest->getPath() != '') { $path = '/'.$drequest->getPath(); $is_dir = (substr($path, -1) == '/'); $where[] = ($is_dir ? qsprintf($conn, 'path LIKE %>', $path) : qsprintf($conn, 'path = %s', $path)); } } if ($owner_phids) { $or = array(); $or[] = qsprintf($conn, 'authorPHID IN (%Ls)', $owner_phids); $paths = array(); $packages = id(new PhabricatorOwnersOwner()) ->loadAllWhere('userPHID IN (%Ls)', $owner_phids); if ($packages) { $paths = id(new PhabricatorOwnersPath())->loadAllWhere( 'packageID IN (%Ld)', mpull($packages, 'getPackageID')); } if ($paths) { $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getRequest()->getUser()) ->withPHIDs(mpull($paths, 'getRepositoryPHID')) ->execute(); $repositories = mpull($repositories, 'getID', 'getPHID'); $branches = id(new PhabricatorRepositoryBranch())->loadAllWhere( 'repositoryID IN (%Ld)', $repositories); $branches = mgroup($branches, 'getRepositoryID'); } foreach ($paths as $path) { $branch = idx( $branches, idx( $repositories, $path->getRepositoryPHID())); if ($branch) { $condition = qsprintf( $conn, '(branchID IN (%Ld) AND path LIKE %>)', array_keys($branch), $path->getPath()); if ($path->getExcluded()) { $where[] = 'NOT '.$condition; } else { $or[] = $condition; } } } $where[] = '('.implode(' OR ', $or).')'; } return queryfx_all( $conn, 'SELECT branchID, code, MAX(severity) AS maxSeverity, MAX(name) AS maxName, MAX(description) AS maxDescription, COUNT(DISTINCT path) AS files, COUNT(*) AS n FROM %T WHERE %Q GROUP BY branchID, code ORDER BY n DESC', PhabricatorRepository::TABLE_LINTMESSAGE, implode(' AND ', $where)); } protected function buildActionView(DiffusionRequest $drequest) { $viewer = $this->getRequest()->getUser(); $view = id(new PhabricatorActionListView()) ->setUser($viewer); $list_uri = $drequest->generateURI( array( 'action' => 'lint', 'lint' => '', )); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('View As List')) ->setHref($list_uri) ->setIcon('fa-list')); $history_uri = $drequest->generateURI( array( 'action' => 'history', )); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('View History')) ->setHref($history_uri) ->setIcon('fa-clock-o')); $browse_uri = $drequest->generateURI( array( 'action' => 'browse', )); $view->addAction( id(new PhabricatorActionView()) ->setName(pht('Browse Content')) ->setHref($browse_uri) ->setIcon('fa-files-o')); return $view; } protected function buildPropertyView( DiffusionRequest $drequest, PhabricatorRepositoryBranch $branch, $total, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setActionList($actions); $callsign = $drequest->getRepository()->getCallsign(); $lint_commit = $branch->getLintCommit(); $view->addProperty( pht('Lint Commit'), phutil_tag( 'a', array( 'href' => $drequest->generateURI( array( 'action' => 'commit', 'commit' => $lint_commit, )), ), $drequest->getRepository()->formatCommitName($lint_commit))); $view->addProperty( pht('Total Messages'), pht('%s', new PhutilNumber($total))); return $view; } } diff --git a/src/applications/diffusion/controller/DiffusionLintDetailsController.php b/src/applications/diffusion/controller/DiffusionLintDetailsController.php index 255b9e55cd..ba1a1dcbaf 100644 --- a/src/applications/diffusion/controller/DiffusionLintDetailsController.php +++ b/src/applications/diffusion/controller/DiffusionLintDetailsController.php @@ -1,144 +1,144 @@ <?php final class DiffusionLintDetailsController extends DiffusionController { protected function processDiffusionRequest(AphrontRequest $request) { $limit = 500; $offset = $request->getInt('offset', 0); $drequest = $this->getDiffusionRequest(); $branch = $drequest->loadBranch(); $messages = $this->loadLintMessages($branch, $limit, $offset); $is_dir = (substr('/'.$drequest->getPath(), -1) == '/'); $authors = $this->loadViewerHandles(ipull($messages, 'authorPHID')); $rows = array(); foreach ($messages as $message) { $path = phutil_tag( 'a', array( 'href' => $drequest->generateURI(array( 'action' => 'lint', 'path' => $message['path'], )), ), substr($message['path'], strlen($drequest->getPath()) + 1)); $line = phutil_tag( 'a', array( 'href' => $drequest->generateURI(array( 'action' => 'browse', 'path' => $message['path'], 'line' => $message['line'], 'commit' => $branch->getLintCommit(), )), ), $message['line']); $author = $message['authorPHID']; if ($author && $authors[$author]) { $author = $authors[$author]->renderLink(); } $rows[] = array( $path, $line, $author, ArcanistLintSeverity::getStringForSeverity($message['severity']), $message['name'], $message['description'], ); } $table = id(new AphrontTableView($rows)) ->setHeaders(array( pht('Path'), pht('Line'), pht('Author'), pht('Severity'), pht('Name'), pht('Description'), )) ->setColumnClasses(array('', 'n')) ->setColumnVisibility(array($is_dir)); $content = array(); $pager = id(new AphrontPagerView()) ->setPageSize($limit) ->setOffset($offset) ->setHasMorePages(count($messages) >= $limit) ->setURI($request->getRequestURI(), 'offset'); - $content[] = id(new AphrontPanelView()) - ->setNoBackground(true) - ->appendChild($table) - ->appendChild($pager); + $content[] = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Lint Details')) + ->appendChild($table); $crumbs = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'lint', )); return $this->buildApplicationPage( array( $crumbs, $content, + $pager, ), array( 'title' => array( pht('Lint'), $drequest->getRepository()->getCallsign(), ), )); } private function loadLintMessages( PhabricatorRepositoryBranch $branch, $limit, $offset) { $drequest = $this->getDiffusionRequest(); if (!$branch) { return array(); } $conn = $branch->establishConnection('r'); $where = array( qsprintf($conn, 'branchID = %d', $branch->getID()), ); if ($drequest->getPath() != '') { $path = '/'.$drequest->getPath(); $is_dir = (substr($path, -1) == '/'); $where[] = ($is_dir ? qsprintf($conn, 'path LIKE %>', $path) : qsprintf($conn, 'path = %s', $path)); } if ($drequest->getLint() != '') { $where[] = qsprintf( $conn, 'code = %s', $drequest->getLint()); } return queryfx_all( $conn, 'SELECT * FROM %T WHERE %Q ORDER BY path, code, line LIMIT %d OFFSET %d', PhabricatorRepository::TABLE_LINTMESSAGE, implode(' AND ', $where), $limit, $offset); } } diff --git a/src/applications/diffusion/controller/DiffusionSymbolController.php b/src/applications/diffusion/controller/DiffusionSymbolController.php index 104b9fc515..34a291a8cd 100644 --- a/src/applications/diffusion/controller/DiffusionSymbolController.php +++ b/src/applications/diffusion/controller/DiffusionSymbolController.php @@ -1,153 +1,151 @@ <?php final class DiffusionSymbolController extends DiffusionController { private $name; protected function processDiffusionRequest(AphrontRequest $request) { $user = $request->getUser(); $this->name = $request->getURIData('name'); $query = id(new DiffusionSymbolQuery()) ->setViewer($user) ->setName($this->name); if ($request->getStr('context') !== null) { $query->setContext($request->getStr('context')); } if ($request->getStr('type')) { $query->setType($request->getStr('type')); } if ($request->getStr('lang')) { $query->setLanguage($request->getStr('lang')); } if ($request->getStr('projects')) { $phids = $request->getStr('projects'); $phids = explode(',', $phids); $phids = array_filter($phids); if ($phids) { $projects = id(new PhabricatorRepositoryArcanistProject()) ->loadAllWhere( 'phid IN (%Ls)', $phids); $projects = mpull($projects, 'getID'); if ($projects) { $query->setProjectIDs($projects); } } } $query->needPaths(true); $query->needArcanistProjects(true); $query->needRepositories(true); $symbols = $query->execute(); // For PHP builtins, jump to php.net documentation. if ($request->getBool('jump') && count($symbols) == 0) { if ($request->getStr('lang', 'php') == 'php') { if ($request->getStr('type', 'function') == 'function') { $functions = get_defined_functions(); if (in_array($this->name, $functions['internal'])) { return id(new AphrontRedirectResponse()) ->setIsExternal(true) ->setURI('http://www.php.net/function.'.$this->name); } } if ($request->getStr('type', 'class') == 'class') { if (class_exists($this->name, false) || interface_exists($this->name, false)) { if (id(new ReflectionClass($this->name))->isInternal()) { return id(new AphrontRedirectResponse()) ->setIsExternal(true) ->setURI('http://www.php.net/class.'.$this->name); } } } } } $rows = array(); foreach ($symbols as $symbol) { $project = $symbol->getArcanistProject(); if ($project) { $project_name = $project->getName(); } else { $project_name = '-'; } $file = $symbol->getPath(); $line = $symbol->getLineNumber(); $repo = $symbol->getRepository(); if ($repo) { $href = $symbol->getURI(); if ($request->getBool('jump') && count($symbols) == 1) { // If this is a clickthrough from Differential, just jump them // straight to the target if we got a single hit. return id(new AphrontRedirectResponse())->setURI($href); } $location = phutil_tag( 'a', array( 'href' => $href, ), $file.':'.$line); } else if ($file) { $location = $file.':'.$line; } else { $location = '?'; } $rows[] = array( $symbol->getSymbolType(), $symbol->getSymbolContext(), $symbol->getSymbolName(), $symbol->getSymbolLanguage(), $project_name, $location, ); } $table = new AphrontTableView($rows); $table->setHeaders( array( pht('Type'), pht('Context'), pht('Name'), pht('Language'), pht('Project'), pht('File'), )); $table->setColumnClasses( array( '', '', 'pri', '', '', '', )); $table->setNoDataString( pht('No matching symbol could be found in any indexed project.')); - $panel = new AphrontPanelView(); - $panel->setHeader(pht('Similar Symbols')); + $panel = new PHUIObjectBoxView(); + $panel->setHeaderText(pht('Similar Symbols')); $panel->appendChild($table); return $this->buildApplicationPage( - array( - $panel, - ), + $panel, array( 'title' => pht('Find Symbol'), )); } } diff --git a/src/applications/diffusion/controller/DiffusionTagListController.php b/src/applications/diffusion/controller/DiffusionTagListController.php index bf2c2f84ce..349df62520 100644 --- a/src/applications/diffusion/controller/DiffusionTagListController.php +++ b/src/applications/diffusion/controller/DiffusionTagListController.php @@ -1,96 +1,96 @@ <?php final class DiffusionTagListController extends DiffusionController { public function shouldAllowPublic() { return true; } protected function processDiffusionRequest(AphrontRequest $request) { $drequest = $this->getDiffusionRequest(); $viewer = $request->getUser(); $repository = $drequest->getRepository(); $pager = new AphrontPagerView(); $pager->setURI($request->getRequestURI(), 'offset'); $pager->setOffset($request->getInt('offset')); $params = array( 'limit' => $pager->getPageSize() + 1, 'offset' => $pager->getOffset(), ); if ($drequest->getSymbolicCommit()) { $is_commit = true; $params['commit'] = $drequest->getSymbolicCommit(); } else { $is_commit = false; } switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $tags = array(); break; default: $conduit_result = $this->callConduitWithDiffusionRequest( 'diffusion.tagsquery', $params); $tags = DiffusionRepositoryTag::newFromConduit($conduit_result); break; } $tags = $pager->sliceResults($tags); $content = null; if (!$tags) { $content = $this->renderStatusMessage( pht('No Tags'), $is_commit ? pht('This commit has no tags.') : pht('This repository has no tags.')); } else { $commits = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->withRepository($repository) ->withIdentifiers(mpull($tags, 'getCommitIdentifier')) ->needCommitData(true) ->execute(); $view = id(new DiffusionTagListView()) ->setTags($tags) ->setUser($viewer) ->setCommits($commits) ->setDiffusionRequest($drequest); $phids = $view->getRequiredHandlePHIDs(); $handles = $this->loadViewerHandles($phids); $view->setHandles($handles); - $panel = id(new AphrontPanelView()) - ->setNoBackground(true) - ->appendChild($view) - ->appendChild($pager); + $panel = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Tags')) + ->appendChild($view); $content = $panel; } $crumbs = $this->buildCrumbs( array( 'tags' => true, 'commit' => $drequest->getSymbolicCommit(), )); return $this->buildApplicationPage( array( $crumbs, $content, + $pager, ), array( 'title' => array( pht('Tags'), pht('%s Repository', $repository->getCallsign()), ), )); } }