Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/conpherence/query/ConpherenceThreadQuery.php
12262 views
1
<?php
2
3
final class ConpherenceThreadQuery
4
extends PhabricatorCursorPagedPolicyAwareQuery {
5
6
const TRANSACTION_LIMIT = 100;
7
8
private $phids;
9
private $ids;
10
private $participantPHIDs;
11
private $needParticipants;
12
private $needTransactions;
13
private $afterTransactionID;
14
private $beforeTransactionID;
15
private $transactionLimit;
16
private $fulltext;
17
private $needProfileImage;
18
19
public function needParticipants($need) {
20
$this->needParticipants = $need;
21
return $this;
22
}
23
24
public function needProfileImage($need) {
25
$this->needProfileImage = $need;
26
return $this;
27
}
28
29
public function needTransactions($need_transactions) {
30
$this->needTransactions = $need_transactions;
31
return $this;
32
}
33
34
public function withIDs(array $ids) {
35
$this->ids = $ids;
36
return $this;
37
}
38
39
public function withPHIDs(array $phids) {
40
$this->phids = $phids;
41
return $this;
42
}
43
44
public function withParticipantPHIDs(array $phids) {
45
$this->participantPHIDs = $phids;
46
return $this;
47
}
48
49
public function setAfterTransactionID($id) {
50
$this->afterTransactionID = $id;
51
return $this;
52
}
53
54
public function setBeforeTransactionID($id) {
55
$this->beforeTransactionID = $id;
56
return $this;
57
}
58
59
public function setTransactionLimit($transaction_limit) {
60
$this->transactionLimit = $transaction_limit;
61
return $this;
62
}
63
64
public function getTransactionLimit() {
65
return $this->transactionLimit;
66
}
67
68
public function withFulltext($query) {
69
$this->fulltext = $query;
70
return $this;
71
}
72
73
public function withTitleNgrams($ngrams) {
74
return $this->withNgramsConstraint(
75
id(new ConpherenceThreadTitleNgrams()),
76
$ngrams);
77
}
78
79
protected function loadPage() {
80
$table = new ConpherenceThread();
81
$conn_r = $table->establishConnection('r');
82
83
$data = queryfx_all(
84
$conn_r,
85
'SELECT thread.* FROM %T thread %Q %Q %Q %Q %Q',
86
$table->getTableName(),
87
$this->buildJoinClause($conn_r),
88
$this->buildWhereClause($conn_r),
89
$this->buildGroupClause($conn_r),
90
$this->buildOrderClause($conn_r),
91
$this->buildLimitClause($conn_r));
92
93
$conpherences = $table->loadAllFromArray($data);
94
95
if ($conpherences) {
96
$conpherences = mpull($conpherences, null, 'getPHID');
97
$this->loadParticipantsAndInitHandles($conpherences);
98
if ($this->needParticipants) {
99
$this->loadCoreHandles($conpherences, 'getParticipantPHIDs');
100
}
101
if ($this->needTransactions) {
102
$this->loadTransactionsAndHandles($conpherences);
103
}
104
if ($this->needProfileImage) {
105
$default = null;
106
$file_phids = mpull($conpherences, 'getProfileImagePHID');
107
$file_phids = array_filter($file_phids);
108
if ($file_phids) {
109
$files = id(new PhabricatorFileQuery())
110
->setParentQuery($this)
111
->setViewer($this->getViewer())
112
->withPHIDs($file_phids)
113
->execute();
114
$files = mpull($files, null, 'getPHID');
115
} else {
116
$files = array();
117
}
118
119
foreach ($conpherences as $conpherence) {
120
$file = idx($files, $conpherence->getProfileImagePHID());
121
if (!$file) {
122
if (!$default) {
123
$default = PhabricatorFile::loadBuiltin(
124
$this->getViewer(),
125
'conpherence.png');
126
}
127
$file = $default;
128
}
129
$conpherence->attachProfileImageFile($file);
130
}
131
}
132
}
133
134
return $conpherences;
135
}
136
137
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
138
if ($this->participantPHIDs !== null
139
|| ($this->fulltext !== null && strlen($this->fulltext))) {
140
return qsprintf($conn_r, 'GROUP BY thread.id');
141
} else {
142
return $this->buildApplicationSearchGroupClause($conn_r);
143
}
144
}
145
146
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
147
$joins = parent::buildJoinClauseParts($conn);
148
149
if ($this->participantPHIDs !== null) {
150
$joins[] = qsprintf(
151
$conn,
152
'JOIN %T p ON p.conpherencePHID = thread.phid',
153
id(new ConpherenceParticipant())->getTableName());
154
}
155
156
if ($this->fulltext !== null && strlen($this->fulltext)) {
157
$joins[] = qsprintf(
158
$conn,
159
'JOIN %T idx ON idx.threadPHID = thread.phid',
160
id(new ConpherenceIndex())->getTableName());
161
}
162
163
// See note in buildWhereClauseParts() about this optimization.
164
$viewer = $this->getViewer();
165
if (!$viewer->isOmnipotent() && $viewer->isLoggedIn()) {
166
$joins[] = qsprintf(
167
$conn,
168
'LEFT JOIN %T vp ON vp.conpherencePHID = thread.phid
169
AND vp.participantPHID = %s',
170
id(new ConpherenceParticipant())->getTableName(),
171
$viewer->getPHID());
172
}
173
174
return $joins;
175
}
176
177
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
178
$where = parent::buildWhereClauseParts($conn);
179
180
// Optimize policy filtering of private rooms. If we are not looking for
181
// particular rooms by ID or PHID, we can just skip over any rooms with
182
// "View Policy: Room Participants" if the viewer isn't a participant: we
183
// know they won't be able to see the room.
184
// This avoids overheating browse/search queries, since it's common for
185
// a large number of rooms to be private and have this view policy.
186
$viewer = $this->getViewer();
187
188
$can_optimize =
189
!$viewer->isOmnipotent() &&
190
($this->ids === null) &&
191
($this->phids === null);
192
193
if ($can_optimize) {
194
$members_policy = id(new ConpherenceThreadMembersPolicyRule())
195
->getObjectPolicyFullKey();
196
$policies = array(
197
$members_policy,
198
PhabricatorPolicies::POLICY_USER,
199
PhabricatorPolicies::POLICY_ADMIN,
200
PhabricatorPolicies::POLICY_NOONE,
201
);
202
203
if ($viewer->isLoggedIn()) {
204
$where[] = qsprintf(
205
$conn,
206
'thread.viewPolicy NOT IN (%Ls) OR vp.participantPHID = %s',
207
$policies,
208
$viewer->getPHID());
209
} else {
210
$where[] = qsprintf(
211
$conn,
212
'thread.viewPolicy NOT IN (%Ls)',
213
$policies);
214
}
215
}
216
217
if ($this->ids !== null) {
218
$where[] = qsprintf(
219
$conn,
220
'thread.id IN (%Ld)',
221
$this->ids);
222
}
223
224
if ($this->phids !== null) {
225
$where[] = qsprintf(
226
$conn,
227
'thread.phid IN (%Ls)',
228
$this->phids);
229
}
230
231
if ($this->participantPHIDs !== null) {
232
$where[] = qsprintf(
233
$conn,
234
'p.participantPHID IN (%Ls)',
235
$this->participantPHIDs);
236
}
237
238
if ($this->fulltext !== null && strlen($this->fulltext)) {
239
$where[] = qsprintf(
240
$conn,
241
'MATCH(idx.corpus) AGAINST (%s IN BOOLEAN MODE)',
242
$this->fulltext);
243
}
244
245
return $where;
246
}
247
248
private function loadParticipantsAndInitHandles(array $conpherences) {
249
$participants = id(new ConpherenceParticipant())
250
->loadAllWhere('conpherencePHID IN (%Ls)', array_keys($conpherences));
251
$map = mgroup($participants, 'getConpherencePHID');
252
253
foreach ($conpherences as $current_conpherence) {
254
$conpherence_phid = $current_conpherence->getPHID();
255
256
$conpherence_participants = idx(
257
$map,
258
$conpherence_phid,
259
array());
260
261
$conpherence_participants = mpull(
262
$conpherence_participants,
263
null,
264
'getParticipantPHID');
265
266
$current_conpherence->attachParticipants($conpherence_participants);
267
$current_conpherence->attachHandles(array());
268
}
269
270
return $this;
271
}
272
273
private function loadCoreHandles(
274
array $conpherences,
275
$method) {
276
277
$handle_phids = array();
278
foreach ($conpherences as $conpherence) {
279
$handle_phids[$conpherence->getPHID()] =
280
$conpherence->$method();
281
}
282
$flat_phids = array_mergev($handle_phids);
283
$viewer = $this->getViewer();
284
$handles = $viewer->loadHandles($flat_phids);
285
$handles = iterator_to_array($handles);
286
foreach ($handle_phids as $conpherence_phid => $phids) {
287
$conpherence = $conpherences[$conpherence_phid];
288
$conpherence->attachHandles(
289
$conpherence->getHandles() + array_select_keys($handles, $phids));
290
}
291
return $this;
292
}
293
294
private function loadTransactionsAndHandles(array $conpherences) {
295
// NOTE: This is older code which has been modernized to the minimum
296
// standard required by T13266. It probably isn't the best available
297
// approach to the problems it solves.
298
299
$limit = $this->getTransactionLimit();
300
if ($limit) {
301
// fetch an extra for "show older" scenarios
302
$limit = $limit + 1;
303
} else {
304
$limit = 0xFFFF;
305
}
306
307
$pager = id(new AphrontCursorPagerView())
308
->setPageSize($limit);
309
310
// We have to flip these for the underlying query class. The semantics of
311
// paging are tricky business.
312
if ($this->afterTransactionID) {
313
$pager->setBeforeID($this->afterTransactionID);
314
} else if ($this->beforeTransactionID) {
315
$pager->setAfterID($this->beforeTransactionID);
316
}
317
318
$transactions = id(new ConpherenceTransactionQuery())
319
->setViewer($this->getViewer())
320
->withObjectPHIDs(array_keys($conpherences))
321
->needHandles(true)
322
->executeWithCursorPager($pager);
323
324
$transactions = mgroup($transactions, 'getObjectPHID');
325
foreach ($conpherences as $phid => $conpherence) {
326
$current_transactions = idx($transactions, $phid, array());
327
$handles = array();
328
foreach ($current_transactions as $transaction) {
329
$handles += $transaction->getHandles();
330
}
331
$conpherence->attachHandles($conpherence->getHandles() + $handles);
332
$conpherence->attachTransactions($current_transactions);
333
}
334
return $this;
335
}
336
337
public function getQueryApplicationClass() {
338
return 'PhabricatorConpherenceApplication';
339
}
340
341
protected function getPrimaryTableAlias() {
342
return 'thread';
343
}
344
345
}
346
347