Page MenuHomec4science

DifferentialInlineCommentQuery.php
No OneTemporary

File Metadata

Created
Tue, Jul 9, 13:02

DifferentialInlineCommentQuery.php

<?php
/**
* Temporary wrapper for transitioning Differential to ApplicationTransactions.
*/
final class DifferentialInlineCommentQuery
extends PhabricatorOffsetPagedQuery {
// TODO: Remove this when this query eventually moves to PolicyAware.
private $viewer;
private $ids;
private $phids;
private $drafts;
private $authorPHIDs;
private $revisionPHIDs;
private $deletedDrafts;
private $needHidden;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withDrafts($drafts) {
$this->drafts = $drafts;
return $this;
}
public function withAuthorPHIDs(array $author_phids) {
$this->authorPHIDs = $author_phids;
return $this;
}
public function withRevisionPHIDs(array $revision_phids) {
$this->revisionPHIDs = $revision_phids;
return $this;
}
public function withDeletedDrafts($deleted_drafts) {
$this->deletedDrafts = $deleted_drafts;
return $this;
}
public function needHidden($need) {
$this->needHidden = $need;
return $this;
}
public function execute() {
$table = new DifferentialTransactionComment();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildLimitClause($conn_r));
$comments = $table->loadAllFromArray($data);
if ($this->needHidden) {
$viewer_phid = $this->getViewer()->getPHID();
if ($viewer_phid && $comments) {
$hidden = queryfx_all(
$conn_r,
'SELECT commentID FROM %T WHERE userPHID = %s
AND commentID IN (%Ls)',
id(new DifferentialHiddenComment())->getTableName(),
$viewer_phid,
mpull($comments, 'getID'));
$hidden = array_fuse(ipull($hidden, 'commentID'));
} else {
$hidden = array();
}
foreach ($comments as $inline) {
$inline->attachIsHidden(isset($hidden[$inline->getID()]));
}
}
foreach ($comments as $key => $value) {
$comments[$key] = DifferentialInlineComment::newFromModernComment(
$value);
}
return $comments;
}
public function executeOne() {
// TODO: Remove when this query moves to PolicyAware.
return head($this->execute());
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
// Only find inline comments.
$where[] = qsprintf(
$conn_r,
'changesetID IS NOT NULL');
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->revisionPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'revisionPHID IN (%Ls)',
$this->revisionPHIDs);
}
if ($this->drafts === null) {
if ($this->deletedDrafts) {
$where[] = qsprintf(
$conn_r,
'(authorPHID = %s) OR (transactionPHID IS NOT NULL)',
$this->getViewer()->getPHID());
} else {
$where[] = qsprintf(
$conn_r,
'(authorPHID = %s AND isDeleted = 0)
OR (transactionPHID IS NOT NULL)',
$this->getViewer()->getPHID());
}
} else if ($this->drafts) {
$where[] = qsprintf(
$conn_r,
'(authorPHID = %s AND isDeleted = 0) AND (transactionPHID IS NULL)',
$this->getViewer()->getPHID());
} else {
$where[] = qsprintf(
$conn_r,
'transactionPHID IS NOT NULL');
}
return $this->formatWhereClause($where);
}
public function adjustInlinesForChangesets(
array $inlines,
array $old,
array $new,
DifferentialRevision $revision) {
assert_instances_of($inlines, 'DifferentialInlineComment');
assert_instances_of($old, 'DifferentialChangeset');
assert_instances_of($new, 'DifferentialChangeset');
$viewer = $this->getViewer();
$no_ghosts = $viewer->compareUserSetting(
PhabricatorOlderInlinesSetting::SETTINGKEY,
PhabricatorOlderInlinesSetting::VALUE_GHOST_INLINES_DISABLED);
if ($no_ghosts) {
return $inlines;
}
$all = array_merge($old, $new);
$changeset_ids = mpull($inlines, 'getChangesetID');
$changeset_ids = array_unique($changeset_ids);
$all_map = mpull($all, null, 'getID');
// We already have at least some changesets, and we might not need to do
// any more data fetching. Remove everything we already have so we can
// tell if we need new stuff.
foreach ($changeset_ids as $key => $id) {
if (isset($all_map[$id])) {
unset($changeset_ids[$key]);
}
}
if ($changeset_ids) {
$changesets = id(new DifferentialChangesetQuery())
->setViewer($viewer)
->withIDs($changeset_ids)
->execute();
$changesets = mpull($changesets, null, 'getID');
} else {
$changesets = array();
}
$changesets += $all_map;
$id_map = array();
foreach ($all as $changeset) {
$id_map[$changeset->getID()] = $changeset->getID();
}
// Generate filename maps for older and newer comments. If we're bringing
// an older comment forward in a diff-of-diffs, we want to put it on the
// left side of the screen, not the right side. Both sides are "new" files
// with the same name, so they're both appropriate targets, but the left
// is a better target conceptually for users because it's more consistent
// with the rest of the UI, which shows old information on the left and
// new information on the right.
$move_here = DifferentialChangeType::TYPE_MOVE_HERE;
$name_map_old = array();
$name_map_new = array();
$move_map = array();
foreach ($all as $changeset) {
$changeset_id = $changeset->getID();
$filenames = array();
$filenames[] = $changeset->getFilename();
// If this is the target of a move, also map comments on the old filename
// to this changeset.
if ($changeset->getChangeType() == $move_here) {
$old_file = $changeset->getOldFile();
$filenames[] = $old_file;
$move_map[$changeset_id][$old_file] = true;
}
foreach ($filenames as $filename) {
// We update the old map only if we don't already have an entry (oldest
// changeset persists).
if (empty($name_map_old[$filename])) {
$name_map_old[$filename] = $changeset_id;
}
// We always update the new map (newest changeset overwrites).
$name_map_new[$changeset->getFilename()] = $changeset_id;
}
}
// Find the smallest "new" changeset ID. We'll consider everything
// larger than this to be "newer", and everything smaller to be "older".
$first_new_id = min(mpull($new, 'getID'));
$results = array();
foreach ($inlines as $inline) {
$changeset_id = $inline->getChangesetID();
if (isset($id_map[$changeset_id])) {
// This inline is legitimately on one of the current changesets, so
// we can include it in the result set unmodified.
$results[] = $inline;
continue;
}
$changeset = idx($changesets, $changeset_id);
if (!$changeset) {
// Just discard this inline, as it has bogus data.
continue;
}
$target_id = null;
if ($changeset_id >= $first_new_id) {
$name_map = $name_map_new;
$is_new = true;
} else {
$name_map = $name_map_old;
$is_new = false;
}
$filename = $changeset->getFilename();
if (isset($name_map[$filename])) {
// This changeset is on a file with the same name as the current
// changeset, so we're going to port it forward or backward.
$target_id = $name_map[$filename];
$is_move = isset($move_map[$target_id][$filename]);
if ($is_new) {
if ($is_move) {
$reason = pht(
'This comment was made on a file with the same name as the '.
'file this file was moved from, but in a newer diff.');
} else {
$reason = pht(
'This comment was made on a file with the same name, but '.
'in a newer diff.');
}
} else {
if ($is_move) {
$reason = pht(
'This comment was made on a file with the same name as the '.
'file this file was moved from, but in an older diff.');
} else {
$reason = pht(
'This comment was made on a file with the same name, but '.
'in an older diff.');
}
}
}
// If we didn't find a target and this change is the target of a move,
// look for a match against the old filename.
if (!$target_id) {
if ($changeset->getChangeType() == $move_here) {
$filename = $changeset->getOldFile();
if (isset($name_map[$filename])) {
$target_id = $name_map[$filename];
if ($is_new) {
$reason = pht(
'This comment was made on a file which this file was moved '.
'to, but in a newer diff.');
} else {
$reason = pht(
'This comment was made on a file which this file was moved '.
'to, but in an older diff.');
}
}
}
}
// If we found a changeset to port this comment to, bring it forward
// or backward and mark it.
if ($target_id) {
$diff_id = $changeset->getDiffID();
$inline_id = $inline->getID();
$revision_id = $revision->getID();
$href = "/D{$revision_id}?id={$diff_id}#inline-{$inline_id}";
$inline
->makeEphemeral(true)
->setChangesetID($target_id)
->setIsGhost(
array(
'new' => $is_new,
'reason' => $reason,
'href' => $href,
'originalID' => $changeset->getID(),
));
$results[] = $inline;
}
}
// Filter out the inlines we ported forward which won't be visible because
// they appear on the wrong side of a file.
$keep_map = array();
foreach ($old as $changeset) {
$keep_map[$changeset->getID()][0] = true;
}
foreach ($new as $changeset) {
$keep_map[$changeset->getID()][1] = true;
}
foreach ($results as $key => $inline) {
$is_new = (int)$inline->getIsNewFile();
$changeset_id = $inline->getChangesetID();
if (!isset($keep_map[$changeset_id][$is_new])) {
unset($results[$key]);
continue;
}
}
// Adjust inline line numbers to account for content changes across
// updates and rebases.
$plan = array();
$need = array();
foreach ($results as $inline) {
$ghost = $inline->getIsGhost();
if (!$ghost) {
// If this isn't a "ghost" inline, ignore it.
continue;
}
$src_id = $ghost['originalID'];
$dst_id = $inline->getChangesetID();
$xforms = array();
// If the comment is on the right, transform it through the inverse map
// back to the left.
if ($inline->getIsNewFile()) {
$xforms[] = array($src_id, $src_id, true);
}
// Transform it across rebases.
$xforms[] = array($src_id, $dst_id, false);
// If the comment is on the right, transform it back onto the right.
if ($inline->getIsNewFile()) {
$xforms[] = array($dst_id, $dst_id, false);
}
$key = array();
foreach ($xforms as $xform) {
list($u, $v, $inverse) = $xform;
$short = $u.'/'.$v;
$need[$short] = array($u, $v);
$part = $u.($inverse ? '<' : '>').$v;
$key[] = $part;
}
$key = implode(',', $key);
if (empty($plan[$key])) {
$plan[$key] = array(
'xforms' => $xforms,
'inlines' => array(),
);
}
$plan[$key]['inlines'][] = $inline;
}
if ($need) {
$maps = DifferentialLineAdjustmentMap::loadMaps($need);
} else {
$maps = array();
}
foreach ($plan as $step) {
$xforms = $step['xforms'];
$chain = null;
foreach ($xforms as $xform) {
list($u, $v, $inverse) = $xform;
$map = idx(idx($maps, $u, array()), $v);
if (!$map) {
continue 2;
}
if ($inverse) {
$map = DifferentialLineAdjustmentMap::newInverseMap($map);
} else {
$map = clone $map;
}
if ($chain) {
$chain->addMapToChain($map);
} else {
$chain = $map;
}
}
foreach ($step['inlines'] as $inline) {
$head_line = $inline->getLineNumber();
$tail_line = ($head_line + $inline->getLineLength());
$head_info = $chain->mapLine($head_line, false);
$tail_info = $chain->mapLine($tail_line, true);
list($head_deleted, $head_offset, $head_line) = $head_info;
list($tail_deleted, $tail_offset, $tail_line) = $tail_info;
if ($head_offset !== false) {
$inline->setLineNumber($head_line + 1 + $head_offset);
} else {
$inline->setLineNumber($head_line);
$inline->setLineLength($tail_line - $head_line);
}
}
}
return $results;
}
}

Event Timeline