Path: blob/master/src/infrastructure/diff/query/PhabricatorDiffInlineCommentQuery.php
12242 views
<?php12abstract class PhabricatorDiffInlineCommentQuery3extends PhabricatorApplicationTransactionCommentQuery {45const INLINE_CONTEXT_CACHE_VERSION = 1;67private $fixedStates;8private $needReplyToComments;9private $publishedComments;10private $publishableComments;11private $needHidden;12private $needAppliedDrafts;13private $needInlineContext;1415abstract protected function buildInlineCommentWhereClauseParts(16AphrontDatabaseConnection $conn);17abstract public function withObjectPHIDs(array $phids);18abstract protected function loadHiddenCommentIDs(19$viewer_phid,20array $comments);2122abstract protected function newInlineContextMap(array $inlines);23abstract protected function newInlineContextFromCacheData(array $map);2425final public function withFixedStates(array $states) {26$this->fixedStates = $states;27return $this;28}2930final public function needReplyToComments($need_reply_to) {31$this->needReplyToComments = $need_reply_to;32return $this;33}3435final public function withPublishableComments($with_publishable) {36$this->publishableComments = $with_publishable;37return $this;38}3940final public function withPublishedComments($with_published) {41$this->publishedComments = $with_published;42return $this;43}4445final public function needHidden($need_hidden) {46$this->needHidden = $need_hidden;47return $this;48}4950final public function needInlineContext($need_context) {51$this->needInlineContext = $need_context;52return $this;53}5455final public function needAppliedDrafts($need_applied) {56$this->needAppliedDrafts = $need_applied;57return $this;58}5960protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {61$where = parent::buildWhereClauseParts($conn);62$alias = $this->getPrimaryTableAlias();6364foreach ($this->buildInlineCommentWhereClauseParts($conn) as $part) {65$where[] = $part;66}6768if ($this->fixedStates !== null) {69$where[] = qsprintf(70$conn,71'%T.fixedState IN (%Ls)',72$alias,73$this->fixedStates);74}7576$show_published = false;77$show_publishable = false;7879if ($this->publishableComments !== null) {80if (!$this->publishableComments) {81throw new Exception(82pht(83'Querying for comments that are "not publishable" is '.84'not supported.'));85}86$show_publishable = true;87}8889if ($this->publishedComments !== null) {90if (!$this->publishedComments) {91throw new Exception(92pht(93'Querying for comments that are "not published" is '.94'not supported.'));95}96$show_published = true;97}9899if ($show_publishable || $show_published) {100$clauses = array();101102if ($show_published) {103$clauses[] = qsprintf(104$conn,105'%T.transactionPHID IS NOT NULL',106$alias);107}108109if ($show_publishable) {110$viewer = $this->getViewer();111$viewer_phid = $viewer->getPHID();112113// If the viewer has a PHID, unpublished comments they authored and114// have not deleted are visible.115if ($viewer_phid) {116$clauses[] = qsprintf(117$conn,118'%T.authorPHID = %s119AND %T.isDeleted = 0120AND %T.transactionPHID IS NULL ',121$alias,122$viewer_phid,123$alias,124$alias);125}126}127128// We can end up with a known-empty query if we (for example) query for129// publishable comments and the viewer is logged-out.130if (!$clauses) {131throw new PhabricatorEmptyQueryException();132}133134$where[] = qsprintf(135$conn,136'%LO',137$clauses);138}139140return $where;141}142143protected function willFilterPage(array $inlines) {144$viewer = $this->getViewer();145146if ($this->needReplyToComments) {147$reply_phids = array();148foreach ($inlines as $inline) {149$reply_phid = $inline->getReplyToCommentPHID();150if ($reply_phid) {151$reply_phids[] = $reply_phid;152}153}154155if ($reply_phids) {156$reply_inlines = newv(get_class($this), array())157->setViewer($this->getViewer())158->setParentQuery($this)159->withPHIDs($reply_phids)160->execute();161$reply_inlines = mpull($reply_inlines, null, 'getPHID');162} else {163$reply_inlines = array();164}165166foreach ($inlines as $key => $inline) {167$reply_phid = $inline->getReplyToCommentPHID();168if (!$reply_phid) {169$inline->attachReplyToComment(null);170continue;171}172$reply = idx($reply_inlines, $reply_phid);173if (!$reply) {174$this->didRejectResult($inline);175unset($inlines[$key]);176continue;177}178$inline->attachReplyToComment($reply);179}180}181182if (!$inlines) {183return $inlines;184}185186$need_drafts = $this->needAppliedDrafts;187$drop_void = $this->publishableComments;188$convert_objects = ($need_drafts || $drop_void);189190if ($convert_objects) {191$inlines = mpull($inlines, 'newInlineCommentObject');192193PhabricatorInlineComment::loadAndAttachVersionedDrafts(194$viewer,195$inlines);196197if ($need_drafts) {198// Don't count void inlines when considering draft state.199foreach ($inlines as $key => $inline) {200if ($inline->isVoidComment($viewer)) {201$this->didRejectResult($inline->getStorageObject());202unset($inlines[$key]);203continue;204}205206// For other inlines: if they have a nonempty draft state, set their207// content to the draft state content. We want to submit the comment208// as it is currently shown to the user, not as it was stored the last209// time they clicked "Save".210211$draft_state = $inline->getContentStateForEdit($viewer);212if (!$draft_state->isEmptyContentState()) {213$inline->setContentState($draft_state);214}215}216}217218// If we're loading publishable comments, discard any comments that are219// empty.220if ($drop_void) {221foreach ($inlines as $key => $inline) {222if ($inline->getTransactionPHID()) {223continue;224}225226if ($inline->isVoidComment($viewer)) {227$this->didRejectResult($inline->getStorageObject());228unset($inlines[$key]);229continue;230}231}232}233234$inlines = mpull($inlines, 'getStorageObject');235}236237return $inlines;238}239240protected function didFilterPage(array $inlines) {241$viewer = $this->getViewer();242243if ($this->needHidden) {244$viewer_phid = $viewer->getPHID();245246if ($viewer_phid) {247$hidden = $this->loadHiddenCommentIDs(248$viewer_phid,249$inlines);250} else {251$hidden = array();252}253254foreach ($inlines as $inline) {255$inline->attachIsHidden(isset($hidden[$inline->getID()]));256}257}258259if ($this->needInlineContext) {260$need_context = array();261foreach ($inlines as $inline) {262$object = $inline->newInlineCommentObject();263264if ($object->getDocumentEngineKey() !== null) {265$inline->attachInlineContext(null);266continue;267}268269$need_context[] = $inline;270}271272if ($need_context) {273$this->loadInlineCommentContext($need_context);274}275}276277return $inlines;278}279280private function loadInlineCommentContext(array $inlines) {281$cache_keys = array();282foreach ($inlines as $key => $inline) {283$object = $inline->newInlineCommentObject();284$fragment = $object->getInlineCommentCacheFragment();285286if ($fragment === null) {287continue;288}289290$cache_keys[$key] = sprintf(291'%s.context(v%d)',292$fragment,293self::INLINE_CONTEXT_CACHE_VERSION);294}295296$cache = PhabricatorCaches::getMutableStructureCache();297298$cache_map = $cache->getKeys($cache_keys);299300$context_map = array();301$need_construct = array();302303foreach ($inlines as $key => $inline) {304$cache_key = idx($cache_keys, $key);305306if ($cache_key !== null) {307if (array_key_exists($cache_key, $cache_map)) {308$cache_data = $cache_map[$cache_key];309$context_map[$key] = $this->newInlineContextFromCacheData(310$cache_data);311continue;312}313}314315$need_construct[$key] = $inline;316}317318if ($need_construct) {319$construct_map = $this->newInlineContextMap($need_construct);320321$write_map = array();322foreach ($construct_map as $key => $context) {323if ($context === null) {324$cache_data = $context;325} else {326$cache_data = $this->newCacheDataFromInlineContext($context);327}328329$cache_key = idx($cache_keys, $key);330if ($cache_key !== null) {331$write_map[$cache_key] = $cache_data;332}333}334335if ($write_map) {336$cache->setKeys($write_map);337}338339$context_map += $construct_map;340}341342foreach ($inlines as $key => $inline) {343$inline->attachInlineContext(idx($context_map, $key));344}345}346347protected function newCacheDataFromInlineContext(348PhabricatorInlineCommentContext $context) {349return $context->newCacheDataMap();350}351352final protected function simplifyContext(array $lines, $is_head) {353// We want to provide the smallest amount of context we can while still354// being useful, since the actual code is visible nearby and showing a355// ton of context is silly.356357// Examine each line until we find one that looks "useful" (not just358// whitespace or a single bracket). Once we find a useful piece of context359// to anchor the text, discard the rest of the lines beyond it.360361if ($is_head) {362$lines = array_reverse($lines, true);363}364365$saw_context = false;366foreach ($lines as $key => $line) {367if ($saw_context) {368unset($lines[$key]);369continue;370}371372$saw_context = (strlen(trim($line)) > 3);373}374375if ($is_head) {376$lines = array_reverse($lines, true);377}378379return $lines;380}381}382383384