@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Always serve "{meme ...}" from the CDN domain, never from the primary domain

Summary:
Ref T13101. This is a minimal change to make "{meme ...}" work with the new Content-Security-Policy by using an Ajax request to generate the image and then swapping the source on the client.

This could be much cleaner (see T5258, etc).

Test Plan: Used `{meme, src=cat6, above=i am, below=cat}`, chuckled completely unironically.

Maniphest Tasks: T13101

Differential Revision: https://secure.phabricator.com/D19196

+98 -41
+82 -34
src/applications/macro/controller/PhabricatorMacroMemeController.php
··· 13 13 $lower_text = $request->getStr('lowertext'); 14 14 $viewer = $request->getViewer(); 15 15 16 - $uri = self::generateMacro($viewer, $macro_name, 17 - $upper_text, $lower_text); 18 - if ($uri === false) { 19 - return new Aphront404Response(); 20 - } 21 - return id(new AphrontRedirectResponse()) 22 - ->setIsExternal(true) 23 - ->setURI($uri); 16 + $uri = self::generateMacro( 17 + $viewer, 18 + $macro_name, 19 + $upper_text, 20 + $lower_text); 21 + 22 + $content = array( 23 + 'imageURI' => $uri, 24 + ); 25 + 26 + return id(new AphrontAjaxResponse())->setContent($content); 24 27 } 25 28 26 - public static function generateMacro($viewer, $macro_name, $upper_text, 27 - $lower_text) { 29 + public static function generateMacro( 30 + PhabricatorUser $viewer, 31 + $macro_name, 32 + $upper_text, 33 + $lower_text) { 34 + 28 35 $macro = id(new PhabricatorMacroQuery()) 29 36 ->setViewer($viewer) 30 37 ->withNames(array($macro_name)) ··· 35 42 } 36 43 $file = $macro->getFile(); 37 44 38 - $upper_text = strtoupper($upper_text); 39 - $lower_text = strtoupper($lower_text); 40 - $mixed_text = md5($upper_text).':'.md5($lower_text); 41 - $hash = 'meme'.hash('sha256', $mixed_text); 42 - $xform = id(new PhabricatorTransformedFile()) 43 - ->loadOneWhere('originalphid=%s and transform=%s', 44 - $file->getPHID(), $hash); 45 + $upper_text = phutil_utf8_strtoupper($upper_text); 46 + $lower_text = phutil_utf8_strtoupper($lower_text); 47 + 48 + $hash = PhabricatorHash::digestForIndex( 49 + phutil_json_encode( 50 + array( 51 + 'kind' => 'meme', 52 + 'upper' => $upper_text, 53 + 'lower' => $lower_text, 54 + ))); 55 + 56 + $xfile = self::loadTransformedFile($viewer, $file->getPHID(), $hash); 57 + if ($xfile) { 58 + return $xfile->getViewURI(); 59 + } 45 60 46 - if ($xform) { 47 - $memefile = id(new PhabricatorFileQuery()) 48 - ->setViewer($viewer) 49 - ->withPHIDs(array($xform->getTransformedPHID())) 50 - ->executeOne(); 51 - if ($memefile) { 52 - return $memefile->getBestURI(); 61 + $transformer = new PhabricatorImageTransformer(); 62 + 63 + $new_file = $transformer->executeMemeTransform( 64 + $file, 65 + $upper_text, 66 + $lower_text); 67 + 68 + $xfile = id(new PhabricatorTransformedFile()) 69 + ->setOriginalPHID($file->getPHID()) 70 + ->setTransformedPHID($new_file->getPHID()) 71 + ->setTransform($hash); 72 + 73 + try { 74 + $caught = null; 75 + 76 + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 77 + try { 78 + $xfile->save(); 79 + } catch (Exception $ex) { 80 + $caught = $ex; 53 81 } 82 + unset($unguarded); 83 + 84 + if ($caught) { 85 + throw $caught; 86 + } 87 + 88 + return $new_file->getViewURI(); 89 + } catch (AphrontDuplicateKeyQueryException $ex) { 90 + $xfile = self::loadTransformedFile($viewer, $file->getPHID(), $hash); 91 + if (!$xfile) { 92 + throw $ex; 93 + } 94 + return $xfile->getViewURI(); 54 95 } 96 + } 97 + 98 + private static function loadTransformedFile( 99 + PhabricatorUser $viewer, 100 + $file_phid, 101 + $hash) { 55 102 56 - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 57 - $transformers = (new PhabricatorImageTransformer()); 58 - $newfile = $transformers 59 - ->executeMemeTransform($file, $upper_text, $lower_text); 60 - $xfile = new PhabricatorTransformedFile(); 61 - $xfile->setOriginalPHID($file->getPHID()); 62 - $xfile->setTransformedPHID($newfile->getPHID()); 63 - $xfile->setTransform($hash); 64 - $xfile->save(); 103 + $xform = id(new PhabricatorTransformedFile())->loadOneWhere( 104 + 'originalPHID = %s AND transform = %s', 105 + $file_phid, 106 + $hash); 107 + if (!$xform) { 108 + return null; 109 + } 65 110 66 - return $newfile->getBestURI(); 111 + return id(new PhabricatorFileQuery()) 112 + ->setViewer($viewer) 113 + ->withPHIDs(array($xform->getTransformedPHID())) 114 + ->executeOne(); 67 115 } 68 116 }
+4 -7
src/applications/macro/markup/PhabricatorMemeRemarkupRule.php
··· 50 50 $options['above'], 51 51 $options['below']); 52 52 53 - $img = $this->newTag( 54 - 'img', 55 - array( 56 - 'src' => $uri, 57 - 'alt' => $alt_text, 58 - 'class' => 'phabricator-remarkup-macro', 59 - )); 53 + $img = id(new PHUIRemarkupImageView()) 54 + ->setURI($uri) 55 + ->addClass('phabricator-remarkup-macro') 56 + ->setAlt($alt_text); 60 57 } 61 58 62 59 return $this->getEngine()->storeText($img);
+12
src/infrastructure/markup/view/PHUIRemarkupImageView.php
··· 7 7 private $width; 8 8 private $height; 9 9 private $alt; 10 + private $classes = array(); 10 11 11 12 public function setURI($uri) { 12 13 $this->uri = $uri; ··· 44 45 return $this->alt; 45 46 } 46 47 48 + public function addClass($class) { 49 + $this->classes[] = $class; 50 + return $this; 51 + } 52 + 47 53 public function render() { 48 54 $id = celerity_generate_unique_node_id(); 49 55 ··· 54 60 'imageID' => $id, 55 61 )); 56 62 63 + $classes = null; 64 + if ($this->classes) { 65 + $classes = implode(' ', $this->classes); 66 + } 67 + 57 68 return phutil_tag( 58 69 'img', 59 70 array( ··· 61 72 'width' => $this->getWidth(), 62 73 'height' => $this->getHeight(), 63 74 'alt' => $this->getAlt(), 75 + 'class' => $classes, 64 76 )); 65 77 } 66 78