Something went wrong. Try again.
@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
Something went wrong. Try again.
10 kB · 382 lines
PHP
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383<?php
abstract class PhabricatorDiffInlineCommentQuery extends PhabricatorApplicationTransactionCommentQuery {
const INLINE_CONTEXT_CACHE_VERSION = 1;
private $fixedStates; private $needReplyToComments; private $publishedComments; private $publishableComments; private $needHidden; private $needAppliedDrafts; private $needInlineContext;
abstract protected function buildInlineCommentWhereClauseParts( AphrontDatabaseConnection $conn); abstract public function withObjectPHIDs(array $phids); abstract protected function loadHiddenCommentIDs( $viewer_phid, array $comments);
abstract protected function newInlineContextMap(array $inlines); abstract protected function newInlineContextFromCacheData(array $map);
final public function withFixedStates(array $states) { $this->fixedStates = $states; return $this; }
final public function needReplyToComments($need_reply_to) { $this->needReplyToComments = $need_reply_to; return $this; }
final public function withPublishableComments($with_publishable) { $this->publishableComments = $with_publishable; return $this; }
final public function withPublishedComments($with_published) { $this->publishedComments = $with_published; return $this; }
final public function needHidden($need_hidden) { $this->needHidden = $need_hidden; return $this; }
final public function needInlineContext($need_context) { $this->needInlineContext = $need_context; return $this; }
final public function needAppliedDrafts($need_applied) { $this->needAppliedDrafts = $need_applied; return $this; }
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { $where = parent::buildWhereClauseParts($conn); $alias = $this->getPrimaryTableAlias();
foreach ($this->buildInlineCommentWhereClauseParts($conn) as $part) { $where[] = $part; }
if ($this->fixedStates !== null) { $where[] = qsprintf( $conn, '%T.fixedState IN (%Ls)', $alias, $this->fixedStates); }
$show_published = false; $show_publishable = false;
if ($this->publishableComments !== null) { if (!$this->publishableComments) { throw new Exception( pht( 'Querying for comments that are "not publishable" is '. 'not supported.')); } $show_publishable = true; }
if ($this->publishedComments !== null) { if (!$this->publishedComments) { throw new Exception( pht( 'Querying for comments that are "not published" is '. 'not supported.')); } $show_published = true; }
if ($show_publishable || $show_published) { $clauses = array();
if ($show_published) { $clauses[] = qsprintf( $conn, '%T.transactionPHID IS NOT NULL', $alias); }
if ($show_publishable) { $viewer = $this->getViewer(); $viewer_phid = $viewer->getPHID();
// If the viewer has a PHID, unpublished comments they authored and // have not deleted are visible. if ($viewer_phid) { $clauses[] = qsprintf( $conn, '%T.authorPHID = %s AND %T.isDeleted = 0 AND %T.transactionPHID IS NULL ', $alias, $viewer_phid, $alias, $alias); } }
// We can end up with a known-empty query if we (for example) query for // publishable comments and the viewer is logged-out. if (!$clauses) { throw new PhabricatorEmptyQueryException(); }
$where[] = qsprintf( $conn, '%LO', $clauses); }
return $where; }
protected function willFilterPage(array $inlines) { $viewer = $this->getViewer();
if ($this->needReplyToComments) { $reply_phids = array(); foreach ($inlines as $inline) { $reply_phid = $inline->getReplyToCommentPHID(); if ($reply_phid) { $reply_phids[] = $reply_phid; } }
if ($reply_phids) { $reply_inlines = newv(get_class($this), array()) ->setViewer($this->getViewer()) ->setParentQuery($this) ->withPHIDs($reply_phids) ->execute(); $reply_inlines = mpull($reply_inlines, null, 'getPHID'); } else { $reply_inlines = array(); }
foreach ($inlines as $key => $inline) { $reply_phid = $inline->getReplyToCommentPHID(); if (!$reply_phid) { $inline->attachReplyToComment(null); continue; } $reply = idx($reply_inlines, $reply_phid); if (!$reply) { $this->didRejectResult($inline); unset($inlines[$key]); continue; } $inline->attachReplyToComment($reply); } }
if (!$inlines) { return $inlines; }
$need_drafts = $this->needAppliedDrafts; $drop_void = $this->publishableComments; $convert_objects = ($need_drafts || $drop_void);
if ($convert_objects) { $inlines = mpull($inlines, 'newInlineCommentObject');
PhabricatorInlineComment::loadAndAttachVersionedDrafts( $viewer, $inlines);
if ($need_drafts) { // Don't count void inlines when considering draft state. foreach ($inlines as $key => $inline) { if ($inline->isVoidComment($viewer)) { $this->didRejectResult($inline->getStorageObject()); unset($inlines[$key]); continue; }
// For other inlines: if they have a nonempty draft state, set their // content to the draft state content. We want to submit the comment // as it is currently shown to the user, not as it was stored the last // time they clicked "Save".
$draft_state = $inline->getContentStateForEdit($viewer); if (!$draft_state->isEmptyContentState()) { $inline->setContentState($draft_state); } } }
// If we're loading publishable comments, discard any comments that are // empty. if ($drop_void) { foreach ($inlines as $key => $inline) { if ($inline->getTransactionPHID()) { continue; }
if ($inline->isVoidComment($viewer)) { $this->didRejectResult($inline->getStorageObject()); unset($inlines[$key]); continue; } } }
$inlines = mpull($inlines, 'getStorageObject'); }
return $inlines; }
protected function didFilterPage(array $inlines) { $viewer = $this->getViewer();
if ($this->needHidden) { $viewer_phid = $viewer->getPHID();
if ($viewer_phid) { $hidden = $this->loadHiddenCommentIDs( $viewer_phid, $inlines); } else { $hidden = array(); }
foreach ($inlines as $inline) { $inline->attachIsHidden(isset($hidden[$inline->getID()])); } }
if ($this->needInlineContext) { $need_context = array(); foreach ($inlines as $inline) { $object = $inline->newInlineCommentObject();
if ($object->getDocumentEngineKey() !== null) { $inline->attachInlineContext(null); continue; }
$need_context[] = $inline; }
if ($need_context) { $this->loadInlineCommentContext($need_context); } }
return $inlines; }
private function loadInlineCommentContext(array $inlines) { $cache_keys = array(); foreach ($inlines as $key => $inline) { $object = $inline->newInlineCommentObject(); $fragment = $object->getInlineCommentCacheFragment();
if ($fragment === null) { continue; }
$cache_keys[$key] = sprintf( '%s.context(v%d)', $fragment, self::INLINE_CONTEXT_CACHE_VERSION); }
$cache = PhabricatorCaches::getMutableStructureCache();
$cache_map = $cache->getKeys($cache_keys);
$context_map = array(); $need_construct = array();
foreach ($inlines as $key => $inline) { $cache_key = idx($cache_keys, $key);
if ($cache_key !== null) { if (array_key_exists($cache_key, $cache_map)) { $cache_data = $cache_map[$cache_key]; $context_map[$key] = $this->newInlineContextFromCacheData( $cache_data); continue; } }
$need_construct[$key] = $inline; }
if ($need_construct) { $construct_map = $this->newInlineContextMap($need_construct);
$write_map = array(); foreach ($construct_map as $key => $context) { if ($context === null) { $cache_data = $context; } else { $cache_data = $this->newCacheDataFromInlineContext($context); }
$cache_key = idx($cache_keys, $key); if ($cache_key !== null) { $write_map[$cache_key] = $cache_data; } }
if ($write_map) { $cache->setKeys($write_map); }
$context_map += $construct_map; }
foreach ($inlines as $key => $inline) { $inline->attachInlineContext(idx($context_map, $key)); } }
protected function newCacheDataFromInlineContext( PhabricatorInlineCommentContext $context) { return $context->newCacheDataMap(); }
final protected function simplifyContext(array $lines, $is_head) { // We want to provide the smallest amount of context we can while still // being useful, since the actual code is visible nearby and showing a // ton of context is silly.
// Examine each line until we find one that looks "useful" (not just // whitespace or a single bracket). Once we find a useful piece of context // to anchor the text, discard the rest of the lines beyond it.
if ($is_head) { $lines = array_reverse($lines, true); }
$saw_context = false; foreach ($lines as $key => $line) { if ($saw_context) { unset($lines[$key]); continue; }
$saw_context = (strlen(trim($line)) > 3); }
if ($is_head) { $lines = array_reverse($lines, true); }
return $lines; }}