3
final class PhabricatorFileQuery
4
extends PhabricatorCursorPagedPolicyAwareQuery {
9
private $explicitUploads;
11
private $dateCreatedAfter;
12
private $dateCreatedBefore;
13
private $contentHashes;
15
public function withIDs(array $ids) {
20
public function withPHIDs(array $phids) {
21
$this->phids = $phids;
25
public function withAuthorPHIDs(array $phids) {
26
$this->authorPHIDs = $phids;
30
public function withDateCreatedBefore($date_created_before) {
31
$this->dateCreatedBefore = $date_created_before;
35
public function withDateCreatedAfter($date_created_after) {
36
$this->dateCreatedAfter = $date_created_after;
40
public function withContentHashes(array $content_hashes) {
41
$this->contentHashes = $content_hashes;
46
* Select files which are transformations of some other file. For example,
47
* you can use this query to find previously generated thumbnails of an image
50
* As a parameter, provide a list of transformation specifications. Each
51
* specification is a dictionary with the keys `originalPHID` and `transform`.
52
* The `originalPHID` is the PHID of the original file (the file which was
53
* transformed) and the `transform` is the name of the transform to query
54
* for. If you pass `true` as the `transform`, all transformations of the
55
* file will be selected.
61
* 'originalPHID' => 'PHID-FILE-aaaa',
62
* 'transform' => 'sepia',
65
* 'originalPHID' => 'PHID-FILE-bbbb',
66
* 'transform' => true,
70
* This selects the `"sepia"` transformation of the file with PHID
71
* `PHID-FILE-aaaa` and all transformations of the file with PHID
74
* @param list<dict> List of transform specifications, described above.
77
public function withTransforms(array $specs) {
78
foreach ($specs as $spec) {
79
if (!is_array($spec) ||
80
empty($spec['originalPHID']) ||
81
empty($spec['transform'])) {
83
"Transform specification must be a dictionary with keys ".
84
"'originalPHID' and 'transform'!");
88
$this->transforms = $specs;
92
public function showOnlyExplicitUploads($explicit_uploads) {
93
$this->explicitUploads = $explicit_uploads;
97
protected function loadPage() {
98
$table = new PhabricatorFile();
99
$conn_r = $table->establishConnection('r');
103
'SELECT f.* FROM %T f %Q %Q %Q %Q',
104
$table->getTableName(),
105
$this->buildJoinClause($conn_r),
106
$this->buildWhereClause($conn_r),
107
$this->buildOrderClause($conn_r),
108
$this->buildLimitClause($conn_r));
110
$files = $table->loadAllFromArray($data);
116
// We need to load attached objects to perform policy checks for files.
117
// First, load the edges.
119
$edge_type = PhabricatorEdgeConfig::TYPE_FILE_HAS_OBJECT;
120
$file_phids = mpull($files, 'getPHID');
121
$edges = id(new PhabricatorEdgeQuery())
122
->withSourcePHIDs($file_phids)
123
->withEdgeTypes(array($edge_type))
126
$object_phids = array();
127
foreach ($files as $file) {
128
$phids = array_keys($edges[$file->getPHID()][$edge_type]);
129
$file->attachObjectPHIDs($phids);
130
foreach ($phids as $phid) {
131
$object_phids[$phid] = true;
135
// If this file is a transform of another file, load that file too. If you
136
// can see the original file, you can see the thumbnail.
138
// TODO: It might be nice to put this directly on PhabricatorFile and remove
139
// the PhabricatorTransformedFile table, which would be a little simpler.
141
$xforms = id(new PhabricatorTransformedFile())->loadAllWhere(
142
'transformedPHID IN (%Ls)',
144
$xform_phids = mpull($xforms, 'getOriginalPHID', 'getTransformedPHID');
145
foreach ($xform_phids as $derived_phid => $original_phid) {
146
$object_phids[$original_phid] = true;
149
$object_phids = array_keys($object_phids);
151
// Now, load the objects.
155
// NOTE: We're explicitly turning policy exceptions off, since the rule
156
// here is "you can see the file if you can see ANY associated object".
157
// Without this explicit flag, we'll incorrectly throw unless you can
158
// see ALL associated objects.
160
$objects = id(new PhabricatorObjectQuery())
161
->setParentQuery($this)
162
->setViewer($this->getViewer())
163
->withPHIDs($object_phids)
164
->setRaisePolicyExceptions(false)
166
$objects = mpull($objects, null, 'getPHID');
169
foreach ($files as $file) {
170
$file_objects = array_select_keys($objects, $file->getObjectPHIDs());
171
$file->attachObjects($file_objects);
174
foreach ($files as $key => $file) {
175
$original_phid = idx($xform_phids, $file->getPHID());
176
if ($original_phid == PhabricatorPHIDConstants::PHID_VOID) {
177
// This is a special case for builtin files, which are handled
180
} else if ($original_phid) {
181
$original = idx($objects, $original_phid);
183
// If the viewer can't see the original file, also prevent them from
184
// seeing the transformed file.
185
$this->didRejectResult($file);
192
$file->attachOriginalFile($original);
198
private function buildJoinClause(AphrontDatabaseConnection $conn_r) {
201
if ($this->transforms) {
204
'JOIN %T t ON t.transformedPHID = f.phid',
205
id(new PhabricatorTransformedFile())->getTableName());
208
return implode(' ', $joins);
211
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
214
$where[] = $this->buildPagingClause($conn_r);
230
if ($this->authorPHIDs) {
233
'f.authorPHID IN (%Ls)',
237
if ($this->explicitUploads) {
240
'f.isExplicitUpload = true');
243
if ($this->transforms) {
245
foreach ($this->transforms as $transform) {
246
if ($transform['transform'] === true) {
247
$clauses[] = qsprintf(
249
'(t.originalPHID = %s)',
250
$transform['originalPHID']);
252
$clauses[] = qsprintf(
254
'(t.originalPHID = %s AND t.transform = %s)',
255
$transform['originalPHID'],
256
$transform['transform']);
259
$where[] = qsprintf($conn_r, '(%Q)', implode(') OR (', $clauses));
262
if ($this->dateCreatedAfter) {
265
'f.dateCreated >= %d',
266
$this->dateCreatedAfter);
269
if ($this->dateCreatedBefore) {
272
'f.dateCreated <= %d',
273
$this->dateCreatedBefore);
276
if ($this->contentHashes) {
279
'f.contentHash IN (%Ls)',
280
$this->contentHashes);
283
return $this->formatWhereClause($where);
286
protected function getPagingColumn() {
290
public function getQueryApplicationClass() {
291
return 'PhabricatorFilesApplication';