2011-05-22 23:40:51 +02:00
|
|
|
<?php
|
|
|
|
|
2012-03-10 00:46:25 +01:00
|
|
|
final class PhabricatorFileTransformController
|
|
|
|
extends PhabricatorFileController {
|
2011-05-22 23:40:51 +02:00
|
|
|
|
2013-10-05 21:55:34 +02:00
|
|
|
public function shouldRequireLogin() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:16:08 +02:00
|
|
|
public function handleRequest(AphrontRequest $request) {
|
|
|
|
$viewer = $this->getViewer();
|
2011-05-22 23:40:51 +02:00
|
|
|
|
2013-10-05 21:55:34 +02:00
|
|
|
// NOTE: This is a public/CDN endpoint, and permission to see files is
|
|
|
|
// controlled by knowing the secret key, not by authentication.
|
|
|
|
|
2015-05-12 19:50:02 +02:00
|
|
|
$is_regenerate = $request->getBool('regenerate');
|
|
|
|
|
2015-05-12 15:16:08 +02:00
|
|
|
$source_phid = $request->getURIData('phid');
|
2013-09-30 18:38:13 +02:00
|
|
|
$file = id(new PhabricatorFileQuery())
|
2013-10-05 21:55:34 +02:00
|
|
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
2015-05-12 15:16:08 +02:00
|
|
|
->withPHIDs(array($source_phid))
|
2013-09-30 18:38:13 +02:00
|
|
|
->executeOne();
|
2012-10-23 04:06:56 +02:00
|
|
|
if (!$file) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:16:08 +02:00
|
|
|
$secret_key = $request->getURIData('key');
|
|
|
|
if (!$file->validateSecretKey($secret_key)) {
|
2012-10-23 04:06:56 +02:00
|
|
|
return new Aphront403Response();
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:16:08 +02:00
|
|
|
$transform = $request->getURIData('transform');
|
2015-05-21 18:42:20 +02:00
|
|
|
$xform = $this->loadTransform($source_phid, $transform);
|
2011-05-22 23:40:51 +02:00
|
|
|
|
|
|
|
if ($xform) {
|
2015-05-12 19:50:02 +02:00
|
|
|
if ($is_regenerate) {
|
|
|
|
$this->destroyTransform($xform);
|
|
|
|
} else {
|
|
|
|
return $this->buildTransformedFileResponse($xform);
|
|
|
|
}
|
2011-05-22 23:40:51 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 00:50:46 +02:00
|
|
|
$xforms = PhabricatorFileTransform::getAllTransforms();
|
|
|
|
if (!isset($xforms[$transform])) {
|
|
|
|
return new Aphront404Response();
|
2011-05-22 23:40:51 +02:00
|
|
|
}
|
|
|
|
|
2015-05-13 00:50:46 +02:00
|
|
|
$xform = $xforms[$transform];
|
|
|
|
|
2011-08-17 23:39:00 +02:00
|
|
|
// We're essentially just building a cache here and don't need CSRF
|
|
|
|
// protection.
|
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
|
|
|
|
2015-05-12 15:16:18 +02:00
|
|
|
$xformed_file = null;
|
2015-05-13 00:50:46 +02:00
|
|
|
if ($xform->canApplyTransform($file)) {
|
|
|
|
try {
|
|
|
|
$xformed_file = $xforms[$transform]->applyTransform($file);
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
// In normal transform mode, we ignore failures and generate a
|
|
|
|
// default transform below. If we're explicitly regenerating the
|
|
|
|
// thumbnail, rethrow the exception.
|
|
|
|
if ($is_regenerate) {
|
|
|
|
throw $ex;
|
2015-05-12 17:16:37 +02:00
|
|
|
}
|
|
|
|
}
|
2015-05-12 15:16:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$xformed_file) {
|
2015-05-13 00:50:46 +02:00
|
|
|
$xformed_file = $xform->getDefaultTransform($file);
|
2011-05-22 23:40:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!$xformed_file) {
|
|
|
|
return new Aphront400Response();
|
|
|
|
}
|
|
|
|
|
2015-05-12 15:16:08 +02:00
|
|
|
$xform = id(new PhabricatorTransformedFile())
|
|
|
|
->setOriginalPHID($source_phid)
|
|
|
|
->setTransform($transform)
|
2015-05-21 18:42:20 +02:00
|
|
|
->setTransformedPHID($xformed_file->getPHID());
|
|
|
|
|
|
|
|
try {
|
|
|
|
$xform->save();
|
|
|
|
} catch (AphrontDuplicateKeyQueryException $ex) {
|
|
|
|
// If we collide when saving, we've raced another endpoint which was
|
|
|
|
// transforming the same file. Just throw our work away and use that
|
|
|
|
// transform instead.
|
|
|
|
$this->destroyTransform($xform);
|
|
|
|
$xform = $this->loadTransform($source_phid, $transform);
|
|
|
|
if (!$xform) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
}
|
2011-05-22 23:40:51 +02:00
|
|
|
|
|
|
|
return $this->buildTransformedFileResponse($xform);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildTransformedFileResponse(
|
|
|
|
PhabricatorTransformedFile $xform) {
|
|
|
|
|
2013-09-30 18:38:13 +02:00
|
|
|
$file = id(new PhabricatorFileQuery())
|
2013-10-05 21:55:34 +02:00
|
|
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
2013-09-30 18:38:13 +02:00
|
|
|
->withPHIDs(array($xform->getTransformedPHID()))
|
|
|
|
->executeOne();
|
|
|
|
if (!$file) {
|
|
|
|
return new Aphront404Response();
|
2012-01-10 23:48:55 +01:00
|
|
|
}
|
|
|
|
|
2011-05-22 23:40:51 +02:00
|
|
|
// TODO: We could just delegate to the file view controller instead,
|
|
|
|
// which would save the client a roundtrip, but is slightly more complex.
|
2014-08-19 23:21:32 +02:00
|
|
|
|
2014-08-20 00:53:15 +02:00
|
|
|
return $file->getRedirectResponse();
|
2011-05-22 23:40:51 +02:00
|
|
|
}
|
|
|
|
|
2015-05-12 19:50:02 +02:00
|
|
|
private function destroyTransform(PhabricatorTransformedFile $xform) {
|
2015-05-15 23:07:17 +02:00
|
|
|
$engine = new PhabricatorDestructionEngine();
|
2015-05-12 19:50:02 +02:00
|
|
|
$file = id(new PhabricatorFileQuery())
|
2015-05-15 23:07:17 +02:00
|
|
|
->setViewer($engine->getViewer())
|
2015-05-12 19:50:02 +02:00
|
|
|
->withPHIDs(array($xform->getTransformedPHID()))
|
|
|
|
->executeOne();
|
|
|
|
|
|
|
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
|
|
|
|
|
|
|
if (!$file) {
|
2015-05-21 18:42:20 +02:00
|
|
|
if ($xform->getID()) {
|
|
|
|
$xform->delete();
|
|
|
|
}
|
2015-05-12 19:50:02 +02:00
|
|
|
} else {
|
|
|
|
$engine->destroyObject($file);
|
|
|
|
}
|
|
|
|
|
|
|
|
unset($unguarded);
|
|
|
|
}
|
|
|
|
|
2015-05-21 18:42:20 +02:00
|
|
|
private function loadTransform($source_phid, $transform) {
|
|
|
|
return id(new PhabricatorTransformedFile())->loadOneWhere(
|
|
|
|
'originalPHID = %s AND transform = %s',
|
|
|
|
$source_phid,
|
|
|
|
$transform);
|
|
|
|
}
|
|
|
|
|
2011-05-22 23:40:51 +02:00
|
|
|
}
|