Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/conpherence/query/ConpherenceThreadQuery.php b/src/applications/conpherence/query/ConpherenceThreadQuery.php
index 99fd18878e..34bbf7e6b0 100644
--- a/src/applications/conpherence/query/ConpherenceThreadQuery.php
+++ b/src/applications/conpherence/query/ConpherenceThreadQuery.php
@@ -1,344 +1,345 @@
<?php
final class ConpherenceThreadQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
const TRANSACTION_LIMIT = 100;
private $phids;
private $ids;
private $participantPHIDs;
private $needParticipants;
private $needTransactions;
private $afterTransactionID;
private $beforeTransactionID;
private $transactionLimit;
private $fulltext;
private $needProfileImage;
public function needParticipants($need) {
$this->needParticipants = $need;
return $this;
}
public function needProfileImage($need) {
$this->needProfileImage = $need;
return $this;
}
public function needTransactions($need_transactions) {
$this->needTransactions = $need_transactions;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withParticipantPHIDs(array $phids) {
$this->participantPHIDs = $phids;
return $this;
}
public function setAfterTransactionID($id) {
$this->afterTransactionID = $id;
return $this;
}
public function setBeforeTransactionID($id) {
$this->beforeTransactionID = $id;
return $this;
}
public function setTransactionLimit($transaction_limit) {
$this->transactionLimit = $transaction_limit;
return $this;
}
public function getTransactionLimit() {
return $this->transactionLimit;
}
public function withFulltext($query) {
$this->fulltext = $query;
return $this;
}
public function withTitleNgrams($ngrams) {
return $this->withNgramsConstraint(
id(new ConpherenceThreadTitleNgrams()),
$ngrams);
}
protected function loadPage() {
$table = new ConpherenceThread();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT thread.* FROM %T thread %Q %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildGroupClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
$conpherences = $table->loadAllFromArray($data);
if ($conpherences) {
$conpherences = mpull($conpherences, null, 'getPHID');
$this->loadParticipantsAndInitHandles($conpherences);
if ($this->needParticipants) {
$this->loadCoreHandles($conpherences, 'getParticipantPHIDs');
}
if ($this->needTransactions) {
$this->loadTransactionsAndHandles($conpherences);
}
if ($this->needProfileImage) {
$default = null;
$file_phids = mpull($conpherences, 'getProfileImagePHID');
$file_phids = array_filter($file_phids);
if ($file_phids) {
$files = id(new PhabricatorFileQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withPHIDs($file_phids)
->execute();
$files = mpull($files, null, 'getPHID');
} else {
$files = array();
}
foreach ($conpherences as $conpherence) {
$file = idx($files, $conpherence->getProfileImagePHID());
if (!$file) {
if (!$default) {
$default = PhabricatorFile::loadBuiltin(
$this->getViewer(),
'conpherence.png');
}
$file = $default;
}
$conpherence->attachProfileImageFile($file);
}
}
}
return $conpherences;
}
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
- if ($this->participantPHIDs !== null || strlen($this->fulltext)) {
+ if ($this->participantPHIDs !== null ||
+ phutil_nonempty_string($this->fulltext)) {
return qsprintf($conn_r, 'GROUP BY thread.id');
} else {
return $this->buildApplicationSearchGroupClause($conn_r);
}
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->participantPHIDs !== null) {
$joins[] = qsprintf(
$conn,
'JOIN %T p ON p.conpherencePHID = thread.phid',
id(new ConpherenceParticipant())->getTableName());
}
- if (strlen($this->fulltext)) {
+ if (phutil_nonempty_string($this->fulltext)) {
$joins[] = qsprintf(
$conn,
'JOIN %T idx ON idx.threadPHID = thread.phid',
id(new ConpherenceIndex())->getTableName());
}
// See note in buildWhereClauseParts() about this optimization.
$viewer = $this->getViewer();
if (!$viewer->isOmnipotent() && $viewer->isLoggedIn()) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T vp ON vp.conpherencePHID = thread.phid
AND vp.participantPHID = %s',
id(new ConpherenceParticipant())->getTableName(),
$viewer->getPHID());
}
return $joins;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
// Optimize policy filtering of private rooms. If we are not looking for
// particular rooms by ID or PHID, we can just skip over any rooms with
// "View Policy: Room Participants" if the viewer isn't a participant: we
// know they won't be able to see the room.
// This avoids overheating browse/search queries, since it's common for
// a large number of rooms to be private and have this view policy.
$viewer = $this->getViewer();
$can_optimize =
!$viewer->isOmnipotent() &&
($this->ids === null) &&
($this->phids === null);
if ($can_optimize) {
$members_policy = id(new ConpherenceThreadMembersPolicyRule())
->getObjectPolicyFullKey();
$policies = array(
$members_policy,
PhabricatorPolicies::POLICY_USER,
PhabricatorPolicies::POLICY_ADMIN,
PhabricatorPolicies::POLICY_NOONE,
);
if ($viewer->isLoggedIn()) {
$where[] = qsprintf(
$conn,
'thread.viewPolicy NOT IN (%Ls) OR vp.participantPHID = %s',
$policies,
$viewer->getPHID());
} else {
$where[] = qsprintf(
$conn,
'thread.viewPolicy NOT IN (%Ls)',
$policies);
}
}
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'thread.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'thread.phid IN (%Ls)',
$this->phids);
}
if ($this->participantPHIDs !== null) {
$where[] = qsprintf(
$conn,
'p.participantPHID IN (%Ls)',
$this->participantPHIDs);
}
- if (strlen($this->fulltext)) {
+ if (phutil_nonempty_string($this->fulltext)) {
$where[] = qsprintf(
$conn,
'MATCH(idx.corpus) AGAINST (%s IN BOOLEAN MODE)',
$this->fulltext);
}
return $where;
}
private function loadParticipantsAndInitHandles(array $conpherences) {
$participants = id(new ConpherenceParticipant())
->loadAllWhere('conpherencePHID IN (%Ls)', array_keys($conpherences));
$map = mgroup($participants, 'getConpherencePHID');
foreach ($conpherences as $current_conpherence) {
$conpherence_phid = $current_conpherence->getPHID();
$conpherence_participants = idx(
$map,
$conpherence_phid,
array());
$conpherence_participants = mpull(
$conpherence_participants,
null,
'getParticipantPHID');
$current_conpherence->attachParticipants($conpherence_participants);
$current_conpherence->attachHandles(array());
}
return $this;
}
private function loadCoreHandles(
array $conpherences,
$method) {
$handle_phids = array();
foreach ($conpherences as $conpherence) {
$handle_phids[$conpherence->getPHID()] =
$conpherence->$method();
}
$flat_phids = array_mergev($handle_phids);
$viewer = $this->getViewer();
$handles = $viewer->loadHandles($flat_phids);
$handles = iterator_to_array($handles);
foreach ($handle_phids as $conpherence_phid => $phids) {
$conpherence = $conpherences[$conpherence_phid];
$conpherence->attachHandles(
$conpherence->getHandles() + array_select_keys($handles, $phids));
}
return $this;
}
private function loadTransactionsAndHandles(array $conpherences) {
// NOTE: This is older code which has been modernized to the minimum
// standard required by T13266. It probably isn't the best available
// approach to the problems it solves.
$limit = $this->getTransactionLimit();
if ($limit) {
// fetch an extra for "show older" scenarios
$limit = $limit + 1;
} else {
$limit = 0xFFFF;
}
$pager = id(new AphrontCursorPagerView())
->setPageSize($limit);
// We have to flip these for the underlying query class. The semantics of
// paging are tricky business.
if ($this->afterTransactionID) {
$pager->setBeforeID($this->afterTransactionID);
} else if ($this->beforeTransactionID) {
$pager->setAfterID($this->beforeTransactionID);
}
$transactions = id(new ConpherenceTransactionQuery())
->setViewer($this->getViewer())
->withObjectPHIDs(array_keys($conpherences))
->needHandles(true)
->executeWithCursorPager($pager);
$transactions = mgroup($transactions, 'getObjectPHID');
foreach ($conpherences as $phid => $conpherence) {
$current_transactions = idx($transactions, $phid, array());
$handles = array();
foreach ($current_transactions as $transaction) {
$handles += $transaction->getHandles();
}
$conpherence->attachHandles($conpherence->getHandles() + $handles);
$conpherence->attachTransactions($current_transactions);
}
return $this;
}
public function getQueryApplicationClass() {
return 'PhabricatorConpherenceApplication';
}
protected function getPrimaryTableAlias() {
return 'thread';
}
}
diff --git a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
index cbaf43b0a9..77e54157cf 100644
--- a/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
+++ b/src/applications/conpherence/query/ConpherenceThreadSearchEngine.php
@@ -1,445 +1,445 @@
<?php
final class ConpherenceThreadSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Conpherence Rooms');
}
public function getApplicationClassName() {
return 'PhabricatorConpherenceApplication';
}
public function newQuery() {
return id(new ConpherenceThreadQuery())
->needProfileImage(true);
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorUsersSearchField())
->setLabel(pht('Participants'))
->setKey('participants')
->setAliases(array('participant')),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Rooms'))
->setKey('phids')
->setDescription(pht('Search by room titles.'))
->setDatasource(id(new ConpherenceThreadDatasource())),
id(new PhabricatorSearchTextField())
->setLabel(pht('Room Contains Words'))
->setKey('fulltext'),
);
}
protected function getDefaultFieldOrder() {
return array(
'participants',
'...',
);
}
protected function shouldShowOrderField() {
return false;
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['participants']) {
$query->withParticipantPHIDs($map['participants']);
}
if ($map['fulltext']) {
$query->withFulltext($map['fulltext']);
}
if ($map['phids']) {
$query->withPHIDs($map['phids']);
}
return $query;
}
protected function getURI($path) {
return '/conpherence/search/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array();
$names['all'] = pht('All Rooms');
if ($this->requireViewer()->isLoggedIn()) {
$names['participant'] = pht('Joined Rooms');
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'participant':
return $query->setParameter(
'participants',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $conpherences,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($conpherences, 'ConpherenceThread');
$viewer = $this->requireViewer();
$policy_objects = ConpherenceThread::loadViewPolicyObjects(
$viewer,
$conpherences);
$engines = array();
$fulltext = $query->getParameter('fulltext');
- if (strlen($fulltext) && $conpherences) {
+ if (phutil_nonempty_string($fulltext) && $conpherences) {
$context = $this->loadContextMessages($conpherences, $fulltext);
$author_phids = array();
foreach ($context as $phid => $messages) {
$conpherence = $conpherences[$phid];
$engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer)
->setContextObject($conpherence);
foreach ($messages as $group) {
foreach ($group as $message) {
$xaction = $message['xaction'];
if ($xaction) {
$author_phids[] = $xaction->getAuthorPHID();
$engine->addObject(
$xaction->getComment(),
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
}
$engine->process();
$engines[$phid] = $engine;
}
$handles = $viewer->loadHandles($author_phids);
$handles = iterator_to_array($handles);
} else {
$context = array();
}
$content = array();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($conpherences as $conpherence_phid => $conpherence) {
$created = phabricator_date($conpherence->getDateCreated(), $viewer);
$title = $conpherence->getTitle();
$monogram = $conpherence->getMonogram();
$icon_name = $conpherence->getPolicyIconName($policy_objects);
$icon = id(new PHUIIconView())
->setIcon($icon_name);
if (!strlen($fulltext)) {
$item = id(new PHUIObjectItemView())
->setObjectName($conpherence->getMonogram())
->setHeader($title)
->setHref('/'.$conpherence->getMonogram())
->setObject($conpherence)
->setImageURI($conpherence->getProfileImageURI())
->addIcon('none', $created)
->addIcon(
'none',
pht('Messages: %d', $conpherence->getMessageCount()))
->addAttribute(
array(
$icon,
' ',
pht(
'Last updated %s',
phabricator_datetime($conpherence->getDateModified(), $viewer)),
));
$list->addItem($item);
} else {
$messages = idx($context, $conpherence_phid);
$box = array();
$list = null;
if ($messages) {
foreach ($messages as $group) {
$rows = array();
foreach ($group as $message) {
$xaction = $message['xaction'];
if (!$xaction) {
continue;
}
$view = id(new ConpherenceTransactionView())
->setUser($viewer)
->setHandles($handles)
->setMarkupEngine($engines[$conpherence_phid])
->setConpherenceThread($conpherence)
->setConpherenceTransaction($xaction)
->setSearchResult(true)
->addClass('conpherence-fulltext-result');
if ($message['match']) {
$view->addClass('conpherence-fulltext-match');
}
$rows[] = $view;
}
$box[] = id(new PHUIBoxView())
->appendChild($rows)
->addClass('conpherence-fulltext-results');
}
}
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon($icon_name)
->setHref('/'.$monogram);
$content[] = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($box);
}
}
if ($list) {
$content = $list;
} else {
$content = id(new PHUIBoxView())
->addClass('conpherence-search-room-results')
->appendChild($content);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($content);
$result->setNoDataString(pht('No results found.'));
return $result;
}
private function loadContextMessages(array $threads, $fulltext) {
$phids = mpull($threads, 'getPHID');
// We want to load a few messages for each thread in the result list, to
// show some of the actual content hits to help the user find what they
// are looking for.
// This method is trying to batch this lookup in most cases, so we do
// between one and "a handful" of queries instead of one per thread in
// most cases. To do this:
//
// - Load a big block of results for all of the threads.
// - If we didn't get a full block back, we have everything that matches
// the query. Sort it out and exit.
// - Otherwise, some threads had a ton of hits, so we might not be
// getting everything we want (we could be getting back 1,000 hits for
// the first thread). Remove any threads which we have enough results
// for and try again.
// - Repeat until we have everything or every thread has enough results.
//
// In the worst case, we could end up degrading to one query per thread,
// but this is incredibly unlikely on real data.
// Size of the result blocks we're going to load.
$limit = 1000;
// Number of messages we want for each thread.
$want = 3;
$need = $phids;
$hits = array();
while ($need) {
$rows = id(new ConpherenceFulltextQuery())
->withThreadPHIDs($need)
->withFulltext($fulltext)
->setLimit($limit)
->execute();
foreach ($rows as $row) {
$hits[$row['threadPHID']][] = $row;
}
if (count($rows) < $limit) {
break;
}
foreach ($need as $key => $phid) {
if (count($hits[$phid]) >= $want) {
unset($need[$key]);
}
}
}
// Now that we have all the fulltext matches, throw away any extras that we
// aren't going to render so we don't need to do lookups on them.
foreach ($hits as $phid => $rows) {
if (count($rows) > $want) {
$hits[$phid] = array_slice($rows, 0, $want);
}
}
// For each fulltext match, we want to render a message before and after
// the match to give it some context. We already know the transactions
// before each match because the rows have a "previousTransactionPHID",
// but we need to do one more query to figure out the transactions after
// each match.
// Collect the transactions we want to find the next transactions for.
$after = array();
foreach ($hits as $phid => $rows) {
foreach ($rows as $row) {
$after[] = $row['transactionPHID'];
}
}
// Look up the next transactions.
if ($after) {
$after_rows = id(new ConpherenceFulltextQuery())
->withPreviousTransactionPHIDs($after)
->execute();
} else {
$after_rows = array();
}
// Build maps from PHIDs to the previous and next PHIDs.
$prev_map = array();
$next_map = array();
foreach ($after_rows as $row) {
$next_map[$row['previousTransactionPHID']] = $row['transactionPHID'];
}
foreach ($hits as $phid => $rows) {
foreach ($rows as $row) {
$prev = $row['previousTransactionPHID'];
if ($prev) {
$prev_map[$row['transactionPHID']] = $prev;
$next_map[$prev] = $row['transactionPHID'];
}
}
}
// Now we're going to collect the actual transaction PHIDs, in order, that
// we want to show for each thread.
$groups = array();
foreach ($hits as $thread_phid => $rows) {
$rows = ipull($rows, null, 'transactionPHID');
$done = array();
foreach ($rows as $phid => $row) {
if (isset($done[$phid])) {
continue;
}
$done[$phid] = true;
$group = array();
// Walk backward, finding all the previous results. We can just keep
// going until we run out of results because we've only loaded things
// that we want to show.
$prev = $phid;
while (true) {
if (!isset($prev_map[$prev])) {
// No previous transaction, so we're done.
break;
}
$prev = $prev_map[$prev];
if (isset($rows[$prev])) {
$match = true;
$done[$prev] = true;
} else {
$match = false;
}
$group[] = array(
'phid' => $prev,
'match' => $match,
);
}
if (count($group) > 1) {
$group = array_reverse($group);
}
$group[] = array(
'phid' => $phid,
'match' => true,
);
$next = $phid;
while (true) {
if (!isset($next_map[$next])) {
break;
}
$next = $next_map[$next];
if (isset($rows[$next])) {
$match = true;
$done[$next] = true;
} else {
$match = false;
}
$group[] = array(
'phid' => $next,
'match' => $match,
);
}
$groups[$thread_phid][] = $group;
}
}
// Load all the actual transactions we need.
$xaction_phids = array();
foreach ($groups as $thread_phid => $group) {
foreach ($group as $list) {
foreach ($list as $item) {
$xaction_phids[] = $item['phid'];
}
}
}
if ($xaction_phids) {
$xactions = id(new ConpherenceTransactionQuery())
->setViewer($this->requireViewer())
->withPHIDs($xaction_phids)
->needComments(true)
->execute();
$xactions = mpull($xactions, null, 'getPHID');
} else {
$xactions = array();
}
foreach ($groups as $thread_phid => $group) {
foreach ($group as $key => $list) {
foreach ($list as $lkey => $item) {
$xaction = idx($xactions, $item['phid']);
if ($xaction->shouldHide()) {
continue;
}
$groups[$thread_phid][$key][$lkey]['xaction'] = $xaction;
}
}
}
// TODO: Sort the groups chronologically?
return $groups;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jul 27, 2:14 PM (1 w, 5 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
185821
Default Alt Text
(23 KB)

Event Timeline