public function renderValueForRevisionView() { $diff = $this->getDiff(); $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff); $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); $postponed_count = 0; $udata = $this->getDiffProperty('arc:unit'); $utail = null; if ($udata) { $unit_messages = array(); foreach ($udata as $test) { $name = idx($test, 'name'); $result = idx($test, 'result'); if ($result != DifferentialUnitTestResult::RESULT_POSTPONED && $result != DifferentialUnitTestResult::RESULT_PASS) { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $userdata = phutil_utf8_shorten(idx($test, 'userdata'), 512); $userdata = $engine->markupText($userdata); $unit_messages[] = '<li>' . '<span class="unit-result-' . phutil_escape_html($result) . '">' . phutil_escape_html(ucwords($result)) . '</span>' . ' ' . phutil_escape_html($name) . '<p>' . $userdata . '</p>' . '</li>'; } else { if ($result == DifferentialUnitTestResult::RESULT_POSTPONED) { $postponed_count++; } } } $uexcuse = $this->getUnitExcuse(); if ($unit_messages) { $utail = '<div class="differential-unit-block">' . $uexcuse . '<ul>' . implode("\n", $unit_messages) . '</ul>' . '</div>'; } } if ($postponed_count > 0 && $diff->getUnitStatus() == DifferentialUnitStatus::UNIT_POSTPONED) { $umsg = $postponed_count . ' ' . $umsg; } return $ustar . ' ' . $umsg . $utail; }
public function processRequest() { $request = $this->getRequest(); $author_phid = $request->getUser()->getPHID(); $action = $request->getStr('action'); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $comment = new DifferentialComment(); $comment->setContent($request->getStr('content')); $comment->setAction($action); $comment->setAuthorPHID($author_phid); $handles = array($author_phid); $reviewers = $request->getStr('reviewers'); if (($action == DifferentialAction::ACTION_ADDREVIEWERS || $action == DifferentialAction::ACTION_REQUEST) && $reviewers) { $reviewers = explode(',', $reviewers); $comment->setMetadata(array(DifferentialComment::METADATA_ADDED_REVIEWERS => $reviewers)); $handles = array_merge($handles, $reviewers); } $ccs = $request->getStr('ccs'); if ($action == DifferentialAction::ACTION_ADDCCS && $ccs) { $ccs = explode(',', $ccs); $comment->setMetadata(array(DifferentialComment::METADATA_ADDED_CCS => $ccs)); $handles = array_merge($handles, $ccs); } $handles = id(new PhabricatorObjectHandleData($handles))->loadHandles(); $view = new DifferentialRevisionCommentView(); $view->setUser($request->getUser()); $view->setComment($comment); $view->setHandles($handles); $view->setMarkupEngine($engine); $view->setPreview(true); $view->setTargetDiff(null); $draft = new PhabricatorDraft(); $draft->setAuthorPHID($author_phid)->setDraftKey('differential-comment-' . $this->id)->setDraft($comment->getContent())->replace(); return id(new AphrontAjaxResponse())->setContent($view->render()); }
public function renderValueForRevisionView() { if (!$this->value) { return null; } $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); return $engine->markupText($this->value); }
public function renderValueForRevisionView() { $diff = $this->getDiff(); $ustar = DifferentialRevisionUpdateHistoryView::renderDiffUnitStar($diff); $umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff); $rows = array(); $rows[] = array('style' => 'star', 'name' => $ustar, 'value' => $umsg, 'show' => true); $excuse = $this->getUnitExcuse(); if ($excuse) { $rows[] = array('style' => 'excuse', 'name' => 'Excuse', 'value' => nl2br(phutil_escape_html($excuse)), 'show' => true); } $show_limit = 10; $hidden = array(); $udata = $this->getDiffProperty('arc:unit'); if ($udata) { $sort_map = array(ArcanistUnitTestResult::RESULT_BROKEN => 0, ArcanistUnitTestResult::RESULT_FAIL => 1, ArcanistUnitTestResult::RESULT_UNSOUND => 2, ArcanistUnitTestResult::RESULT_SKIP => 3, ArcanistUnitTestResult::RESULT_POSTPONED => 4, ArcanistUnitTestResult::RESULT_PASS => 5); foreach ($udata as $key => $test) { $udata[$key]['sort'] = idx($sort_map, idx($test, 'result')); } $udata = isort($udata, 'sort'); foreach ($udata as $test) { $result = idx($test, 'result'); $default_hide = false; switch ($result) { case ArcanistUnitTestResult::RESULT_POSTPONED: case ArcanistUnitTestResult::RESULT_PASS: $default_hide = true; break; } if ($show_limit && !$default_hide) { --$show_limit; $show = true; } else { $show = false; if (empty($hidden[$result])) { $hidden[$result] = 0; } $hidden[$result]++; } $rows[] = array('style' => $this->getResultStyle($result), 'name' => phutil_escape_html(ucwords($result)), 'value' => phutil_escape_html(idx($test, 'name')), 'show' => $show); $userdata = idx($test, 'userdata'); if ($userdata) { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $userdata = $engine->markupText($userdata); $rows[] = array('style' => 'details', 'value' => $userdata, 'show' => false); if (empty($hidden['details'])) { $hidden['details'] = 0; } $hidden['details']++; } } } $show_string = $this->renderShowString($hidden); $view = new DifferentialResultsTableView(); $view->setRows($rows); $view->setShowMoreString($show_string); return $view->render(); }
public function processRequest() { $request = $this->getRequest(); $author_phid = $request->getUser()->getPHID(); $handles = id(new PhabricatorObjectHandleData(array($author_phid)))->loadHandles(); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $comment = new DifferentialComment(); $comment->setContent($request->getStr('content')); $comment->setAction($request->getStr('action')); $comment->setAuthorPHID($author_phid); $view = new DifferentialRevisionCommentView(); $view->setUser($request->getUser()); $view->setComment($comment); $view->setHandles($handles); $view->setMarkupEngine($engine); $view->setPreview(true); $view->setTargetDiff(null); $draft = new PhabricatorDraft(); $draft->setAuthorPHID($author_phid)->setDraftKey('differential-comment-' . $this->id)->setDraft($comment->getContent())->replace(); return id(new AphrontAjaxResponse())->setContent($view->render()); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $inlines = $this->loadInlineComments(); assert_instances_of($inlines, 'PhabricatorInlineCommentInterface'); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $phids = array($user->getPHID()); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $views = array(); foreach ($inlines as $inline) { $view = new DifferentialInlineCommentView(); $view->setInlineComment($inline); $view->setMarkupEngine($engine); $view->setHandles($handles); $view->setEditable(false); $view->setPreview(true); $views[] = $view->render(); } $views = implode("\n", $views); return id(new AphrontAjaxResponse())->setContent($views); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); // TODO: This is a reasonable approximation of the feature as it exists // in Facebook trunk but we should probably pull filename data, sort these, // figure out next/prev/edit/delete, deal with out-of-date inlines, etc. $inlines = id(new DifferentialInlineComment())->loadAllWhere('authorPHID = %s AND revisionID = %d AND commentID IS NULL', $user->getPHID(), $this->revisionID); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $phids = array($user->getPHID()); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $views = array(); foreach ($inlines as $inline) { $view = new DifferentialInlineCommentView(); $view->setInlineComment($inline); $view->setMarkupEngine($engine); $view->setHandles($handles); $view->setEditable(false); $view->setPreview(true); $views[] = $view->render(); } $views = implode("\n", $views); return id(new AphrontAjaxResponse())->setContent($views); }
public function processRequest() { $request = $this->getRequest(); $author_phid = $request->getUser()->getPHID(); $action = $request->getStr('action'); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $comment = new DifferentialComment(); $comment->setContent($request->getStr('content')); $comment->setAction($action); $comment->setAuthorPHID($author_phid); $handles = array($author_phid); $reviewers = $request->getStrList('reviewers'); if (DifferentialAction::allowReviewers($action) && $reviewers) { $comment->setMetadata(array(DifferentialComment::METADATA_ADDED_REVIEWERS => $reviewers)); $handles = array_merge($handles, $reviewers); } $ccs = $request->getStrList('ccs'); if ($action == DifferentialAction::ACTION_ADDCCS && $ccs) { $comment->setMetadata(array(DifferentialComment::METADATA_ADDED_CCS => $ccs)); $handles = array_merge($handles, $ccs); } $handles = $this->loadViewerHandles($handles); $view = new DifferentialRevisionCommentView(); $view->setUser($request->getUser()); $view->setComment($comment); $view->setHandles($handles); $view->setMarkupEngine($engine); $view->setPreview(true); $view->setTargetDiff(null); $metadata = array('reviewers' => $reviewers, 'ccs' => $ccs); if ($action != DifferentialAction::ACTION_COMMENT) { $metadata['action'] = $action; } id(new PhabricatorDraft())->setAuthorPHID($author_phid)->setDraftKey('differential-comment-' . $this->id)->setDraft($comment->getContent())->setMetadata($metadata)->replaceOrDelete(); return id(new AphrontAjaxResponse())->setContent($view->render()); }
public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newDifferentialMarkupEngine(); }
public function processRequest() { $drequest = $this->getDiffusionRequest(); $request = $this->getRequest(); $user = $request->getUser(); $callsign = $drequest->getRepository()->getCallsign(); $content = array(); $content[] = $this->buildCrumbs(array('commit' => true)); $detail_panel = new AphrontPanelView(); $repository = $drequest->getRepository(); $commit = $drequest->loadCommit(); if (!$commit) { // TODO: Make more user-friendly. throw new Exception('This commit has not parsed yet.'); } $commit_data = $drequest->loadCommitData(); $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub'); if ($is_foreign) { $subpath = $commit_data->getCommitDetail('svn-subpath'); $error_panel = new AphrontErrorView(); $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE); $error_panel->setTitle('Commit Not Tracked'); $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING); $error_panel->appendChild("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('" . phutil_escape_html($subpath) . "'), so no information is available."); $content[] = $error_panel; } else { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); require_celerity_resource('diffusion-commit-view-css'); require_celerity_resource('phabricator-remarkup-css'); $property_table = $this->renderPropertyTable($commit, $commit_data); $detail_panel->appendChild('<div class="diffusion-commit-view">' . '<div class="diffusion-commit-dateline">' . 'r' . $callsign . $commit->getCommitIdentifier() . ' · ' . phabricator_datetime($commit->getEpoch(), $user) . '</div>' . '<h1>Revision Detail</h1>' . '<div class="diffusion-commit-details">' . $property_table . '<hr />' . '<div class="diffusion-commit-message phabricator-remarkup">' . $engine->markupText($commit_data->getCommitMessage()) . '</div>' . '</div>' . '</div>'); $content[] = $detail_panel; } $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); $changes = $change_query->loadChanges(); $original_changes_count = count($changes); if ($request->getStr('show_all') !== 'true' && $original_changes_count > self::CHANGES_LIMIT) { $changes = array_slice($changes, 0, self::CHANGES_LIMIT); } $change_table = new DiffusionCommitChangeTableView(); $change_table->setDiffusionRequest($drequest); $change_table->setPathChanges($changes); $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) { $error_panel = new AphrontErrorView(); $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE); $error_panel->setTitle('Bad Commit'); $error_panel->appendChild(phutil_escape_html($bad_commit['description'])); $content[] = $error_panel; } else { if ($is_foreign) { // Don't render anything else. } else { if (!count($changes)) { $no_changes = new AphrontErrorView(); $no_changes->setWidth(AphrontErrorView::WIDTH_WIDE); $no_changes->setSeverity(AphrontErrorView::SEVERITY_WARNING); $no_changes->setTitle('Not Yet Parsed'); // TODO: This can also happen with weird SVN changes that don't do // anything (or only alter properties?), although the real no-changes case // is extremely rare and might be impossible to produce organically. We // should probably write some kind of "Nothing Happened!" change into the // DB once we parse these changes so we can distinguish between // "not parsed yet" and "no changes". $no_changes->appendChild("This commit hasn't been fully parsed yet (or doesn't affect any " . "paths)."); $content[] = $no_changes; } else { $change_panel = new AphrontPanelView(); $change_panel->setHeader("Changes (" . number_format($count) . ")"); if ($count !== $original_changes_count) { $show_all_button = phutil_render_tag('a', array('class' => 'button green', 'href' => '?show_all=true'), phutil_escape_html('Show All Changes')); $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle(sprintf("Showing only the first %d changes out of %s!", self::CHANGES_LIMIT, number_format($original_changes_count))); $change_panel->appendChild($warning_view); $change_panel->addButton($show_all_button); } $change_panel->appendChild($change_table); $content[] = $change_panel; $changesets = DiffusionPathChange::convertToDifferentialChangesets($changes); $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $vcs_supports_directory_changes = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $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; } } $branch = $drequest->getBranchURIComponent($drequest->getBranch()); $filename = $changeset->getFilename(); $commit = $drequest->getCommit(); $reference = "{$branch}{$filename};{$commit}"; $references[$key] = $reference; } $change_list = new DifferentialChangesetListView(); $change_list->setChangesets($changesets); $change_list->setRenderingReferences($references); $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/'); // TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css'); $change_list = '<div class="differential-primary-pane">' . $change_list->render() . '</div>'; $content[] = $change_list; } } } return $this->buildStandardPageResponse($content, array('title' => 'Diffusion')); }
public function processRequest() { $request = $this->getRequest(); $author_phid = $request->getUser()->getPHID(); $rendering_reference = $request->getStr('ref'); $parts = explode('/', $rendering_reference); if (count($parts) == 2) { list($id, $vs) = $parts; } else { $id = $parts[0]; $vs = 0; } $id = (int) $id; $vs = (int) $vs; $changeset = id(new DifferentialChangeset())->load($id); if (!$changeset) { return new Aphront404Response(); } $view = $request->getStr('view'); if ($view) { $changeset->attachHunks($changeset->loadHunks()); switch ($view) { case 'new': return $this->buildRawFileResponse($changeset->makeNewFile()); case 'old': return $this->buildRawFileResponse($changeset->makeOldFile()); default: return new Aphront400Response(); } } if ($vs && $vs != -1) { $vs_changeset = id(new DifferentialChangeset())->load($vs); if (!$vs_changeset) { return new Aphront404Response(); } } if (!$vs) { $right = $changeset; $left = null; $right_source = $right->getID(); $right_new = true; $left_source = $right->getID(); $left_new = false; $render_cache_key = $right->getID(); } else { if ($vs == -1) { $right = null; $left = $changeset; $right_source = $left->getID(); $right_new = false; $left_source = $left->getID(); $left_new = true; $render_cache_key = null; } else { $right = $changeset; $left = $vs_changeset; $right_source = $right->getID(); $right_new = true; $left_source = $left->getID(); $left_new = true; $render_cache_key = null; } } if ($left) { $left->attachHunks($left->loadHunks()); } if ($right) { $right->attachHunks($right->loadHunks()); } if ($left) { $left_data = $left->makeNewFile(); if ($right) { $right_data = $right->makeNewFile(); } else { $right_data = $left->makeOldFile(); } $engine = new PhabricatorDifferenceEngine(); $synthetic = $engine->generateChangesetFromFileContent($left_data, $right_data); $choice = nonempty($left, $right); $choice->attachHunks($synthetic->getHunks()); $changeset = $choice; $changeset->setID(null); } $spec = $request->getStr('range'); list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec); $parser = new DifferentialChangesetParser(); $parser->setChangeset($changeset); $parser->setRenderingReference($rendering_reference); $parser->setRenderCacheKey($render_cache_key); $parser->setRightSideCommentMapping($right_source, $right_new); $parser->setLeftSideCommentMapping($left_source, $left_new); $parser->setWhitespaceMode($request->getStr('whitespace')); // Load both left-side and right-side inline comments. $inlines = $this->loadInlineComments(array($left_source, $right_source), $author_phid); $phids = array(); foreach ($inlines as $inline) { $parser->parseInlineComment($inline); $phids[$inline->getAuthorPHID()] = true; } $phids = array_keys($phids); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $parser->setHandles($handles); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $parser->setMarkupEngine($engine); if ($request->isAjax()) { // TODO: This is sort of lazy, the effect is just to not render "Edit" // links on the "standalone view". $parser->setUser($request->getUser()); } $output = $parser->render($range_s, $range_e, $mask); if ($request->isAjax()) { return id(new AphrontAjaxResponse())->setContent($output); } Javelin::initBehavior('differential-show-more', array('uri' => '/differential/changeset/', 'whitespace' => $request->getStr('whitespace'))); Javelin::initBehavior('differential-comment-jump', array()); $detail = new DifferentialChangesetDetailView(); $detail->setChangeset($changeset); $detail->appendChild($output); if (!$vs) { $detail->addButton(phutil_render_tag('a', array('href' => $request->getRequestURI()->alter('view', 'old'), 'class' => 'grey button small'), 'View Raw File (Old Version)')); $detail->addButton(phutil_render_tag('a', array('href' => $request->getRequestURI()->alter('view', 'new'), 'class' => 'grey button small'), 'View Raw File (New Version)')); } $detail->setRevisionID($request->getInt('revision_id')); $output = id(new DifferentialPrimaryPaneView())->setLineWidthFromChangesets(array($changeset))->appendChild('<div class="differential-review-stage" ' . 'id="differential-review-stage">' . $detail->render() . '</div>'); return $this->buildStandardPageResponse(array($output), array('title' => 'Changeset View')); }
public function processRequest() { $request = $this->getRequest(); $author_phid = $request->getUser()->getPHID(); $rendering_reference = $request->getStr('ref'); $parts = explode('/', $rendering_reference); if (count($parts) == 2) { list($id, $vs) = $parts; } else { $id = $parts[0]; $vs = 0; } $id = (int) $id; $vs = (int) $vs; $changeset = id(new DifferentialChangeset())->load($id); if (!$changeset) { return new Aphront404Response(); } $view = $request->getStr('view'); if ($view) { $changeset->attachHunks($changeset->loadHunks()); $phid = idx($changeset->getMetadata(), "{$view}:binary-phid"); if ($phid) { return id(new AphrontRedirectResponse())->setURI("/file/info/{$phid}/"); } switch ($view) { case 'new': return $this->buildRawFileResponse($changeset, $is_new = true); case 'old': if ($vs && $vs != -1) { $vs_changeset = id(new DifferentialChangeset())->load($vs); if ($vs_changeset) { $vs_changeset->attachHunks($vs_changeset->loadHunks()); return $this->buildRawFileResponse($vs_changeset, $is_new = true); } } return $this->buildRawFileResponse($changeset, $is_new = false); default: return new Aphront400Response(); } } if ($vs && $vs != -1) { $vs_changeset = id(new DifferentialChangeset())->load($vs); if (!$vs_changeset) { return new Aphront404Response(); } } if (!$vs) { $right = $changeset; $left = null; $right_source = $right->getID(); $right_new = true; $left_source = $right->getID(); $left_new = false; $render_cache_key = $right->getID(); } else { if ($vs == -1) { $right = null; $left = $changeset; $right_source = $left->getID(); $right_new = false; $left_source = $left->getID(); $left_new = true; $render_cache_key = null; } else { $right = $changeset; $left = $vs_changeset; $right_source = $right->getID(); $right_new = true; $left_source = $left->getID(); $left_new = true; $render_cache_key = null; } } if ($left) { $left->attachHunks($left->loadHunks()); } if ($right) { $right->attachHunks($right->loadHunks()); } if ($left) { $left_data = $left->makeNewFile(); if ($right) { $right_data = $right->makeNewFile(); } else { $right_data = $left->makeOldFile(); } $engine = new PhabricatorDifferenceEngine(); $synthetic = $engine->generateChangesetFromFileContent($left_data, $right_data); $choice = clone nonempty($left, $right); $choice->attachHunks($synthetic->getHunks()); $changeset = $choice; } $coverage = null; if ($right && $right->getDiffID()) { $unit = id(new DifferentialDiffProperty())->loadOneWhere('diffID = %d AND name = %s', $right->getDiffID(), 'arc:unit'); if ($unit) { $coverage = array(); foreach ($unit->getData() as $result) { $result_coverage = idx($result, 'coverage'); if (!$result_coverage) { continue; } $file_coverage = idx($result_coverage, $right->getFileName()); if (!$file_coverage) { continue; } $coverage[] = $file_coverage; } $coverage = ArcanistUnitTestResult::mergeCoverage($coverage); } } $spec = $request->getStr('range'); list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec); $parser = new DifferentialChangesetParser(); $parser->setCoverage($coverage); $parser->setChangeset($changeset); $parser->setRenderingReference($rendering_reference); $parser->setRenderCacheKey($render_cache_key); $parser->setRightSideCommentMapping($right_source, $right_new); $parser->setLeftSideCommentMapping($left_source, $left_new); $parser->setWhitespaceMode($request->getStr('whitespace')); if ($left && $right) { $parser->setOriginals($left, $right); } // Load both left-side and right-side inline comments. $inlines = $this->loadInlineComments(array($left_source, $right_source), $author_phid); if ($left_new) { $inlines = array_merge($inlines, $this->buildLintInlineComments($left)); } if ($right_new) { $inlines = array_merge($inlines, $this->buildLintInlineComments($right)); } $phids = array(); foreach ($inlines as $inline) { $parser->parseInlineComment($inline); if ($inline->getAuthorPHID()) { $phids[$inline->getAuthorPHID()] = true; } } $phids = array_keys($phids); $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); $parser->setHandles($handles); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $parser->setMarkupEngine($engine); if ($request->isAjax()) { // TODO: This is sort of lazy, the effect is just to not render "Edit" // and "Reply" links on the "standalone view". $parser->setUser($request->getUser()); } $output = $parser->render($range_s, $range_e, $mask); $mcov = $parser->renderModifiedCoverage(); if ($request->isAjax()) { $coverage = array('differential-mcoverage-' . md5($changeset->getFilename()) => $mcov); return id(new PhabricatorChangesetResponse())->setRenderedChangeset($output)->setCoverage($coverage); } Javelin::initBehavior('differential-show-more', array('uri' => '/differential/changeset/', 'whitespace' => $request->getStr('whitespace'))); Javelin::initBehavior('differential-comment-jump', array()); $detail = new DifferentialChangesetDetailView(); $detail->setChangeset($changeset); $detail->appendChild($output); $detail->setVsChangesetID($left_source); $output = id(new DifferentialPrimaryPaneView())->setLineWidthFromChangesets(array($changeset))->appendChild('<div class="differential-review-stage" ' . 'id="differential-review-stage">' . $detail->render() . '</div>'); return $this->buildStandardPageResponse(array($output), array('title' => 'Changeset View')); }
public function processRequest() { $drequest = $this->getDiffusionRequest(); $request = $this->getRequest(); $user = $request->getUser(); if ($request->getStr('diff')) { return $this->buildRawDiffResponse($drequest); } $callsign = $drequest->getRepository()->getCallsign(); $content = array(); $content[] = $this->buildCrumbs(array('commit' => true)); $repository = $drequest->getRepository(); $commit = $drequest->loadCommit(); if (!$commit) { // TODO: Make more user-friendly. throw new Exception('This commit has not parsed yet.'); } $commit_data = $drequest->loadCommitData(); $commit->attachCommitData($commit_data); $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub'); if ($is_foreign) { $subpath = $commit_data->getCommitDetail('svn-subpath'); $error_panel = new AphrontErrorView(); $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE); $error_panel->setTitle('Commit Not Tracked'); $error_panel->setSeverity(AphrontErrorView::SEVERITY_WARNING); $error_panel->appendChild("This Diffusion repository is configured to track only one " . "subdirectory of the entire Subversion repository, and this commit " . "didn't affect the tracked subdirectory ('" . phutil_escape_html($subpath) . "'), so no information is available."); $content[] = $error_panel; } else { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); require_celerity_resource('diffusion-commit-view-css'); require_celerity_resource('phabricator-remarkup-css'); $parent_query = DiffusionCommitParentsQuery::newFromDiffusionRequest($drequest); $headsup_panel = new AphrontHeadsupView(); $headsup_panel->setHeader('Commit Detail'); $headsup_panel->setActionList($this->renderHeadsupActionList($commit)); $headsup_panel->setProperties($this->getCommitProperties($commit, $commit_data, $parent_query->loadParents())); $headsup_panel->appendChild('<div class="diffusion-commit-message phabricator-remarkup">' . $engine->markupText($commit_data->getCommitMessage()) . '</div>'); $content[] = $headsup_panel; } $query = new PhabricatorAuditQuery(); $query->withCommitPHIDs(array($commit->getPHID())); $audit_requests = $query->execute(); $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user); $content[] = $this->buildAuditTable($commit, $audit_requests); $content[] = $this->buildComments($commit); $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); $changes = $change_query->loadChanges(); $content[] = $this->buildMergesTable($commit); $original_changes_count = count($changes); if ($request->getStr('show_all') !== 'true' && $original_changes_count > self::CHANGES_LIMIT) { $changes = array_slice($changes, 0, self::CHANGES_LIMIT); } $owners_paths = array(); if ($this->highlightedAudits) { $packages = id(new PhabricatorOwnersPackage())->loadAllWhere('phid IN (%Ls)', mpull($this->highlightedAudits, '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()); } $pane_id = null; if ($bad_commit) { $error_panel = new AphrontErrorView(); $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE); $error_panel->setTitle('Bad Commit'); $error_panel->appendChild(phutil_escape_html($bad_commit['description'])); $content[] = $error_panel; } else { if ($is_foreign) { // Don't render anything else. } else { if (!count($changes)) { $no_changes = new AphrontErrorView(); $no_changes->setWidth(AphrontErrorView::WIDTH_WIDE); $no_changes->setSeverity(AphrontErrorView::SEVERITY_WARNING); $no_changes->setTitle('Not Yet Parsed'); // TODO: This can also happen with weird SVN changes that don't do // anything (or only alter properties?), although the real no-changes case // is extremely rare and might be impossible to produce organically. We // should probably write some kind of "Nothing Happened!" change into the // DB once we parse these changes so we can distinguish between // "not parsed yet" and "no changes". $no_changes->appendChild("This commit hasn't been fully parsed yet (or doesn't affect any " . "paths)."); $content[] = $no_changes; } else { $change_panel = new AphrontPanelView(); $change_panel->setHeader("Changes (" . number_format($count) . ")"); $change_panel->setID('differential-review-toc'); if ($count !== $original_changes_count) { $show_all_button = phutil_render_tag('a', array('class' => 'button green', 'href' => '?show_all=true'), phutil_escape_html('Show All Changes')); $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle(sprintf("Showing only the first %d changes out of %s!", self::CHANGES_LIMIT, number_format($original_changes_count))); $change_panel->appendChild($warning_view); $change_panel->addButton($show_all_button); } $change_panel->appendChild($change_table); $content[] = $change_panel; $changesets = DiffusionPathChange::convertToDifferentialChangesets($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). $pquery = new DiffusionPathIDQuery(mpull($changesets, 'getFilename')); $path_ids = $pquery->loadPathIDs(); foreach ($changesets as $changeset) { $changeset->makeEphemeral(); $changeset->setID($path_ids[$changeset->getFilename()]); } $change_list = new DifferentialChangesetListView(); $change_list->setChangesets($changesets); $change_list->setVisibleChangesets($changesets); $change_list->setRenderingReferences($references); $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/'); $change_list->setRepository($repository); $change_list->setUser($user); $change_list->setStandaloneURI('/diffusion/' . $callsign . '/diff/'); $change_list->setRawFileURIs(null, '/diffusion/' . $callsign . '/diff/?view=r'); $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/'); // TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css'); $pane_id = celerity_generate_unique_node_id(); $add_comment_view = $this->renderAddCommentPanel($commit, $audit_requests, $pane_id); $main_pane = phutil_render_tag('div', array('class' => 'differential-primary-pane', 'id' => $pane_id), $change_list->render() . $add_comment_view); $content[] = $main_pane; } } } return $this->buildStandardPageResponse($content, array('title' => 'r' . $callsign . $commit->getCommitIdentifier())); }
private function buildRenderedCommentResponse(PhabricatorInlineCommentInterface $inline, $on_right) { $request = $this->getRequest(); $user = $request->getUser(); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $phids = array($user->getPHID()); $handles = $this->loadViewerHandles($phids); $view = new DifferentialInlineCommentView(); $view->setInlineComment($inline); $view->setOnRight($on_right); $view->setBuildScaffolding(true); $view->setMarkupEngine($engine); $view->setHandles($handles); $view->setEditable(true); return id(new AphrontAjaxResponse())->setContent(array('inlineCommentID' => $inline->getID(), 'markup' => $view->render())); }
public function processRequest() { $drequest = $this->getDiffusionRequest(); $request = $this->getRequest(); $user = $request->getUser(); $callsign = $drequest->getRepository()->getCallsign(); $content = array(); $content[] = $this->buildCrumbs(array('commit' => true)); $detail_panel = new AphrontPanelView(); $repository = $drequest->getRepository(); $commit = $drequest->loadCommit(); if (!$commit) { // TODO: Make more user-friendly. throw new Exception('This commit has not parsed yet.'); } $commit_data = $drequest->loadCommitData(); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); require_celerity_resource('diffusion-commit-view-css'); require_celerity_resource('phabricator-remarkup-css'); $property_table = $this->renderPropertyTable($commit, $commit_data); $detail_panel->appendChild('<div class="diffusion-commit-view">' . '<div class="diffusion-commit-dateline">' . 'r' . $callsign . $commit->getCommitIdentifier() . ' · ' . phabricator_datetime($commit->getEpoch(), $user) . '</div>' . '<h1>Revision Detail</h1>' . '<div class="diffusion-commit-details">' . $property_table . '<hr />' . '<div class="diffusion-commit-message phabricator-remarkup">' . $engine->markupText($commit_data->getCommitMessage()) . '</div>' . '</div>' . '</div>'); $content[] = $detail_panel; $change_query = DiffusionPathChangeQuery::newFromDiffusionRequest($drequest); $changes = $change_query->loadChanges(); $original_changes_count = count($changes); if ($request->getStr('show_all') !== 'true' && $original_changes_count > self::CHANGES_LIMIT) { $changes = array_slice($changes, 0, self::CHANGES_LIMIT); } $change_table = new DiffusionCommitChangeTableView(); $change_table->setDiffusionRequest($drequest); $change_table->setPathChanges($changes); $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) { $error_panel = new AphrontErrorView(); $error_panel->setWidth(AphrontErrorView::WIDTH_WIDE); $error_panel->setTitle('Bad Commit'); $error_panel->appendChild(phutil_escape_html($bad_commit['description'])); $content[] = $error_panel; } else { $change_panel = new AphrontPanelView(); $change_panel->setHeader("Changes (" . number_format($count) . ")"); if ($count !== $original_changes_count) { $show_all_button = phutil_render_tag('a', array('class' => 'button green', 'href' => '?show_all=true'), phutil_escape_html('Show All Changes')); $warning_view = id(new AphrontErrorView())->setSeverity(AphrontErrorView::SEVERITY_WARNING)->setTitle(sprintf("Showing only the first %d changes out of %s!", self::CHANGES_LIMIT, number_format($original_changes_count))); $change_panel->appendChild($warning_view); $change_panel->addButton($show_all_button); } $change_panel->appendChild($change_table); $content[] = $change_panel; if ($changes) { $changesets = DiffusionPathChange::convertToDifferentialChangesets($changes); $vcs = $repository->getVersionControlSystem(); switch ($vcs) { case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $vcs_supports_directory_changes = true; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $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; } } $branch = $drequest->getBranchURIComponent($drequest->getBranch()); $filename = $changeset->getFilename(); $commit = $drequest->getCommit(); $reference = "{$branch}{$filename};{$commit}"; $references[$key] = $reference; } $change_list = new DifferentialChangesetListView(); $change_list->setChangesets($changesets); $change_list->setRenderingReferences($references); $change_list->setRenderURI('/diffusion/' . $callsign . '/diff/'); // TODO: This is pretty awkward, unify the CSS between Diffusion and // Differential better. require_celerity_resource('differential-core-view-css'); $change_list = '<div class="differential-primary-pane">' . $change_list->render() . '</div>'; } else { $change_list = '<div style="margin: 2em; color: #666; padding: 1em; background: #eee;">' . '(no changes blah blah)' . '</div>'; } $content[] = $change_list; } return $this->buildStandardPageResponse($content, array('title' => 'Diffusion')); }
public function handleRequest(AphrontRequest $request) { $response = $this->loadDiffusionContext(); if ($response) { return $response; } $drequest = $this->getDiffusionRequest(); $viewer = $request->getUser(); if ($request->getStr('diff')) { return $this->buildRawDiffResponse($drequest); } $repository = $drequest->getRepository(); $commit = id(new DiffusionCommitQuery())->setViewer($viewer)->withRepository($repository)->withIdentifiers(array($drequest->getCommit()))->needCommitData(true)->needAuditRequests(true)->executeOne(); $crumbs = $this->buildCrumbs(array('commit' => true)); $crumbs->setBorder(true); if (!$commit) { if (!$this->getCommitExists()) { 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.')); $title = pht('Commit Still Parsing'); return $this->newPage()->setTitle($title)->setCrumbs($crumbs)->appendChild($error); } $audit_requests = $commit->getAudits(); $this->auditAuthorityPHIDs = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($viewer); $commit_data = $commit->getCommitData(); $is_foreign = $commit_data->getCommitDetail('foreign-svn-stub'); $error_panel = 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)); } else { $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(); $engine->setConfig('viewer', $viewer); $commit_tag = $this->renderCommitHashTag($drequest); $header = id(new PHUIHeaderView())->setHeader(nonempty($commit->getSummary(), pht('Commit Detail')))->setHeaderIcon('fa-code-fork')->addTag($commit_tag); if ($commit->getAuditStatus()) { $icon = PhabricatorAuditCommitStatusConstants::getStatusIcon($commit->getAuditStatus()); $color = PhabricatorAuditCommitStatusConstants::getStatusColor($commit->getAuditStatus()); $status = PhabricatorAuditCommitStatusConstants::getStatusName($commit->getAuditStatus()); $header->setStatus($icon, $color, $status); } $curtain = $this->buildCurtain($commit, $repository); $subheader = $this->buildSubheaderView($commit, $commit_data); $details = $this->buildPropertyListView($commit, $commit_data, $audit_requests); $message = $commit_data->getCommitMessage(); $revision = $commit->getCommitIdentifier(); $message = $this->linkBugtraq($message); $message = $engine->markupText($message); $detail_list = new PHUIPropertyListView(); $detail_list->addTextContent(phutil_tag('div', array('class' => 'diffusion-commit-message phabricator-remarkup'), $message)); if ($this->getCommitErrors()) { $error_panel = id(new PHUIInfoView())->appendChild($this->getCommitErrors())->setSeverity(PHUIInfoView::SEVERITY_WARNING); } } $timeline = $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); } $merge_table = $this->buildMergesTable($commit); $highlighted_audits = $commit->getAuthorityAudits($viewer, $this->auditAuthorityPHIDs); $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, $commit->getMonogram()); } $show_changesets = false; $info_panel = null; $change_list = null; $change_table = null; if ($bad_commit) { $info_panel = $this->renderStatusMessage(pht('Bad Commit'), $bad_commit['description']); } else { if ($is_foreign) { // Don't render anything else. } else { if (!$commit->isImported()) { $info_panel = $this->renderStatusMessage(pht('Still Importing...'), pht('This commit is still importing. Changes will be visible once ' . 'the import finishes.')); } else { if (!count($changes)) { $info_panel = $this->renderStatusMessage(pht('Empty Commit'), pht('This commit is empty and does not affect any paths.')); } else { if ($was_limited) { $info_panel = $this->renderStatusMessage(pht('Enormous Commit'), pht('This commit is enormous, and affects more than %d files. ' . 'Changes are not shown.', $hard_limit)); } else { if (!$this->getCommitExists()) { $info_panel = $this->renderStatusMessage(pht('Commit No Longer Exists'), pht('This commit no longer exists in the repository.')); } else { $show_changesets = true; // 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_header = id(new PHUIHeaderView())->setHeader(pht('Changes (%s)', new PhutilNumber($count))); $warning_view = null; if ($count > self::CHANGES_LIMIT && !$show_all_details) { $button = id(new PHUIButtonView())->setText(pht('Show All Changes'))->setHref('?show_all=true')->setTag('a')->setIcon('fa-files-o'); $warning_view = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_WARNING)->setTitle(pht('Very Large Commit'))->appendChild(pht('This commit is very large. Load each file individually.')); $change_header->addActionLink($button); } $changesets = DiffusionPathChange::convertToDifferentialChangesets($viewer, $changes); // TODO: This table and panel shouldn't really be separate, but we need // to clean up the "Load All Files" interaction first. $change_table = $this->buildTableOfContents($changesets, $change_header, $warning_view); $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(pht('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($viewer, $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 = $commit->getDisplayName(); $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($repository->getPathURI('diff/')); $change_list->setRepository($repository); $change_list->setUser($viewer); $change_list->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); // TODO: Try to setBranch() to something reasonable here? $change_list->setStandaloneURI($repository->getPathURI('diff/')); $change_list->setRawFileURIs(null, $repository->getPathURI('diff/?view=r')); $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/'); } } } } } } $add_comment = $this->renderAddCommentPanel($commit, $audit_requests); $filetree_on = $viewer->compareUserSetting(PhabricatorShowFiletreeSetting::SETTINGKEY, PhabricatorShowFiletreeSetting::VALUE_ENABLE_FILETREE); $pref_collapse = PhabricatorFiletreeVisibleSetting::SETTINGKEY; $collapsed = $viewer->getUserSetting($pref_collapse); $nav = null; if ($show_changesets && $filetree_on) { $nav = id(new DifferentialChangesetFileTreeSideNavBuilder())->setTitle($commit->getDisplayName())->setBaseURI(new PhutilURI($commit->getURI()))->build($changesets)->setCrumbs($crumbs)->setCollapsed((bool) $collapsed); } $view = id(new PHUITwoColumnView())->setHeader($header)->setSubheader($subheader)->setMainColumn(array($error_panel, $timeline, $merge_table, $info_panel))->setFooter(array($change_table, $change_list, $add_comment))->addPropertySection(pht('Description'), $detail_list)->addPropertySection(pht('Details'), $details)->setCurtain($curtain); $page = $this->newPage()->setTitle($commit->getDisplayName())->setCrumbs($crumbs)->setPageObjectPHIDS(array($commit->getPHID()))->appendChild(array($view)); if ($nav) { $page->setNavigation($nav); } return $page; }
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'); 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)); $headsup_view->setTall(true); $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); $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()); } $show_changesets = false; 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 { $show_changesets = true; // 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(pht('Changes (%s)', new PhutilNumber($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(pht('Very Large Commit'))->appendChild(pht('This commit is very large. Load each file individually.')); $change_panel->setInfoView($warning_view); $header->addActionLink($button); } $changesets = DiffusionPathChange::convertToDifferentialChangesets($user, $changes); // TODO: This table and panel shouldn't really be separate, but we need // to clean up the "Load All Files" interaction first. $change_table = $this->buildTableOfContents($changesets); $change_panel->setTable($change_table); $change_panel->setHeader($header); $content[] = $change_panel; $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(pht('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(null, '/diffusion/' . $callsign . '/diff/?view=r'); $change_list->setInlineCommentControllerURI('/diffusion/inline/edit/' . phutil_escape_uri($commit->getPHID()) . '/'); $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 ($show_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()))); }
public function render() { require_celerity_resource('differential-revision-comment-list-css'); $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine(array('differential.diff' => $this->target)); $inlines = mgroup($this->inlines, 'getCommentID'); $num = 1; $html = array(); foreach ($this->comments as $comment) { $view = new DifferentialRevisionCommentView(); $view->setComment($comment); $view->setUser($this->user); $view->setHandles($this->handles); $view->setMarkupEngine($engine); $view->setInlineComments(idx($inlines, $comment->getID(), array())); $view->setChangesets($this->changesets); $view->setTargetDiff($this->target); $view->setVersusDiffID($this->versusDiffID); $view->setCommentNumber($num++); $html[] = $view->render(); } $objs = array_reverse(array_values($this->comments)); $html = array_reverse(array_values($html)); $user = $this->user; $last_comment = null; // Find the most recent comment by the viewer. foreach ($objs as $position => $comment) { if ($user && $comment->getAuthorPHID() == $user->getPHID()) { if ($last_comment === null) { $last_comment = $position; } else { if ($last_comment == $position - 1) { // If the viewer made several comments in a row, show them all. This // is a spaz rule for epriestley. $last_comment = $position; } } } } $header = array(); $hidden = array(); if ($last_comment !== null) { foreach ($objs as $position => $comment) { if (!$comment->getID()) { // These are synthetic comments with summary/test plan information. $header[] = $html[$position]; unset($html[$position]); continue; } if ($position <= $last_comment) { // Always show comments after the viewer's last comment. continue; } if ($position < 3) { // Always show the 3 most recent comments. continue; } $hidden[] = $position; } } if (count($hidden) <= 3) { // Don't hide if there's not much to hide. $hidden = array(); } $header = array_reverse($header); $hidden = array_select_keys($html, $hidden); $visible = array_diff_key($html, $hidden); $hidden = array_reverse($hidden); $visible = array_reverse($visible); if ($hidden) { Javelin::initBehavior('differential-show-all-comments', array('markup' => implode("\n", $hidden))); $hidden = javelin_render_tag('div', array('sigil' => "differential-all-comments-container"), '<div class="differential-older-comments-are-hidden">' . number_format(count($hidden)) . ' older comments are hidden. ' . javelin_render_tag('a', array('href' => '#', 'mustcapture' => true, 'sigil' => 'differential-show-all-comments'), 'Show all comments.') . '</div>'); } else { $hidden = null; } return '<div class="differential-comment-list">' . implode("\n", $header) . $hidden . implode("\n", $visible) . '</div>'; }