1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-22 19:49:02 +01:00

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
This commit is contained in:
epriestley 2018-03-08 07:36:32 -08:00
parent 6095d88998
commit 98cac2cc29
3 changed files with 99 additions and 42 deletions

View file

@ -13,18 +13,25 @@ final class PhabricatorMacroMemeController
$lower_text = $request->getStr('lowertext'); $lower_text = $request->getStr('lowertext');
$viewer = $request->getViewer(); $viewer = $request->getViewer();
$uri = self::generateMacro($viewer, $macro_name, $uri = self::generateMacro(
$upper_text, $lower_text); $viewer,
if ($uri === false) { $macro_name,
return new Aphront404Response(); $upper_text,
} $lower_text);
return id(new AphrontRedirectResponse())
->setIsExternal(true) $content = array(
->setURI($uri); 'imageURI' => $uri,
);
return id(new AphrontAjaxResponse())->setContent($content);
} }
public static function generateMacro($viewer, $macro_name, $upper_text, public static function generateMacro(
$lower_text) { PhabricatorUser $viewer,
$macro_name,
$upper_text,
$lower_text) {
$macro = id(new PhabricatorMacroQuery()) $macro = id(new PhabricatorMacroQuery())
->setViewer($viewer) ->setViewer($viewer)
->withNames(array($macro_name)) ->withNames(array($macro_name))
@ -35,34 +42,75 @@ final class PhabricatorMacroMemeController
} }
$file = $macro->getFile(); $file = $macro->getFile();
$upper_text = strtoupper($upper_text); $upper_text = phutil_utf8_strtoupper($upper_text);
$lower_text = strtoupper($lower_text); $lower_text = phutil_utf8_strtoupper($lower_text);
$mixed_text = md5($upper_text).':'.md5($lower_text);
$hash = 'meme'.hash('sha256', $mixed_text);
$xform = id(new PhabricatorTransformedFile())
->loadOneWhere('originalphid=%s and transform=%s',
$file->getPHID(), $hash);
if ($xform) { $hash = PhabricatorHash::digestForIndex(
$memefile = id(new PhabricatorFileQuery()) phutil_json_encode(
->setViewer($viewer) array(
->withPHIDs(array($xform->getTransformedPHID())) 'kind' => 'meme',
->executeOne(); 'upper' => $upper_text,
if ($memefile) { 'lower' => $lower_text,
return $memefile->getBestURI(); )));
}
$xfile = self::loadTransformedFile($viewer, $file->getPHID(), $hash);
if ($xfile) {
return $xfile->getViewURI();
} }
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $transformer = new PhabricatorImageTransformer();
$transformers = (new PhabricatorImageTransformer());
$newfile = $transformers
->executeMemeTransform($file, $upper_text, $lower_text);
$xfile = new PhabricatorTransformedFile();
$xfile->setOriginalPHID($file->getPHID());
$xfile->setTransformedPHID($newfile->getPHID());
$xfile->setTransform($hash);
$xfile->save();
return $newfile->getBestURI(); $new_file = $transformer->executeMemeTransform(
$file,
$upper_text,
$lower_text);
$xfile = id(new PhabricatorTransformedFile())
->setOriginalPHID($file->getPHID())
->setTransformedPHID($new_file->getPHID())
->setTransform($hash);
try {
$caught = null;
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
try {
$xfile->save();
} catch (Exception $ex) {
$caught = $ex;
}
unset($unguarded);
if ($caught) {
throw $caught;
}
return $new_file->getViewURI();
} catch (AphrontDuplicateKeyQueryException $ex) {
$xfile = self::loadTransformedFile($viewer, $file->getPHID(), $hash);
if (!$xfile) {
throw $ex;
}
return $xfile->getViewURI();
}
}
private static function loadTransformedFile(
PhabricatorUser $viewer,
$file_phid,
$hash) {
$xform = id(new PhabricatorTransformedFile())->loadOneWhere(
'originalPHID = %s AND transform = %s',
$file_phid,
$hash);
if (!$xform) {
return null;
}
return id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($xform->getTransformedPHID()))
->executeOne();
} }
} }

View file

@ -50,13 +50,10 @@ final class PhabricatorMemeRemarkupRule extends PhutilRemarkupRule {
$options['above'], $options['above'],
$options['below']); $options['below']);
$img = $this->newTag( $img = id(new PHUIRemarkupImageView())
'img', ->setURI($uri)
array( ->addClass('phabricator-remarkup-macro')
'src' => $uri, ->setAlt($alt_text);
'alt' => $alt_text,
'class' => 'phabricator-remarkup-macro',
));
} }
return $this->getEngine()->storeText($img); return $this->getEngine()->storeText($img);

View file

@ -7,6 +7,7 @@ final class PHUIRemarkupImageView
private $width; private $width;
private $height; private $height;
private $alt; private $alt;
private $classes = array();
public function setURI($uri) { public function setURI($uri) {
$this->uri = $uri; $this->uri = $uri;
@ -44,6 +45,11 @@ final class PHUIRemarkupImageView
return $this->alt; return $this->alt;
} }
public function addClass($class) {
$this->classes[] = $class;
return $this;
}
public function render() { public function render() {
$id = celerity_generate_unique_node_id(); $id = celerity_generate_unique_node_id();
@ -54,6 +60,11 @@ final class PHUIRemarkupImageView
'imageID' => $id, 'imageID' => $id,
)); ));
$classes = null;
if ($this->classes) {
$classes = implode(' ', $this->classes);
}
return phutil_tag( return phutil_tag(
'img', 'img',
array( array(
@ -61,6 +72,7 @@ final class PHUIRemarkupImageView
'width' => $this->getWidth(), 'width' => $this->getWidth(),
'height' => $this->getHeight(), 'height' => $this->getHeight(),
'alt' => $this->getAlt(), 'alt' => $this->getAlt(),
'class' => $classes,
)); ));
} }