Path: blob/master/src/applications/files/controller/PhabricatorFileTransformController.php
12242 views
<?php12final class PhabricatorFileTransformController3extends PhabricatorFileController {45public function shouldRequireLogin() {6return false;7}89public function handleRequest(AphrontRequest $request) {10$viewer = $this->getViewer();1112// NOTE: This is a public/CDN endpoint, and permission to see files is13// controlled by knowing the secret key, not by authentication.1415$is_regenerate = $request->getBool('regenerate');1617$source_phid = $request->getURIData('phid');18$file = id(new PhabricatorFileQuery())19->setViewer(PhabricatorUser::getOmnipotentUser())20->withPHIDs(array($source_phid))21->executeOne();22if (!$file) {23return new Aphront404Response();24}2526$secret_key = $request->getURIData('key');27if (!$file->validateSecretKey($secret_key)) {28return new Aphront403Response();29}3031$transform = $request->getURIData('transform');32$xform = $this->loadTransform($source_phid, $transform);3334if ($xform) {35if ($is_regenerate) {36$this->destroyTransform($xform);37} else {38return $this->buildTransformedFileResponse($xform);39}40}4142$xforms = PhabricatorFileTransform::getAllTransforms();43if (!isset($xforms[$transform])) {44return new Aphront404Response();45}4647$xform = $xforms[$transform];4849// We're essentially just building a cache here and don't need CSRF50// protection.51$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();5253$xformed_file = null;54if ($xform->canApplyTransform($file)) {55try {56$xformed_file = $xforms[$transform]->applyTransform($file);57} catch (Exception $ex) {58// In normal transform mode, we ignore failures and generate a59// default transform below. If we're explicitly regenerating the60// thumbnail, rethrow the exception.61if ($is_regenerate) {62throw $ex;63}64}65}6667if (!$xformed_file) {68$xformed_file = $xform->getDefaultTransform($file);69}7071if (!$xformed_file) {72return new Aphront400Response();73}7475$xform = id(new PhabricatorTransformedFile())76->setOriginalPHID($source_phid)77->setTransform($transform)78->setTransformedPHID($xformed_file->getPHID());7980try {81$xform->save();82} catch (AphrontDuplicateKeyQueryException $ex) {83// If we collide when saving, we've raced another endpoint which was84// transforming the same file. Just throw our work away and use that85// transform instead.86$this->destroyTransform($xform);87$xform = $this->loadTransform($source_phid, $transform);88if (!$xform) {89return new Aphront404Response();90}91}9293return $this->buildTransformedFileResponse($xform);94}9596private function buildTransformedFileResponse(97PhabricatorTransformedFile $xform) {9899$file = id(new PhabricatorFileQuery())100->setViewer(PhabricatorUser::getOmnipotentUser())101->withPHIDs(array($xform->getTransformedPHID()))102->executeOne();103if (!$file) {104return new Aphront404Response();105}106107// TODO: We could just delegate to the file view controller instead,108// which would save the client a roundtrip, but is slightly more complex.109110return $file->getRedirectResponse();111}112113private function destroyTransform(PhabricatorTransformedFile $xform) {114$engine = new PhabricatorDestructionEngine();115$file = id(new PhabricatorFileQuery())116->setViewer($engine->getViewer())117->withPHIDs(array($xform->getTransformedPHID()))118->executeOne();119120$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();121122if (!$file) {123if ($xform->getID()) {124$xform->delete();125}126} else {127$engine->destroyObject($file);128}129130unset($unguarded);131}132133private function loadTransform($source_phid, $transform) {134return id(new PhabricatorTransformedFile())->loadOneWhere(135'originalPHID = %s AND transform = %s',136$source_phid,137$transform);138}139140}141142143