Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php
index a445020e67..e5c5fbbc83 100644
--- a/src/applications/differential/controller/DifferentialChangesetViewController.php
+++ b/src/applications/differential/controller/DifferentialChangesetViewController.php
@@ -1,355 +1,374 @@
<?php
final class DifferentialChangesetViewController extends DifferentialController {
public function shouldRequireLogin() {
- return !$this->allowsAnonymousAccess();
+ if ($this->allowsAnonymousAccess()) {
+ return false;
+ }
+
+ return parent::shouldRequireLogin();
+ }
+
+ public function shouldAllowPublic() {
+ return true;
}
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();
}
+ // TODO: (T603) Make Changeset policy-aware. For now, just fake it
+ // by making sure we can see the diff.
+ $diff = id(new DifferentialDiffQuery())
+ ->setViewer($request->getUser())
+ ->withIDs(array($changeset->getDiffID()))
+ ->executeOne();
+ if (!$diff) {
+ 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 ($request->getStr('renderer') == '1up') {
$parser->setRenderer(new DifferentialChangesetOneUpRenderer());
}
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 = $this->loadViewerHandles($phids);
$parser->setHandles($handles);
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($request->getUser());
foreach ($inlines as $inline) {
$engine->addObject(
$inline,
PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
}
$engine->process();
$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());
// TODO: [HTML] Clean up DifferentialChangesetParser output, but it's
// undergoing like six kinds of refactoring anyway.
$output = phutil_safe_html($output);
$detail = new DifferentialChangesetDetailView();
$detail->setChangeset($changeset);
$detail->appendChild($output);
$detail->setVsChangesetID($left_source);
$panel = new DifferentialPrimaryPaneView();
$panel->appendChild(
phutil_tag(
'div',
array(
'class' => 'differential-review-stage',
'id' => 'differential-review-stage',
),
$detail->render()));
return $this->buildApplicationPage(
array(
$panel
),
array(
'title' => pht('Changeset View'),
));
}
private function loadInlineComments(array $changeset_ids, $author_phid) {
$changeset_ids = array_unique(array_filter($changeset_ids));
if (!$changeset_ids) {
return;
}
return id(new DifferentialInlineCommentQuery())
->withViewerAndChangesetIDs($author_phid, $changeset_ids)
->execute();
}
private function buildRawFileResponse(
DifferentialChangeset $changeset,
$is_new) {
if ($is_new) {
$key = 'raw:new:phid';
} else {
$key = 'raw:old:phid';
}
$metadata = $changeset->getMetadata();
$file = null;
$phid = idx($metadata, $key);
if ($phid) {
$file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$phid);
}
if (!$file) {
// This is just building a cache of the changeset content in the file
// tool, and is safe to run on a read pathway.
$unguard = AphrontWriteGuard::beginScopedUnguardedWrites();
if ($is_new) {
$data = $changeset->makeNewFile();
} else {
$data = $changeset->makeOldFile();
}
$file = PhabricatorFile::newFromFileData(
$data,
array(
'name' => $changeset->getFilename(),
'mime-type' => 'text/plain',
));
$metadata[$key] = $file->getPHID();
$changeset->setMetadata($metadata);
$changeset->save();
unset($unguard);
}
return id(new AphrontRedirectResponse())
->setURI($file->getBestURI());
}
private function buildLintInlineComments($changeset) {
$lint = id(new DifferentialDiffProperty())->loadOneWhere(
'diffID = %d AND name = %s',
$changeset->getDiffID(),
'arc:lint');
if (!$lint) {
return array();
}
$lint = $lint->getData();
$inlines = array();
foreach ($lint as $msg) {
if ($msg['path'] != $changeset->getFilename()) {
continue;
}
$inline = new DifferentialInlineComment();
$inline->setChangesetID($changeset->getID());
$inline->setIsNewFile(true);
$inline->setSyntheticAuthor('Lint: '.$msg['name']);
$inline->setLineNumber($msg['line']);
$inline->setLineLength(0);
$inline->setContent('%%%'.$msg['description'].'%%%');
$inlines[] = $inline;
}
return $inlines;
}
}
diff --git a/src/applications/differential/controller/DifferentialRevisionListController.php b/src/applications/differential/controller/DifferentialRevisionListController.php
index f7f9931be7..8322f5615b 100644
--- a/src/applications/differential/controller/DifferentialRevisionListController.php
+++ b/src/applications/differential/controller/DifferentialRevisionListController.php
@@ -1,95 +1,98 @@
<?php
final class DifferentialRevisionListController extends DifferentialController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey;
public function shouldRequireLogin() {
- return !$this->allowsAnonymousAccess();
+ if ($this->allowsAnonymousAccess()) {
+ return false;
+ }
+ return parent::shouldRequireLogin();
}
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->queryKey = idx($data, 'queryKey');
}
public function processRequest() {
$request = $this->getRequest();
$controller = id(new PhabricatorApplicationSearchController($request))
->setQueryKey($this->queryKey)
->setSearchEngine(new DifferentialRevisionSearchEngine())
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
public function renderResultsList(
array $revisions,
PhabricatorSavedQuery $query) {
assert_instances_of($revisions, 'DifferentialRevision');
$user = $this->getRequest()->getUser();
$template = id(new DifferentialRevisionListView())
->setUser($user)
->setFields(DifferentialRevisionListView::getDefaultFields($user));
$views = array();
if ($query->getQueryKey() == 'active') {
$split = DifferentialRevisionQuery::splitResponsible(
$revisions,
$query->getParameter('responsiblePHIDs'));
list($blocking, $active, $waiting) = $split;
$views[] = id(clone $template)
->setHeader(pht('Blocking Others'))
->setNoDataString(
pht('No revisions are blocked on your action.'))
->setHighlightAge(true)
->setRevisions($blocking)
->setHandles(array())
->loadAssets();
$views[] = id(clone $template)
->setHeader(pht('Action Required'))
->setNoDataString(
pht('No revisions require your action.'))
->setHighlightAge(true)
->setRevisions($active)
->setHandles(array())
->loadAssets();
$views[] = id(clone $template)
->setHeader(pht('Waiting on Others'))
->setNoDataString(
pht('You have no revisions waiting on others.'))
->setRevisions($waiting)
->setHandles(array())
->loadAssets();
} else {
$views[] = id(clone $template)
->setRevisions($revisions)
->setHandles(array())
->loadAssets();
}
$phids = array_mergev(mpull($views, 'getRequiredHandlePHIDs'));
$handles = $this->loadViewerHandles($phids);
foreach ($views as $view) {
$view->setHandles($handles);
}
if (count($views) == 1) {
// Reduce this to a PHUIObjectItemListView so we can get the free
// support from ApplicationSearch.
return head($views)->render();
} else {
return $views;
}
}
}
diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php
index 54750a094b..a1861e962f 100644
--- a/src/applications/differential/controller/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/DifferentialRevisionViewController.php
@@ -1,979 +1,999 @@
<?php
final class DifferentialRevisionViewController extends DifferentialController {
private $revisionID;
public function shouldRequireLogin() {
- return !$this->allowsAnonymousAccess();
+ if ($this->allowsAnonymousAccess()) {
+ return false;
+ }
+ return parent::shouldRequireLogin();
+ }
+
+ public function shouldAllowPublic() {
+ return true;
}
public function willProcessRequest(array $data) {
$this->revisionID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$viewer_is_anonymous = !$user->isLoggedIn();
$revision = id(new DifferentialRevisionQuery())
->withIDs(array($this->revisionID))
->setViewer($request->getUser())
->needRelationships(true)
->needReviewerStatus(true)
->executeOne();
if (!$revision) {
return new Aphront404Response();
}
$diffs = id(new DifferentialDiffQuery())
->setViewer($request->getUser())
->withRevisionIDs(array($this->revisionID))
->execute();
$diffs = array_reverse($diffs, $preserve_keys = true);
if (!$diffs) {
throw new Exception(
"This revision has no diffs. Something has gone quite wrong.");
}
$diff_vs = $request->getInt('vs');
$target_id = $request->getInt('id');
$target = idx($diffs, $target_id, end($diffs));
$target_manual = $target;
if (!$target_id) {
foreach ($diffs as $diff) {
if ($diff->getCreationMethod() != 'commit') {
$target_manual = $diff;
}
}
}
if (empty($diffs[$diff_vs])) {
$diff_vs = null;
}
$arc_project = $target->loadArcanistProject();
$repository = ($arc_project ? $arc_project->loadRepository() : null);
list($changesets, $vs_map, $vs_changesets, $rendering_references) =
$this->loadChangesetsAndVsMap(
$target,
idx($diffs, $diff_vs),
$repository);
if ($request->getExists('download')) {
return $this->buildRawDiffResponse(
$changesets,
$vs_changesets,
$vs_map,
$repository);
}
$props = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$target_manual->getID());
$props = mpull($props, 'getData', 'getName');
$aux_fields = $this->loadAuxiliaryFields($revision);
$comments = $revision->loadComments();
$comments = array_merge(
$this->getImplicitComments($revision, reset($diffs)),
$comments);
$all_changesets = $changesets;
$inlines = $this->loadInlineComments(
$revision,
$all_changesets);
$object_phids = array_merge(
$revision->getReviewers(),
$revision->getCCPHIDs(),
$revision->loadCommitPHIDs(),
array(
$revision->getAuthorPHID(),
$user->getPHID(),
),
mpull($comments, 'getAuthorPHID'));
foreach ($comments as $comment) {
$metadata = $comment->getMetadata();
$added_reviewers = idx(
$metadata,
DifferentialComment::METADATA_ADDED_REVIEWERS);
if ($added_reviewers) {
foreach ($added_reviewers as $phid) {
$object_phids[] = $phid;
}
}
$added_ccs = idx(
$metadata,
DifferentialComment::METADATA_ADDED_CCS);
if ($added_ccs) {
foreach ($added_ccs as $phid) {
$object_phids[] = $phid;
}
}
}
foreach ($revision->getAttached() as $type => $phids) {
foreach ($phids as $phid => $info) {
$object_phids[] = $phid;
}
}
$aux_phids = array();
foreach ($aux_fields as $key => $aux_field) {
$aux_field->setDiff($target);
$aux_field->setManualDiff($target_manual);
$aux_field->setDiffProperties($props);
$aux_phids[$key] = $aux_field->getRequiredHandlePHIDsForRevisionView();
}
$object_phids = array_merge($object_phids, array_mergev($aux_phids));
$object_phids = array_unique($object_phids);
$handles = $this->loadViewerHandles($object_phids);
foreach ($aux_fields as $key => $aux_field) {
// Make sure each field only has access to handles it specifically
// requested, not all handles. Otherwise you can get a field which works
// only in the presence of other fields.
$aux_field->setHandles(array_select_keys($handles, $aux_phids[$key]));
}
$reviewer_warning = null;
if ($revision->getStatus() ==
ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
$has_live_reviewer = false;
foreach ($revision->getReviewers() as $reviewer) {
if (!$handles[$reviewer]->isDisabled()) {
$has_live_reviewer = true;
break;
}
}
if (!$has_live_reviewer) {
$reviewer_warning = new AphrontErrorView();
$reviewer_warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$reviewer_warning->setTitle(pht('No Active Reviewers'));
if ($revision->getReviewers()) {
$reviewer_warning->appendChild(
phutil_tag(
'p',
array(),
pht('All specified reviewers are disabled and this revision '.
'needs review. You may want to add some new reviewers.')));
} else {
$reviewer_warning->appendChild(
phutil_tag(
'p',
array(),
pht('This revision has no specified reviewers and needs '.
'review. You may want to add some reviewers.')));
}
}
}
$request_uri = $request->getRequestURI();
$limit = 100;
$large = $request->getStr('large');
if (count($changesets) > $limit && !$large) {
$count = count($changesets);
$warning = new AphrontErrorView();
$warning->setTitle('Very Large Diff');
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
$warning->appendChild(hsprintf(
'%s <strong>%s</strong>',
pht(
'This diff is very large and affects %s files. Load each file '.
'individually.',
new PhutilNumber($count)),
phutil_tag(
'a',
array(
'href' => $request_uri
->alter('large', 'true')
->setFragment('toc'),
),
pht('Show All Files Inline'))));
$warning = $warning->render();
$my_inlines = id(new DifferentialInlineCommentQuery())
->withDraftComments($user->getPHID(), $this->revisionID)
->execute();
$visible_changesets = array();
foreach ($inlines + $my_inlines as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($changesets[$changeset_id])) {
$visible_changesets[$changeset_id] = $changesets[$changeset_id];
}
}
if (!empty($props['arc:lint'])) {
$changeset_paths = mpull($changesets, null, 'getFilename');
foreach ($props['arc:lint'] as $lint) {
$changeset = idx($changeset_paths, $lint['path']);
if ($changeset) {
$visible_changesets[$changeset->getID()] = $changeset;
}
}
}
} else {
$warning = null;
$visible_changesets = $changesets;
}
$revision_detail = id(new DifferentialRevisionDetailView())
->setUser($user)
->setRevision($revision)
->setDiff(end($diffs))
->setAuxiliaryFields($aux_fields)
->setURI($request->getRequestURI());
$actions = $this->getRevisionActions($revision);
$custom_renderer_class = PhabricatorEnv::getEnvConfig(
'differential.revision-custom-detail-renderer');
if ($custom_renderer_class) {
// TODO: build a better version of the action links and deprecate the
// whole DifferentialRevisionDetailRenderer class.
$custom_renderer = newv($custom_renderer_class, array());
$custom_renderer->setUser($user);
$custom_renderer->setDiff($target);
if ($diff_vs) {
$custom_renderer->setVSDiff($diffs[$diff_vs]);
}
$actions = array_merge(
$actions,
$custom_renderer->generateActionLinks($revision, $target_manual));
}
$whitespace = $request->getStr(
'whitespace',
DifferentialChangesetParser::WHITESPACE_IGNORE_ALL);
if ($arc_project) {
list($symbol_indexes, $project_phids) = $this->buildSymbolIndexes(
$arc_project,
$visible_changesets);
} else {
$symbol_indexes = array();
$project_phids = null;
}
$revision_detail->setActions($actions);
$revision_detail->setUser($user);
$comment_view = new DifferentialRevisionCommentListView();
$comment_view->setComments($comments);
$comment_view->setHandles($handles);
$comment_view->setInlineComments($inlines);
$comment_view->setChangesets($all_changesets);
$comment_view->setUser($user);
$comment_view->setTargetDiff($target);
$comment_view->setVersusDiffID($diff_vs);
if ($arc_project) {
Javelin::initBehavior(
'repository-crossreference',
array(
'section' => $comment_view->getID(),
'projects' => $project_phids,
));
}
$changeset_view = new DifferentialChangesetListView();
$changeset_view->setChangesets($changesets);
$changeset_view->setVisibleChangesets($visible_changesets);
if (!$viewer_is_anonymous) {
$changeset_view->setInlineCommentControllerURI(
'/differential/comment/inline/edit/'.$revision->getID().'/');
}
$changeset_view->setStandaloneURI('/differential/changeset/');
$changeset_view->setRawFileURIs(
'/differential/changeset/?view=old',
'/differential/changeset/?view=new');
$changeset_view->setUser($user);
$changeset_view->setDiff($target);
$changeset_view->setRenderingReferences($rendering_references);
$changeset_view->setVsMap($vs_map);
$changeset_view->setWhitespace($whitespace);
if ($repository) {
$changeset_view->setRepository($repository);
}
$changeset_view->setSymbolIndexes($symbol_indexes);
$changeset_view->setTitle('Diff '.$target->getID());
$diff_history = new DifferentialRevisionUpdateHistoryView();
$diff_history->setDiffs($diffs);
$diff_history->setSelectedVersusDiffID($diff_vs);
$diff_history->setSelectedDiffID($target->getID());
$diff_history->setSelectedWhitespace($whitespace);
$diff_history->setUser($user);
$local_view = new DifferentialLocalCommitsView();
$local_view->setUser($user);
$local_view->setLocalCommits(idx($props, 'local:commits'));
if ($repository) {
$other_revisions = $this->loadOtherRevisions(
$changesets,
$target,
$repository);
} else {
$other_revisions = array();
}
$other_view = null;
if ($other_revisions) {
$other_view = $this->renderOtherRevisions($other_revisions);
}
$toc_view = new DifferentialDiffTableOfContentsView();
$toc_view->setChangesets($changesets);
$toc_view->setVisibleChangesets($visible_changesets);
$toc_view->setRenderingReferences($rendering_references);
$toc_view->setUnitTestData(idx($props, 'arc:unit', array()));
if ($repository) {
$toc_view->setRepository($repository);
}
$toc_view->setDiff($target);
$toc_view->setUser($user);
$toc_view->setRevisionID($revision->getID());
$toc_view->setWhitespace($whitespace);
$comment_form = null;
if (!$viewer_is_anonymous) {
$draft = id(new PhabricatorDraft())->loadOneWhere(
'authorPHID = %s AND draftKey = %s',
$user->getPHID(),
'differential-comment-'.$revision->getID());
$reviewers = array();
$ccs = array();
if ($draft) {
$reviewers = idx($draft->getMetadata(), 'reviewers', array());
$ccs = idx($draft->getMetadata(), 'ccs', array());
if ($reviewers || $ccs) {
$handles = $this->loadViewerHandles(array_merge($reviewers, $ccs));
$reviewers = array_select_keys($handles, $reviewers);
$ccs = array_select_keys($handles, $ccs);
}
}
$comment_form = new DifferentialAddCommentView();
$comment_form->setRevision($revision);
$comment_form->setAuxFields($aux_fields);
$comment_form->setActions($this->getRevisionCommentActions($revision));
$comment_form->setActionURI('/differential/comment/save/');
$comment_form->setUser($user);
$comment_form->setDraft($draft);
$comment_form->setReviewers(mpull($reviewers, 'getFullName', 'getPHID'));
$comment_form->setCCs(mpull($ccs, 'getFullName', 'getPHID'));
}
$pane_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'differential-keyboard-navigation',
array(
'haunt' => $pane_id,
));
Javelin::initBehavior('differential-user-select');
$page_pane = id(new DifferentialPrimaryPaneView())
->setID($pane_id)
->appendChild(array(
$comment_view->render(),
$diff_history->render(),
$warning,
$local_view->render(),
$toc_view->render(),
$other_view,
$changeset_view->render(),
));
if ($comment_form) {
$page_pane->appendChild($comment_form->render());
+ } else {
+ // TODO: For now, just use this to get "Login to Comment".
+ $page_pane->appendChild(
+ id(new PhabricatorApplicationTransactionCommentView())
+ ->setUser($user)
+ ->setRequestURI($request->getRequestURI()));
}
+
$object_id = 'D'.$revision->getID();
$top_anchor = id(new PhabricatorAnchorView())
->setAnchorName('top')
->setNavigationMarker(true);
$content = array(
$reviewer_warning,
$top_anchor,
$revision_detail,
$page_pane,
);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb(
id(new PhabricatorCrumbView())
->setName($object_id)
->setHref('/'.$object_id));
$prefs = $user->loadPreferences();
$pref_filetree = PhabricatorUserPreferences::PREFERENCE_DIFF_FILETREE;
if ($prefs->getPreference($pref_filetree)) {
$collapsed = $prefs->getPreference(
PhabricatorUserPreferences::PREFERENCE_NAV_COLLAPSED,
false);
$nav = id(new DifferentialChangesetFileTreeSideNavBuilder())
->setAnchorName('top')
->setTitle('D'.$revision->getID())
->setBaseURI(new PhutilURI('/D'.$revision->getID()))
->setCollapsed((bool)$collapsed)
->build($changesets);
$nav->appendChild($content);
$nav->setCrumbs($crumbs);
$content = $nav;
} else {
array_unshift($content, $crumbs);
}
return $this->buildApplicationPage(
$content,
array(
'title' => $object_id.' '.$revision->getTitle(),
'pageObjects' => array($revision->getPHID()),
));
}
private function getImplicitComments(
DifferentialRevision $revision,
DifferentialDiff $diff) {
$author_phid = nonempty(
$diff->getAuthorPHID(),
$revision->getAuthorPHID());
$template = new DifferentialComment();
$template->setAuthorPHID($author_phid);
$template->setRevisionID($revision->getID());
$template->setDateCreated($revision->getDateCreated());
$comments = array();
if (strlen($revision->getSummary())) {
$summary_comment = clone $template;
$summary_comment->setContent($revision->getSummary());
$summary_comment->setAction(DifferentialAction::ACTION_SUMMARIZE);
$comments[] = $summary_comment;
}
if (strlen($revision->getTestPlan())) {
$testplan_comment = clone $template;
$testplan_comment->setContent($revision->getTestPlan());
$testplan_comment->setAction(DifferentialAction::ACTION_TESTPLAN);
$comments[] = $testplan_comment;
}
return $comments;
}
private function getRevisionActions(DifferentialRevision $revision) {
$user = $this->getRequest()->getUser();
$viewer_phid = $user->getPHID();
$viewer_is_owner = ($revision->getAuthorPHID() == $viewer_phid);
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$viewer_is_cc = in_array($viewer_phid, $revision->getCCPHIDs());
- $viewer_is_anonymous = !$this->getRequest()->getUser()->isLoggedIn();
+ $logged_in = $this->getRequest()->getUser()->isLoggedIn();
$status = $revision->getStatus();
$revision_id = $revision->getID();
$revision_phid = $revision->getPHID();
$links = array();
- if ($viewer_is_owner) {
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $user,
+ $revision,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $links[] = array(
+ 'icon' => 'edit',
+ 'href' => "/differential/revision/edit/{$revision_id}/",
+ 'name' => pht('Edit Revision'),
+ 'disabled' => !$can_edit,
+ 'sigil' => $can_edit ? null : 'workflow',
+ );
+
+ if (!$viewer_is_owner && !$viewer_is_reviewer) {
+ $action = $viewer_is_cc ? 'rem' : 'add';
$links[] = array(
- 'icon' => 'edit',
- 'href' => "/differential/revision/edit/{$revision_id}/",
- 'name' => pht('Edit Revision'),
+ 'icon' => $viewer_is_cc ? 'disable' : 'check',
+ 'href' => "/differential/subscribe/{$action}/{$revision_id}/",
+ 'name' => $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe'),
+ 'instant' => $logged_in,
+ 'disabled' => !$logged_in,
+ 'sigil' => $can_edit ? null : 'workflow',
+ );
+ } else {
+ $links[] = array(
+ 'icon' => 'enable',
+ 'name' => pht('Automatically Subscribed'),
+ 'disabled' => true,
);
}
- if (!$viewer_is_anonymous) {
-
- if (!$viewer_is_owner && !$viewer_is_reviewer) {
- $action = $viewer_is_cc ? 'rem' : 'add';
- $links[] = array(
- 'icon' => $viewer_is_cc ? 'disable' : 'check',
- 'href' => "/differential/subscribe/{$action}/{$revision_id}/",
- 'name' => $viewer_is_cc ? pht('Unsubscribe') : pht('Subscribe'),
- 'instant' => true,
- );
- } else {
- $links[] = array(
- 'icon' => 'enable',
- 'name' => pht('Automatically Subscribed'),
- 'disabled' => true,
- );
- }
+ require_celerity_resource('phabricator-object-selector-css');
+ require_celerity_resource('javelin-behavior-phabricator-object-selector');
- require_celerity_resource('phabricator-object-selector-css');
- require_celerity_resource('javelin-behavior-phabricator-object-selector');
+ $links[] = array(
+ 'icon' => 'link',
+ 'name' => pht('Edit Dependencies'),
+ 'href' => "/search/attach/{$revision_phid}/DREV/dependencies/",
+ 'sigil' => 'workflow',
+ 'disabled' => !$can_edit,
+ );
+ $maniphest = 'PhabricatorApplicationManiphest';
+ if (PhabricatorApplication::isClassInstalled($maniphest)) {
$links[] = array(
- 'icon' => 'link',
- 'name' => pht('Edit Dependencies'),
- 'href' => "/search/attach/{$revision_phid}/DREV/dependencies/",
+ 'icon' => 'attach',
+ 'name' => pht('Edit Maniphest Tasks'),
+ 'href' => "/search/attach/{$revision_phid}/TASK/",
'sigil' => 'workflow',
+ 'disabled' => !$can_edit,
);
-
- $maniphest = 'PhabricatorApplicationManiphest';
- if (PhabricatorApplication::isClassInstalled($maniphest)) {
- $links[] = array(
- 'icon' => 'attach',
- 'name' => pht('Edit Maniphest Tasks'),
- 'href' => "/search/attach/{$revision_phid}/TASK/",
- 'sigil' => 'workflow',
- );
- }
}
$request_uri = $this->getRequest()->getRequestURI();
$links[] = array(
'icon' => 'download',
'name' => pht('Download Raw Diff'),
'href' => $request_uri->alter('download', 'true')
);
return $links;
}
private function getRevisionCommentActions(DifferentialRevision $revision) {
$actions = array(
DifferentialAction::ACTION_COMMENT => true,
);
$viewer = $this->getRequest()->getUser();
$viewer_phid = $viewer->getPHID();
$viewer_is_owner = ($viewer_phid == $revision->getAuthorPHID());
$viewer_is_reviewer = in_array($viewer_phid, $revision->getReviewers());
$viewer_did_accept = ($viewer_phid === $revision->loadReviewedBy());
$status = $revision->getStatus();
$allow_self_accept = PhabricatorEnv::getEnvConfig(
'differential.allow-self-accept');
$always_allow_close = PhabricatorEnv::getEnvConfig(
'differential.always-allow-close');
$allow_reopen = PhabricatorEnv::getEnvConfig(
'differential.allow-reopen');
if ($viewer_is_owner) {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ACCEPT] = $allow_self_accept;
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_ABANDON] = true;
$actions[DifferentialAction::ACTION_REQUEST] = true;
$actions[DifferentialAction::ACTION_RETHINK] = true;
$actions[DifferentialAction::ACTION_CLOSE] = true;
break;
case ArcanistDifferentialRevisionStatus::CLOSED:
break;
case ArcanistDifferentialRevisionStatus::ABANDONED:
$actions[DifferentialAction::ACTION_RECLAIM] = true;
break;
}
} else {
switch ($status) {
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::NEEDS_REVISION:
$actions[DifferentialAction::ACTION_ACCEPT] = true;
$actions[DifferentialAction::ACTION_RESIGN] = $viewer_is_reviewer;
break;
case ArcanistDifferentialRevisionStatus::ACCEPTED:
$actions[DifferentialAction::ACTION_REJECT] = true;
$actions[DifferentialAction::ACTION_RESIGN] =
$viewer_is_reviewer && !$viewer_did_accept;
break;
case ArcanistDifferentialRevisionStatus::CLOSED:
case ArcanistDifferentialRevisionStatus::ABANDONED:
break;
}
if ($status != ArcanistDifferentialRevisionStatus::CLOSED) {
$actions[DifferentialAction::ACTION_CLAIM] = true;
$actions[DifferentialAction::ACTION_CLOSE] = $always_allow_close;
}
}
$actions[DifferentialAction::ACTION_ADDREVIEWERS] = true;
$actions[DifferentialAction::ACTION_ADDCCS] = true;
$actions[DifferentialAction::ACTION_REOPEN] = $allow_reopen &&
($status == ArcanistDifferentialRevisionStatus::CLOSED);
$actions = array_keys(array_filter($actions));
$actions_dict = array();
foreach ($actions as $action) {
$actions_dict[$action] = DifferentialAction::getActionVerb($action);
}
return $actions_dict;
}
private function loadInlineComments(
DifferentialRevision $revision,
array &$changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$inline_comments = array();
$inline_comments = id(new DifferentialInlineCommentQuery())
->withRevisionIDs(array($revision->getID()))
->withNotDraft(true)
->execute();
$load_changesets = array();
foreach ($inline_comments as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($changesets[$changeset_id])) {
continue;
}
$load_changesets[$changeset_id] = true;
}
$more_changesets = array();
if ($load_changesets) {
$changeset_ids = array_keys($load_changesets);
$more_changesets += id(new DifferentialChangeset())
->loadAllWhere(
'id IN (%Ld)',
$changeset_ids);
}
if ($more_changesets) {
$changesets += $more_changesets;
$changesets = msort($changesets, 'getSortKey');
}
return $inline_comments;
}
private function loadChangesetsAndVsMap(
DifferentialDiff $target,
DifferentialDiff $diff_vs = null,
PhabricatorRepository $repository = null) {
$load_ids = array();
if ($diff_vs) {
$load_ids[] = $diff_vs->getID();
}
$load_ids[] = $target->getID();
$raw_changesets = id(new DifferentialChangeset())
->loadAllWhere(
'diffID IN (%Ld)',
$load_ids);
$changeset_groups = mgroup($raw_changesets, 'getDiffID');
$changesets = idx($changeset_groups, $target->getID(), array());
$changesets = mpull($changesets, null, 'getID');
$refs = array();
$vs_map = array();
$vs_changesets = array();
if ($diff_vs) {
$vs_id = $diff_vs->getID();
$vs_changesets_path_map = array();
foreach (idx($changeset_groups, $vs_id, array()) as $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $diff_vs);
$vs_changesets_path_map[$path] = $changeset;
$vs_changesets[$changeset->getID()] = $changeset;
}
foreach ($changesets as $key => $changeset) {
$path = $changeset->getAbsoluteRepositoryPath($repository, $target);
if (isset($vs_changesets_path_map[$path])) {
$vs_map[$changeset->getID()] =
$vs_changesets_path_map[$path]->getID();
$refs[$changeset->getID()] =
$changeset->getID().'/'.$vs_changesets_path_map[$path]->getID();
unset($vs_changesets_path_map[$path]);
} else {
$refs[$changeset->getID()] = $changeset->getID();
}
}
foreach ($vs_changesets_path_map as $path => $changeset) {
$changesets[$changeset->getID()] = $changeset;
$vs_map[$changeset->getID()] = -1;
$refs[$changeset->getID()] = $changeset->getID().'/-1';
}
} else {
foreach ($changesets as $changeset) {
$refs[$changeset->getID()] = $changeset->getID();
}
}
$changesets = msort($changesets, 'getSortKey');
return array($changesets, $vs_map, $vs_changesets, $refs);
}
private function loadAuxiliaryFields(DifferentialRevision $revision) {
$aux_fields = DifferentialFieldSelector::newSelector()
->getFieldSpecifications();
foreach ($aux_fields as $key => $aux_field) {
if (!$aux_field->shouldAppearOnRevisionView()) {
unset($aux_fields[$key]);
} else {
$aux_field->setUser($this->getRequest()->getUser());
}
}
$aux_fields = DifferentialAuxiliaryField::loadFromStorage(
$revision,
$aux_fields);
return $aux_fields;
}
private function buildSymbolIndexes(
PhabricatorRepositoryArcanistProject $arc_project,
array $visible_changesets) {
assert_instances_of($visible_changesets, 'DifferentialChangeset');
$engine = PhabricatorSyntaxHighlighter::newEngine();
$langs = $arc_project->getSymbolIndexLanguages();
if (!$langs) {
return array(array(), array());
}
$symbol_indexes = array();
$project_phids = array_merge(
array($arc_project->getPHID()),
nonempty($arc_project->getSymbolIndexProjects(), array()));
$indexed_langs = array_fill_keys($langs, true);
foreach ($visible_changesets as $key => $changeset) {
$lang = $engine->getLanguageFromFilename($changeset->getFilename());
if (isset($indexed_langs[$lang])) {
$symbol_indexes[$key] = array(
'lang' => $lang,
'projects' => $project_phids,
);
}
}
return array($symbol_indexes, $project_phids);
}
private function loadOtherRevisions(
array $changesets,
DifferentialDiff $target,
PhabricatorRepository $repository) {
assert_instances_of($changesets, 'DifferentialChangeset');
$paths = array();
foreach ($changesets as $changeset) {
$paths[] = $changeset->getAbsoluteRepositoryPath(
$repository,
$target);
}
if (!$paths) {
return array();
}
$path_map = id(new DiffusionPathIDQuery($paths))->loadPathIDs();
if (!$path_map) {
return array();
}
$query = id(new DifferentialRevisionQuery())
->setViewer($this->getRequest()->getUser())
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->setOrder(DifferentialRevisionQuery::ORDER_PATH_MODIFIED)
->setLimit(10)
->needRelationships(true);
foreach ($path_map as $path => $path_id) {
$query->withPath($repository->getID(), $path_id);
}
$results = $query->execute();
// Strip out *this* revision.
foreach ($results as $key => $result) {
if ($result->getID() == $this->revisionID) {
unset($results[$key]);
}
}
return $results;
}
private function renderOtherRevisions(array $revisions) {
assert_instances_of($revisions, 'DifferentialRevision');
$user = $this->getRequest()->getUser();
$view = id(new DifferentialRevisionListView())
->setRevisions($revisions)
->setFields(DifferentialRevisionListView::getDefaultFields($user))
->setUser($user)
->loadAssets();
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
return array(
id(new PHUIHeaderView())
->setHeader(pht('Open Revisions Affecting These Files')),
$view,
);
}
/**
* Straight copy of the loadFileByPhid method in
* @{class:DifferentialReviewRequestMail}.
*
* This is because of the code similarity between the buildPatch method in
* @{class:DifferentialReviewRequestMail} and @{method:buildRawDiffResponse}
* in this class. Both of these methods end up using call_user_func and this
* piece of code is the lucky function.
*
* @return mixed (@{class:PhabricatorFile} if found, null if not)
*/
public function loadFileByPHID($phid) {
$file = id(new PhabricatorFile())->loadOneWhere(
'phid = %s',
$phid);
if (!$file) {
return null;
}
return $file->loadFileData();
}
/**
* Note this code is somewhat similar to the buildPatch method in
* @{class:DifferentialReviewRequestMail}.
*
* @return @{class:AphrontRedirectResponse}
*/
private function buildRawDiffResponse(
array $changesets,
array $vs_changesets,
array $vs_map,
PhabricatorRepository $repository = null) {
assert_instances_of($changesets, 'DifferentialChangeset');
assert_instances_of($vs_changesets, 'DifferentialChangeset');
$engine = new PhabricatorDifferenceEngine();
$generated_changesets = array();
foreach ($changesets as $changeset) {
$changeset->attachHunks($changeset->loadHunks());
$right = $changeset->makeNewFile();
$choice = $changeset;
$vs = idx($vs_map, $changeset->getID());
if ($vs == -1) {
$left = $right;
$right = $changeset->makeOldFile();
} else if ($vs) {
$choice = $vs_changeset = $vs_changesets[$vs];
$vs_changeset->attachHunks($vs_changeset->loadHunks());
$left = $vs_changeset->makeNewFile();
} else {
$left = $changeset->makeOldFile();
}
$synthetic = $engine->generateChangesetFromFileContent(
$left,
$right);
if (!$synthetic->getAffectedLineCount()) {
$filetype = $choice->getFileType();
if ($filetype == DifferentialChangeType::FILE_TEXT ||
$filetype == DifferentialChangeType::FILE_SYMLINK) {
continue;
}
}
$choice->attachHunks($synthetic->getHunks());
$generated_changesets[] = $choice;
}
$diff = new DifferentialDiff();
$diff->attachChangesets($generated_changesets);
$raw_changes = $diff->buildChangesList();
$changes = array();
foreach ($raw_changes as $changedict) {
$changes[] = ArcanistDiffChange::newFromDictionary($changedict);
}
$bundle = ArcanistBundle::newFromChanges($changes);
$bundle->setLoadFileDataCallback(array($this, 'loadFileByPHID'));
$vcs = $repository ? $repository->getVersionControlSystem() : null;
switch ($vcs) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
$raw_diff = $bundle->toGitPatch();
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
default:
$raw_diff = $bundle->toUnifiedDiff();
break;
}
$request_uri = $this->getRequest()->getRequestURI();
// this ends up being something like
// D123.diff
// or the verbose
// D123.vs123.id123.whitespaceignore-all.diff
// lame but nice to include these options
$file_name = ltrim($request_uri->getPath(), '/').'.';
foreach ($request_uri->getQueryParams() as $key => $value) {
if ($key == 'download') {
continue;
}
$file_name .= $key.$value.'.';
}
$file_name .= 'diff';
$file = PhabricatorFile::buildFromFileDataOrHash(
$raw_diff,
array(
'name' => $file_name,
));
return id(new AphrontRedirectResponse())->setURI($file->getBestURI());
}
}
diff --git a/src/applications/differential/query/DifferentialDiffQuery.php b/src/applications/differential/query/DifferentialDiffQuery.php
index d1344e4245..a608a5421e 100644
--- a/src/applications/differential/query/DifferentialDiffQuery.php
+++ b/src/applications/differential/query/DifferentialDiffQuery.php
@@ -1,114 +1,140 @@
<?php
final class DifferentialDiffQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $revisionIDs;
private $needChangesets = false;
private $needArcanistProjects = false;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withRevisionIDs(array $revision_ids) {
$this->revisionIDs = $revision_ids;
return $this;
}
public function needChangesets($bool) {
$this->needChangesets = $bool;
return $this;
}
public function needArcanistProjects($bool) {
$this->needArcanistProjects = $bool;
return $this;
}
public function loadPage() {
$table = new DifferentialDiff();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
}
public function willFilterPage(array $diffs) {
+ $revision_ids = array_filter(mpull($diffs, 'getRevisionID'));
+
+ $revisions = array();
+ if ($revision_ids) {
+ $revisions = id(new DifferentialRevisionQuery())
+ ->setViewer($this->getViewer())
+ ->withIDs($revision_ids)
+ ->execute();
+ }
+
+ foreach ($diffs as $key => $diff) {
+ if (!$diff->getRevisionID()) {
+ $diff->attachRevision(null);
+ continue;
+ }
+
+ $revision = idx($revisions, $diff->getRevisionID());
+ if ($revision) {
+ $diff->attachRevision($revision);
+ continue;
+ }
+
+ unset($diffs[$key]);
+ }
+
+
if ($this->needChangesets) {
$this->loadChangesets($diffs);
}
if ($this->needArcanistProjects) {
$this->loadArcanistProjects($diffs);
}
return $diffs;
}
private function loadChangesets(array $diffs) {
foreach ($diffs as $diff) {
$diff->attachChangesets(
$diff->loadRelatives(new DifferentialChangeset(), 'diffID'));
foreach ($diff->getChangesets() as $changeset) {
$changeset->attachHunks(
$changeset->loadRelatives(new DifferentialHunk(), 'changesetID'));
}
}
return $diffs;
}
private function loadArcanistProjects(array $diffs) {
$phids = array_filter(mpull($diffs, 'getArcanistProjectPHID'));
$projects = array();
$project_map = array();
if ($phids) {
$projects = id(new PhabricatorRepositoryArcanistProject())
->loadAllWhere(
'phid IN (%Ls)',
$phids);
$project_map = mpull($projects, null, 'getPHID');
}
foreach ($diffs as $diff) {
$project = null;
if ($diff->getArcanistProjectPHID()) {
$project = idx($project_map, $diff->getArcanistProjectPHID());
}
$diff->attachArcanistProject($project);
}
return $diffs;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->revisionIDs) {
$where[] = qsprintf(
$conn_r,
'revisionID IN (%Ld)',
$this->revisionIDs);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
}
}
diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php
index a7ba369dc9..32cded90fe 100644
--- a/src/applications/differential/storage/DifferentialDiff.php
+++ b/src/applications/differential/storage/DifferentialDiff.php
@@ -1,305 +1,323 @@
<?php
final class DifferentialDiff
extends DifferentialDAO
implements PhabricatorPolicyInterface {
protected $revisionID;
protected $authorPHID;
protected $sourceMachine;
protected $sourcePath;
protected $sourceControlSystem;
protected $sourceControlBaseRevision;
protected $sourceControlPath;
protected $lintStatus;
protected $unitStatus;
protected $lineCount;
protected $branch;
protected $bookmark;
protected $parentRevisionID;
protected $arcanistProjectPHID;
protected $creationMethod;
protected $repositoryUUID;
protected $description;
private $unsavedChangesets = array();
private $changesets = self::ATTACHABLE;
private $arcanistProject = self::ATTACHABLE;
+ private $revision = self::ATTACHABLE;
public function addUnsavedChangeset(DifferentialChangeset $changeset) {
if ($this->changesets === null) {
$this->changesets = array();
}
$this->unsavedChangesets[] = $changeset;
$this->changesets[] = $changeset;
return $this;
}
public function attachChangesets(array $changesets) {
assert_instances_of($changesets, 'DifferentialChangeset');
$this->changesets = $changesets;
return $this;
}
public function getChangesets() {
return $this->assertAttached($this->changesets);
}
public function loadChangesets() {
if (!$this->getID()) {
return array();
}
return id(new DifferentialChangeset())->loadAllWhere(
'diffID = %d',
$this->getID());
}
public function attachArcanistProject(
PhabricatorRepositoryArcanistProject $project = null) {
$this->arcanistProject = $project;
return $this;
}
public function getArcanistProject() {
return $this->assertAttached($this->arcanistProject);
}
public function getArcanistProjectName() {
$name = '';
if ($this->arcanistProject) {
$project = $this->getArcanistProject();
$name = $project->getName();
}
return $name;
}
public function loadArcanistProject() {
if (!$this->getArcanistProjectPHID()) {
return null;
}
return id(new PhabricatorRepositoryArcanistProject())->loadOneWhere(
'phid = %s',
$this->getArcanistProjectPHID());
}
public function getBackingVersionControlSystem() {
$arcanist_project = $this->loadArcanistProject();
if (!$arcanist_project) {
return null;
}
$repository = $arcanist_project->loadRepository();
if (!$repository) {
return null;
}
return $repository->getVersionControlSystem();
}
public function save() {
$this->openTransaction();
$ret = parent::save();
foreach ($this->unsavedChangesets as $changeset) {
$changeset->setDiffID($this->getID());
$changeset->save();
}
$this->saveTransaction();
return $ret;
}
public function delete() {
$this->openTransaction();
foreach ($this->loadChangesets() as $changeset) {
$changeset->delete();
}
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$this->getID());
foreach ($properties as $prop) {
$prop->delete();
}
$ret = parent::delete();
$this->saveTransaction();
return $ret;
}
public static function newFromRawChanges(array $changes) {
assert_instances_of($changes, 'ArcanistDiffChange');
$diff = new DifferentialDiff();
// There may not be any changes; initialize the changesets list so that
// we don't throw later when accessing it.
$diff->attachChangesets(array());
$lines = 0;
foreach ($changes as $change) {
if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) {
// If a user pastes a diff into Differential which includes a commit
// message (e.g., they ran `git show` to generate it), discard that
// change when constructing a DifferentialDiff.
continue;
}
$changeset = new DifferentialChangeset();
$add_lines = 0;
$del_lines = 0;
$first_line = PHP_INT_MAX;
$hunks = $change->getHunks();
if ($hunks) {
foreach ($hunks as $hunk) {
$dhunk = new DifferentialHunk();
$dhunk->setOldOffset($hunk->getOldOffset());
$dhunk->setOldLen($hunk->getOldLength());
$dhunk->setNewOffset($hunk->getNewOffset());
$dhunk->setNewLen($hunk->getNewLength());
$dhunk->setChanges($hunk->getCorpus());
$changeset->addUnsavedHunk($dhunk);
$add_lines += $hunk->getAddLines();
$del_lines += $hunk->getDelLines();
$added_lines = $hunk->getChangedLines('new');
if ($added_lines) {
$first_line = min($first_line, head_key($added_lines));
}
}
$lines += $add_lines + $del_lines;
} else {
// This happens when you add empty files.
$changeset->attachHunks(array());
}
$metadata = $change->getAllMetadata();
if ($first_line != PHP_INT_MAX) {
$metadata['line:first'] = $first_line;
}
$changeset->setOldFile($change->getOldPath());
$changeset->setFilename($change->getCurrentPath());
$changeset->setChangeType($change->getType());
$changeset->setFileType($change->getFileType());
$changeset->setMetadata($metadata);
$changeset->setOldProperties($change->getOldProperties());
$changeset->setNewProperties($change->getNewProperties());
$changeset->setAwayPaths($change->getAwayPaths());
$changeset->setAddLines($add_lines);
$changeset->setDelLines($del_lines);
$diff->addUnsavedChangeset($changeset);
}
$diff->setLineCount($lines);
$parser = new DifferentialChangesetParser();
$changesets = $parser->detectCopiedCode(
$diff->getChangesets(),
$min_width = 30,
$min_lines = 3);
$diff->attachChangesets($changesets);
return $diff;
}
public function getDiffDict() {
$dict = array(
'id' => $this->getID(),
'parent' => $this->getParentRevisionID(),
'revisionID' => $this->getRevisionID(),
'dateCreated' => $this->getDateCreated(),
'dateModified' => $this->getDateModified(),
'sourceControlBaseRevision' => $this->getSourceControlBaseRevision(),
'sourceControlPath' => $this->getSourceControlPath(),
'sourceControlSystem' => $this->getSourceControlSystem(),
'branch' => $this->getBranch(),
'bookmark' => $this->getBookmark(),
'creationMethod' => $this->getCreationMethod(),
'description' => $this->getDescription(),
'unitStatus' => $this->getUnitStatus(),
'lintStatus' => $this->getLintStatus(),
'changes' => array(),
'properties' => array(),
'projectName' => $this->getArcanistProjectName()
);
$dict['changes'] = $this->buildChangesList();
$properties = id(new DifferentialDiffProperty())->loadAllWhere(
'diffID = %d',
$this->getID());
foreach ($properties as $property) {
$dict['properties'][$property->getName()] = $property->getData();
if ($property->getName() == 'local:commits') {
foreach ($property->getData() as $commit) {
$dict['authorName'] = $commit['author'];
$dict['authorEmail'] = idx($commit, 'authorEmail');
break;
}
}
}
return $dict;
}
public function buildChangesList() {
$changes = array();
foreach ($this->getChangesets() as $changeset) {
$hunks = array();
foreach ($changeset->getHunks() as $hunk) {
$hunks[] = array(
'oldOffset' => $hunk->getOldOffset(),
'newOffset' => $hunk->getNewOffset(),
'oldLength' => $hunk->getOldLen(),
'newLength' => $hunk->getNewLen(),
'addLines' => null,
'delLines' => null,
'isMissingOldNewline' => null,
'isMissingNewNewline' => null,
'corpus' => $hunk->getChanges(),
);
}
$change = array(
'id' => $changeset->getID(),
'metadata' => $changeset->getMetadata(),
'oldPath' => $changeset->getOldFile(),
'currentPath' => $changeset->getFilename(),
'awayPaths' => $changeset->getAwayPaths(),
'oldProperties' => $changeset->getOldProperties(),
'newProperties' => $changeset->getNewProperties(),
'type' => $changeset->getChangeType(),
'fileType' => $changeset->getFileType(),
'commitHash' => null,
'addLines' => $changeset->getAddLines(),
'delLines' => $changeset->getDelLines(),
'hunks' => $hunks,
);
$changes[] = $change;
}
return $changes;
}
+ public function getRevision() {
+ return $this->assertAttached($this->revision);
+ }
+
+ public function attachRevision(DifferentialRevision $revision = null) {
+ $this->revision = $revision;
+ return $this;
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
+ if ($this->getRevision()) {
+ return $this->getRevision()->getPolicy($capability);
+ }
+
return PhabricatorPolicies::POLICY_USER;
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ if ($this->getRevision()) {
+ return $this->getRevision()->hasAutomaticCapability($capability, $viewer);
+ }
+
return false;
}
}
diff --git a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php
index f745b6da3c..55321a9155 100644
--- a/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php
+++ b/src/applications/help/controller/PhabricatorHelpKeyboardShortcutController.php
@@ -1,66 +1,70 @@
<?php
final class PhabricatorHelpKeyboardShortcutController
extends PhabricatorHelpController {
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$keys = $request->getStr('keys');
$keys = json_decode($keys, true);
if (!is_array($keys)) {
return new Aphront400Response();
}
// There have been at least two users asking for a keyboard shortcut to
// close the dialog, so be explicit that escape works since it isn't
// terribly discoverable.
$keys[] = array(
'keys' => array('esc'),
'description' => pht('Close any dialog, including this one.'),
);
$stroke_map = array(
'left' => "\xE2\x86\x90",
'right' => "\xE2\x86\x92",
'up' => "\xE2\x86\x91",
'down' => "\xE2\x86\x93",
'return' => "\xE2\x8F\x8E",
'tab' => "\xE2\x87\xA5",
'delete' => "\xE2\x8C\xAB",
);
$rows = array();
foreach ($keys as $shortcut) {
$keystrokes = array();
foreach ($shortcut['keys'] as $stroke) {
$stroke = idx($stroke_map, $stroke, $stroke);
$keystrokes[] = phutil_tag('kbd', array(), $stroke);
}
$keystrokes = phutil_implode_html(' or ', $keystrokes);
$rows[] = phutil_tag(
'tr',
array(),
array(
phutil_tag('th', array(), $keystrokes),
phutil_tag('td', array(), $shortcut['description']),
));
}
$table = phutil_tag(
'table',
array('class' => 'keyboard-shortcut-help'),
$rows);
$dialog = id(new AphrontDialogView())
->setUser($user)
->setTitle(pht('Keyboard Shortcuts'))
->appendChild($table)
->addCancelButton('#', pht('Close'));
return id(new AphrontDialogResponse())
->setDialog($dialog);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Aug 14, 7:12 PM (2 d, 4 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
202314
Default Alt Text
(64 KB)

Event Timeline