mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-19 10:18:38 +01:00
Summary: Ref T4103. This doesn't get everything, but takes care of most of the easy stuff. The tricky-ish bit here is that I need to move timezones, pronouns and translations to proper settings. I expect to pursue that next. Test Plan: - Grepped for `loadPreferences` to identify callsites. - Changed start-of-week setting, loaded Calendar, saw correct start. - Visited welcome page, read "Adjust Settings" point. - Loaded Conpherence -- I changed behavior here slightly (switching threads drops the title glyph) but it wasn't consistent to start with and this seems like a good thing to push to the next version of Conpherence. - Enabled Filetree, toggled in Differential. - Disabled Filetree, no longer visible in Differential. - Changed "Unified Diffs" preference to "Small Screens" vs "Always". - Toggled filetree in Diffusion. - Edited a task, saw sensible projects in policy dropdown. - Viewed user profile, uncollapsed/collapsed side nav, reloaded page, sticky'd. - Toggled "monospaced textareas", used a comment box, got appropriate fonts. - Toggled durable column. - Disabled title glyphs. - Changed monospaced font to 18px/36px impact. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4103 Differential Revision: https://secure.phabricator.com/D16004
483 lines
14 KiB
PHP
483 lines
14 KiB
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;
|
|
}
|
|
|
|
}
|