diff --git a/src/applications/macro/controller/PhabricatorMacroMemeController.php b/src/applications/macro/controller/PhabricatorMacroMemeController.php index 03b13f6205..794e24e36d 100644 --- a/src/applications/macro/controller/PhabricatorMacroMemeController.php +++ b/src/applications/macro/controller/PhabricatorMacroMemeController.php @@ -13,18 +13,25 @@ final class PhabricatorMacroMemeController $lower_text = $request->getStr('lowertext'); $viewer = $request->getViewer(); - $uri = self::generateMacro($viewer, $macro_name, - $upper_text, $lower_text); - if ($uri === false) { - return new Aphront404Response(); - } - return id(new AphrontRedirectResponse()) - ->setIsExternal(true) - ->setURI($uri); + $uri = self::generateMacro( + $viewer, + $macro_name, + $upper_text, + $lower_text); + + $content = array( + 'imageURI' => $uri, + ); + + return id(new AphrontAjaxResponse())->setContent($content); } - public static function generateMacro($viewer, $macro_name, $upper_text, - $lower_text) { + public static function generateMacro( + PhabricatorUser $viewer, + $macro_name, + $upper_text, + $lower_text) { + $macro = id(new PhabricatorMacroQuery()) ->setViewer($viewer) ->withNames(array($macro_name)) @@ -35,34 +42,75 @@ final class PhabricatorMacroMemeController } $file = $macro->getFile(); - $upper_text = strtoupper($upper_text); - $lower_text = 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); + $upper_text = phutil_utf8_strtoupper($upper_text); + $lower_text = phutil_utf8_strtoupper($lower_text); - if ($xform) { - $memefile = id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs(array($xform->getTransformedPHID())) - ->executeOne(); - if ($memefile) { - return $memefile->getBestURI(); - } + $hash = PhabricatorHash::digestForIndex( + phutil_json_encode( + array( + 'kind' => 'meme', + 'upper' => $upper_text, + 'lower' => $lower_text, + ))); + + $xfile = self::loadTransformedFile($viewer, $file->getPHID(), $hash); + if ($xfile) { + return $xfile->getViewURI(); } - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - $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(); + $transformer = new PhabricatorImageTransformer(); - 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(); } } diff --git a/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php b/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php index 004e415450..278bd403cb 100644 --- a/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php +++ b/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php @@ -50,13 +50,10 @@ final class PhabricatorMemeRemarkupRule extends PhutilRemarkupRule { $options['above'], $options['below']); - $img = $this->newTag( - 'img', - array( - 'src' => $uri, - 'alt' => $alt_text, - 'class' => 'phabricator-remarkup-macro', - )); + $img = id(new PHUIRemarkupImageView()) + ->setURI($uri) + ->addClass('phabricator-remarkup-macro') + ->setAlt($alt_text); } return $this->getEngine()->storeText($img); diff --git a/src/infrastructure/markup/view/PHUIRemarkupImageView.php b/src/infrastructure/markup/view/PHUIRemarkupImageView.php index 0a73efb83b..0402f000a4 100644 --- a/src/infrastructure/markup/view/PHUIRemarkupImageView.php +++ b/src/infrastructure/markup/view/PHUIRemarkupImageView.php @@ -7,6 +7,7 @@ final class PHUIRemarkupImageView private $width; private $height; private $alt; + private $classes = array(); public function setURI($uri) { $this->uri = $uri; @@ -44,6 +45,11 @@ final class PHUIRemarkupImageView return $this->alt; } + public function addClass($class) { + $this->classes[] = $class; + return $this; + } + public function render() { $id = celerity_generate_unique_node_id(); @@ -54,6 +60,11 @@ final class PHUIRemarkupImageView 'imageID' => $id, )); + $classes = null; + if ($this->classes) { + $classes = implode(' ', $this->classes); + } + return phutil_tag( 'img', array( @@ -61,6 +72,7 @@ final class PHUIRemarkupImageView 'width' => $this->getWidth(), 'height' => $this->getHeight(), 'alt' => $this->getAlt(), + 'class' => $classes, )); }