@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 141 lines 4.0 kB view raw
1<?php 2 3final class PhabricatorFileTransformController 4 extends PhabricatorFileController { 5 6 public function shouldRequireLogin() { 7 return false; 8 } 9 10 public function handleRequest(AphrontRequest $request) { 11 $viewer = $this->getViewer(); 12 13 // NOTE: This is a public/CDN endpoint, and permission to see files is 14 // controlled by knowing the secret key, not by authentication. 15 16 $is_regenerate = $request->getBool('regenerate'); 17 18 $source_phid = $request->getURIData('phid'); 19 $file = id(new PhabricatorFileQuery()) 20 ->setViewer(PhabricatorUser::getOmnipotentUser()) 21 ->withPHIDs(array($source_phid)) 22 ->executeOne(); 23 if (!$file) { 24 return new Aphront404Response(); 25 } 26 27 $secret_key = $request->getURIData('key'); 28 if (!$file->validateSecretKey($secret_key)) { 29 return new Aphront403Response(); 30 } 31 32 $transform = $request->getURIData('transform'); 33 $xform = $this->loadTransform($source_phid, $transform); 34 35 if ($xform) { 36 if ($is_regenerate) { 37 $this->destroyTransform($xform); 38 } else { 39 return $this->buildTransformedFileResponse($xform); 40 } 41 } 42 43 $xforms = PhabricatorFileTransform::getAllTransforms(); 44 if (!isset($xforms[$transform])) { 45 return new Aphront404Response(); 46 } 47 48 $xform = $xforms[$transform]; 49 50 // We're essentially just building a cache here and don't need CSRF 51 // protection. 52 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 53 54 $xformed_file = null; 55 if ($xform->canApplyTransform($file)) { 56 try { 57 $xformed_file = $xforms[$transform]->applyTransform($file); 58 } catch (Exception $ex) { 59 // In normal transform mode, we ignore failures and generate a 60 // default transform below. If we're explicitly regenerating the 61 // thumbnail, rethrow the exception. 62 if ($is_regenerate) { 63 throw $ex; 64 } 65 } 66 } 67 68 if (!$xformed_file) { 69 $xformed_file = $xform->getDefaultTransform($file); 70 } 71 72 if (!$xformed_file) { 73 return new Aphront400Response(); 74 } 75 76 $xform = id(new PhabricatorTransformedFile()) 77 ->setOriginalPHID($source_phid) 78 ->setTransform($transform) 79 ->setTransformedPHID($xformed_file->getPHID()); 80 81 try { 82 $xform->save(); 83 } catch (AphrontDuplicateKeyQueryException $ex) { 84 // If we collide when saving, we've raced another endpoint which was 85 // transforming the same file. Just throw our work away and use that 86 // transform instead. 87 $this->destroyTransform($xform); 88 $xform = $this->loadTransform($source_phid, $transform); 89 if (!$xform) { 90 return new Aphront404Response(); 91 } 92 } 93 94 return $this->buildTransformedFileResponse($xform); 95 } 96 97 private function buildTransformedFileResponse( 98 PhabricatorTransformedFile $xform) { 99 100 $file = id(new PhabricatorFileQuery()) 101 ->setViewer(PhabricatorUser::getOmnipotentUser()) 102 ->withPHIDs(array($xform->getTransformedPHID())) 103 ->executeOne(); 104 if (!$file) { 105 return new Aphront404Response(); 106 } 107 108 // TODO: We could just delegate to the file view controller instead, 109 // which would save the client a roundtrip, but is slightly more complex. 110 111 return $file->getRedirectResponse(); 112 } 113 114 private function destroyTransform(PhabricatorTransformedFile $xform) { 115 $engine = new PhabricatorDestructionEngine(); 116 $file = id(new PhabricatorFileQuery()) 117 ->setViewer($engine->getViewer()) 118 ->withPHIDs(array($xform->getTransformedPHID())) 119 ->executeOne(); 120 121 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 122 123 if (!$file) { 124 if ($xform->getID()) { 125 $xform->delete(); 126 } 127 } else { 128 $engine->destroyObject($file); 129 } 130 131 unset($unguarded); 132 } 133 134 private function loadTransform($source_phid, $transform) { 135 return id(new PhabricatorTransformedFile())->loadOneWhere( 136 'originalPHID = %s AND transform = %s', 137 $source_phid, 138 $transform); 139 } 140 141}